<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
  xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/RSS">
  <title>Das deutsche Plone Buch</title>
  <link>http://www.derstappen-it.de</link>

  <description>
    
      Andy McKay: Plone. Addison-Wesley 2005
    
  </description>

  

  
            <syn:updatePeriod>monthly</syn:updatePeriod>
            <syn:updateFrequency>1</syn:updateFrequency>
            <syn:updateBase>2007-04-06T21:15:38Z</syn:updateBase>
        

  <image
    rdf:resource="http://www.derstappen-it.de/logo.png" />

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/pref.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch1.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch2.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch3.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch4.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch5.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch6.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch7.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch8.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch9.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch10.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch11.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch12.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch13.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch14.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appA.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appB.rst" />
      
      
        <rdf:li rdf:resource="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appC.rst" />
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/pref.rst">
    <title>Vorwort</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/pref.rst</link>
    <description>Vorwort zur deutschen Ausgabe (von Alan Runyan).</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Vorwort zur deutschen Ausgabe</h2>
<p>Wir stehen am Anfang des Jahres 2005. Um genau zu sein, es ist der 7. Februar 2005. Das heißt, Plone gibt es seit nun etwa fünf Jahren, und ich bin verblüfft, dass ich das Vorwort zur deutschen Übersetzung dieses Buchs schreibe.</p>
<p>In der Geschichte von Plone sind viele der wichtigsten Ereignisse mit Deutschland und seinen Nachbarländern verbunden. Die ersten international besuchten Coding-Veranstaltungen fanden vor fast zwei Jahren im schweizerischen Bern statt. Sieben Monate später hatten wir einen Sprint in einem Schloss im österreichischen Goldegg. Außerdem gibt es in Deutschland eine sehr aktive und effektiv arbeitende Zope- und Plone-User-Group, die DZUG. Europa ist, jedenfalls bisher, frei von Software-Patenten, und es waren deutsche Minister, die E-Mails an uns Plone-Entwickler geschrieben haben, um ihren Dank und ihre Wertschätzung für diese Software auszudrücken. In Europa scheinen der Open Source-Gedanke und Plone stark ausgeprägt zu sein.</p>
<p>Und nun das, Andy McKays Buch, <em>The Definitive Guide to Plone</em>, wurde auf Deutsch übersetzt. Es übertrifft einfach meine Erwartungen, wie groß die Zielgruppe von Plone ist. Seien Sie willkommen bei diesem Software-System, in seiner weltweiten Community, und lernen Sie eines der interessantesten verfügbaren Technologienbündel kennen, das Content-Management-System namens Plone!</p>
<p>Andy McKay habe ich auf einer O'Reilly-Konferenz getroffen, ein Jahr, bevor es mit Plone losging. Er war damals ein erfolgreicher Zope-Entwickler und arbeitete bei ActiveState in Vancouver, Kanada. Nachdem Plone einmal abgehoben hatte, war Andy einer derjenigen, der wegen seiner Consulting-Projekte großes Interesse daran hatte. Er kümmert sich immer auch um das Open Source-Installationsprogramm für Plone unter Windows, betreibt die Website <em>zopezen.org</em> und hat viele Patches zu Zope, CMF und Plone beigesteuert.</p>
<p>Und nun hat Andy viel von seiner Zeit dem Schreiben dieses Buchs gewidmet, das nach dem Druck unter der Creative Commons-Lizenz allen öffentlich zur Verfügung steht. Das Buch wird heute in Nordamerika gut verkauft. Mir ist völlig schleierhaft, wie Andy es geschafft hat, das Buch zu schreiben, seine Consulting-Tätigkeit fortzuführen und auch noch mit seiner wunderbaren Frau Danae am gesellschaftlichen Leben teilzunehmen. Wie viele andere Projekte von Andy ist auch dieses Buch keines, das auf Profit ausgelegt ist. Andy wollte den Leuten zeigen, wie man Plone und die ganzen Technologien dahinter benutzt. Während der letzten drei Jahre sind wir gute Freunde geworden. Wir sind sogar Partner in einer kommerziellen Software- und Consulting-Firma, Enfold Systems, in der wir Plone unter Windows wirksam einsetzen.</p>
<p>Plone begann als spaßige Zusammenarbeit zwischen Alexander Limi aus Norwegen und mir (ich komme aus Houston, Texas) und ist mittlerweile für uns beide wie auch für viele andere zu einer Vollzeit-Hauptbeschäftigung geworden. Aus diesen Ursprüngen wurde Plone größer und größer und hat sich zu einer Erfolgsgeschichte entwickelt. Dabei dürfen wir aber nicht vergessen, aus der Geschichte zu lernen, und wir sollten uns bewusst machen, was Plone zu dem Erfolg gemacht hat, der es heute ist.</p>
<p>Der wichtigste Grund für den Erfolg von Plone ist seine Community. Man vergisst sehr leicht die einfache Formel für eine gute Community: Je mehr man investiert, desto mehr bekommt man zurück. Das wird überdeutlich, wenn man jede Woche neue Komponenten findet, die für Zope/CMF/Plone entwickelt werden. Ich glaube, es gibt zwei Ursachen dafür, warum sich so schnell eine Community um Plone herum gebildet hat: seine einfache Installation (jedenfalls unter Windows ;-) und seine einfache Benutzung. Durch die einfache Installation ergibt sich eine hohe Zahl von Testbenutzern. Der größte Wert liegt aber, wie ich meine, in der einfachen Benutzung. Das habe ich immer wieder in meinen Gesprächen mit Consulting-Firmen und staatlichen Einrichtungen gehört. Dank seiner Lokalisierung, d.h. dadurch, dass die Benutzerschnittstelle in der Muttersprache des Benutzers vorhanden ist, verbreitet sich das Produkt sehr schnell. Heute ist Plone ein weltweites Phänomen, bei dem es jeden Monat neue Übersetzungen, Komponenten und mehr Dokumentation gibt. In Deutschland gibt es sogar ein Online-Magazin, das <em>Zope Magazine</em>.</p>
<p>Nun gibt es zu Plone drei englischsprachige Bücher und eines auf deutsch. Auf vielen Open Source-Konferenzen gibt es eine oder mehrere Präsentationen zu Plone, z.B. auf der PyCon (US-Ostküste), O'Reilly (US-Westküste), FISL (Porto Alegre, Brasilien), Solutions Linux (Frankreich), EuroPython (Schweden), dem LinuxTag (Deutschland) und Vancouver Python Workshop (Kanada). Ach ja, die jährliche Plone-Konferenz gibt es auch. Letztes Jahr fand sie im Wiener Volksgarten in einer Diskothek stand, in die sich 350 Teilnehmer in zwei Tracks mit Vorträgen und Tutorien hineinzwängten. Dort fand auch die erste Jahresversammlung der Plone Foundation statt. Ich muss jetzt vieles auslassen, da das Jahr einfach sehr reich an Ereignissen war. Zwischen diesen größeren Veranstaltungen gibt es auch &quot;Sprints&quot;, wo sich 10 bis 30 Entwickler treffen und an einem bestimmten Feature oder einem Teil des Technologiepakets von Plone arbeiten. Mitglieder der Community, ob alteingesessene oder neue, haben Unmengen von Möglichkeiten, weltweit an der Entwicklung des Systems teilzunehmen.</p>
<p>Dieses Buch hilft Ihnen dabei, schnell Fortschritte mit Plone zu machen. Es gibt keine größere Genugtuung, als ein System wie Plone einzurichten, das dann von Ihnen selbst oder einer Gruppe von Leuten benutzt wird. Während Sie diesen Weg gehen, hoffe ich, dass Sie noch die Zeit haben, Kontakt mit der Community aufzunehmen, ob das nun mit Hilfe der Mailing-Listen, mit IRC, auf Konferenzen oder in lokalen User-Groups geschieht. Die Community bietet eine überwältigende Menge an Unterstützung und Weisheit. Sie ist das größte Gut eines jeden Open Source-Projekts, ja - sie ist das Open Source-Projekt. Viele Leute aus dieser Community haben mir erzählt, dass ihnen dieses Buch dabei geholfen hat, das Wissen über ihre fertigen Projektteile an Ihre Kunden weiterzugeben. Es ist die Community in Form von Freiwilligen und Firmen, die ihre Unterstützung angeboten haben, die Plone so weit gebracht hat. Ich hoffe, der Inhalt dieses Buchs hilft Ihnen dabei, sich den Wert von Plone einfach und schnell zu erschließen. Und wer weiß? Vielleicht finden Sie sich selbst als Mitglied der weltweiten Plone-Community wieder, nachdem Sie dieses Buch gelesen haben. Geben auch Sie Ihr Wissen weiter!</p>
<p>Alan Runyan</p>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch1.rst">
    <title>1. Einführung in Plone</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch1.rst</link>
    <description>Eine Firma ohne Website ist undenkbar, und die meisten Firmen und Organisationen verfügen gleich über mehrere. Egal, ob es eine externe Site für die Kommunikation mit Kunden oder ein Intranet für die eigenen Mitarbeiter ist oder eine Webseite für direkte Kommunikation mit und Feedback von Kunden - die meisten Websites haben ein Problem, nämlich die Verwaltung ihres Inhalts. Dies ist eine Herausforderung, für deren Lösung eine Organisation sehr viel Zeit investieren und großen Aufwand betreiben muss. Es ist keine leichte Aufgabe, für solche Sites ein mächtiges, aber dennoch flexibles System zu schaffen, das gleichzeitig mit den sich ständig ändernden Anforderungen und mit den sich weiterentwickelnden Bedürfnissen der Firma klarkommt.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Einführung in Plone</h2>
<p>Eine Firma ohne Website ist undenkbar, und die meisten Firmen und Organisationen verfügen gleich über mehrere. Egal, ob es eine externe Site für die Kommunikation mit Kunden oder ein Intranet für die eigenen Mitarbeiter ist oder eine Webseite für direkte Kommunikation mit und Feedback von Kunden - die meisten Websites haben ein Problem, nämlich die Verwaltung ihres Inhalts. Dies ist eine Herausforderung, für deren Lösung eine Organisation sehr viel Zeit investieren und großen Aufwand betreiben muss. Es ist keine leichte Aufgabe, für solche Sites ein mächtiges, aber dennoch flexibles System zu schaffen, das gleichzeitig mit den sich ständig ändernden Anforderungen und mit den sich weiterentwickelnden Bedürfnissen der Firma klarkommt.</p>
<p>Unabhängig von den Anforderungen Ihrer Website und von der Menge an Inhalten oder Benutzern stellt Plone eine benutzerfreundliche, mächtige Lösung dar, mit der Sie über das Web ganz einfach beliebige Inhalte hinzufügen und bearbeiten können sowie Navigations- und Suchmöglichkeiten für diese Inhalte hinzufügen können, aber auch Sicherheitsmaßnahmen und Workflow-Einstellungen dafür vornehmen können.</p>
<p>Mit Plone können Sie fast jede Website erstellen und sie dann ganz leicht aktualisieren. Damit können Sie inhaltsreiche Sites schnell erstellen und sich einen Wettbewerbsvorteil verschaffen. Und schließlich ist vielleicht das Beste an diesem System, dass es Open Source und gratis ist. Aufgrund seiner beeindruckenden Liste von Eigenschaften ist es vergleichbar mit, wenn nicht sogar besser als viele geschlossene Content-Management-Systeme, die viele Hunderttausende von Dollar kosten.</p>
<p>Folgendes sagte Mike Sugarbaker in seinem Vortrag auf der Open Source Content Management-Konferenz (OSCOM) im Jahre 2002 für die Mindjack-Site (<a class="reference external" href="http://www.mindjack.com/events/oscom.html">http://www.mindjack.com/events/oscom.html</a>):</p>
<blockquote>
<em>&quot;Ich werde nicht die vollständige Liste aller konkurrierenden Open-Source-Management-Frameworks behandeln. Ich lasse alle Verfolger aus: Der Sieger heißt Plone. Dieses Produkt, das auf dem sechs Jahre alten Web Application Framework Zope basiert, war das Paket mit den meisten Werkzeugen, der höchsten Professionalität, der größten Zugkraft, und vor allem mit dem größten Rummel.&quot;</em></blockquote>
<p>Sie finden die Plone-Website unter <a class="reference external" href="http://www.plone.org">http://www.plone.org</a>, wie in Abbildung 1.1 zu sehen ist. Um Plone einfach mal auszuprobieren, ist eine Demonstrations-Site unter <a class="reference external" href="http://demo.plone.org">http://demo.plone.org</a> verfügbar. Dort können Sie einfach und schnell Inhalte über das Web bearbeiten. Das heißt, Sie können Ereignisse und Dokumente hinzufügen, Bilder hochladen und alles über das Framework verarbeiten, das Plone zur Verfügung stellt.</p>
<p>Wenn Sie einen Schritt weiter gehen möchten, können Sie unter <a class="reference external" href="http://www.objectis.org">http://www.objectis.org</a> eine kostenlose Hosting-Dienstleistung von Objectis in Anspruch nehmen und eine eigene nichtkommerzielle Zope- oder Plone-Site aufsetzen und betreiben. Dort verfügen Sie dann über ein eigenes Portal mit vollen Manager-Rechten und vielen zusätzlich installierten Produkten. Derzeit betreibt Objectis fast 6000 solcher Portale.</p>
<a class="reference external image-reference" href="img/01-01.png/image_view_fullscreen"><img alt="img/01-01.png" class="original" src="img/01-01.png" /></a>
<p>Abbildung 1.1. Die Plone-Website</p>
<div class="section" id="was-ist-ein-content-management-system">
<h3>Was ist ein Content-Management-System?</h3>
<p>Eine einfache Definition eines Content-Management-Systems (CMS) ist die, dass es ein System für die Verwaltung von Inhalten ist. Da diese Definition wenig hilfreich ist, möchte ich eine vollständigere Erklärung in kleineren Teilen angeben. Ich beginne mit einer allgemeinen Definition von <em>Inhalt</em> (<em>Content</em>): ein Inhalt ist eine Dateneinheit, die um dazugehörige Informationen erweitert ist. Eine Dateneinheit können dabei eine Webseite, ein bevorstehender Termin, ein Microsoft Word-Dokument, ein Bild, ein Videofilm oder irgendwelche Daten sein, die in der Organisation, die mit dem System arbeitet, von Bedeutung sind.</p>
<p>All diese Einheiten werden <em>Inhalte</em> genannt, und sie alle verfügen über ähnliche Attribute, z.B. dass sie von bestimmten Benutzern hinzugefügt und bearbeitet und auf verschiedene Weisen veröffentlicht werden müssen. Diese Attribute werden von einem  <em>Workflow</em>-System gesteuert. Dahinter verbirgt sich eine regelbasierte Logik, mit der die Verwaltung der Inhalte beschrieben wird.</p>
<p>Historisch betrachtet, kann man einen Unterschied zwischen Content-Management- und Dokumentenmanagement-Systemen beobachten, aber heute sind beide überwiegend zu einem konvergiert. Der wesentliche Unterschied liegt in den verwalteten Einheiten: Meistens werden unter <em>Inhalten</em> beliebige Daten verstanden, während  <em>Dokumente</em> Dinge sind, die von Menschen z.B. mit Software wie Microsoft Office geschrieben und bearbeitet werden. Nehmen Sie etwa ein Buch: Ein Buch besteht aus vielen Dateneinheiten und erfordert möglicherweise eine Verwaltung, die sich leicht von der allgemeiner Inhalte unterscheidet. In den meisten Fällen ist dieser Unterschied jedoch recht klein, und Produkte wie Plone können die kleinen Bestandteile eines größeren Inhalts verwalten und zusammensetzen.</p>
<p>Durch die Allgegenwart des Webs werden heute viele CMS als Web-CMS bezeichnet, sei es, weil sie über eine webbasierte Schnittstelle verfügen oder weil sie sich auf ein webbasiertes Übertragungssystem per Internet oder Intranet stützen. Plone bietet beides: eine Verwaltungsschnittstelle und ein Übertragungssystem auf Basis des Webs.</p>
<p>Folgendes ist eine mögliche Definition eines CMS (<a class="reference external" href="http://www.contentmanager.eu.com/history.htm">http://www.contentmanager.eu.com/history.htm</a>):</p>
<blockquote>
<em>Ein CMS ist ein Werkzeug, das es vielen verschiedenen (zentralen) technischen und (dezentralen) nichttechnischen Mitarbeitern ermöglicht, eine Vielzahl von Inhalten (z.B. Text, Grafik, Video usw.) zu erstellen, zu bearbeiten, zu verwalten und schließlich zu veröffentlichen, und zwar unter zentralen Randbedingungen bzgl. Regeln, Prozessen und Workflow, die ein konsistentes und gültiges Aussehen im Web sicherstellen.</em></blockquote>
</div>
<div class="section" id="brauchen-sie-ein-content-management-system">
<h3>Brauchen Sie ein Content-Management-System?</h3>
<p>Auch wenn es nicht der einzige Vorteil eines CMS ist, so ist der offensichtlichste, dass eine Website damit einfach zu koordinieren ist. Stellen Sie sich eine Situation vor, in der eine Person, der <em>Webmaster</em>, eine Website koordiniert, sei es ein Intranet oder eine externe Site. Die Benutzer liefern Inhalte in den verschiedensten Formaten, und der Webmaster macht daraus benutzbare Webseiten, indem er sie in HTML (Hypertext Markup Language) umwandelt. Wenn ein Benutzer diese Seiten ändern muss, schickt er diese Änderungen an den Webmaster, und so weiter.</p>
<p>Daraus ergeben sich für die Organisation viele Probleme, wobei das größte ist, dass der gesamte Inhalt von einer Person abgewickelt wird - ein offensichtlicher Engpass. Diese eine Person kann nur ein bestimmtes Arbeitspensum erledigen, und wenn sie krank wird oder die Firma verlässt, geht viel Produktivität dabei verloren, einen Ersatz zu finden. Der Vorgang der Veröffentlichung kann sehr frustrierend sein, wenn E-Mails zwischen Webmaster und Benutzern ausgetauscht werden, die versuchen, ihre Inhalte zu veröffentlichen.</p>
<p>Was man braucht, ist ein System, das Folgendes leistet:</p>
<ul class="simple">
<li><strong>Trennung des Inhalts einer Seite von seiner Präsentation</strong>: Wenn der eigentliche Inhalt von der Art seiner Präsentation getrennt wird, muss der Autor des Inhalts nichts über HTML oder darüber wissen, wie die Seite ausgegeben wird. Tatsächlich könnten auf den gleichen Inhalt verschiedene Templates angewandt werden, inklusive anderer Formate als HTML, z.B. PDF (Portable Document Format) oder SVG (Scalable Vector Graphics). Wenn Sie das Aussehen der Site ändern möchten, müssen Sie nur noch das eine Template ändern und nicht mehr alle Inhalte.</li>
<li><strong>Erlaubnis für bestimmte Benutzer, Inhalte hinzuzufügen und zu ändern</strong>: Wenn bestimmte Benutzer Inhalte leicht hinzufügen und ändern können, müssen diese nicht mehr zum Webmaster oder ans Webteam geschickt werden. Stattdessen kann der Benutzer, der eine Seite erstellen möchte, das einfach tun und die Seite so lange bearbeiten, wie es nötig ist.</li>
<li><strong>Anwendung von Regeln, die angeben, wer was und wann veröffentlichen kann</strong>: Ihre Geschäftslogik soll evtl. verhindern, dass jeder Inhalte auf Ihrer Website veröffentlicht. So könnten z.B. Marketing-Mitarbeiter Inhalte auf der Seite der Pressemitteilungen veröffentlichen, aber nicht auf den technischen Seiten.</li>
<li><strong>Anwendung von Geschäftslogik anf Inhalte</strong>: Wenn ein Marketing-Mitarbeiter eine Pressemitteilung erstellt, muss sie evtl. jemand aus der Rechtsabteilung überprüfen. In diesem Fall wird das Dokument von einem Revisionsprozess erfasst, der sicherstellt, dass es nicht öffentlich wird, bevor die Prüfung erfolgt ist.</li>
<li><strong>Informationen können intelligent gesucht und indiziert werden</strong>: Da das CMS strukturierte Informationen über den Inhalt verwalten kann (z.B. den Namen des Autors, das Datum der Veröffentlichung bzw. von Änderungen, Kategorien usw.), kann es Listen von Inhalten nach Autor, Zeit usw. erstellen. Es kann auch Suchfunktionen anbieten, die wesentlich intelligenter und nützlicher sind als eine rein textuelle Suche.</li>
</ul>
<p>Obwohl dieses Beispiel Vorteile darstellt, die besonders für große Organisationen von Bedeutung sind, profitieren Organisationen aller Größen davon. Tatsächlich können kleine Organisationen, die üblicherweise keinen Vollzeit-Webmaster beschäftigen können, besonders große Vorteile durch ein solches System erhalten. Durch die Einführung eines CMS können Sie all diese und noch weitere Probleme lösen.</p>
<p>Das Schlüsselkonzept eines jeden CMS besteht in der klaren Trennung seiner wesentlichen Bestandteile: Sicherheit, Workflow, Templates usw. So sind z.B. die Templates, mit denen ein Eintrag dargestellt wird, von seinem Inhalt getrennt. Dadurch können Sie seine Darstellung leicht ändern.</p>
</div>
<div class="section" id="ein-erster-blick-auf-plone">
<h3>Ein erster Blick auf Plone</h3>
<p>Plone ist Open Source-Software, die unter der GPL (General Public License) lizenziert wird. Dies ist eine häufig verwendete Lizenz, die es jedem gestattet, die Software gratis einzusetzen. Weitere Informationen zur GPL finden Sie auf der Website der Free Software Foundation unter <a class="reference external" href="http://www.gnu.org">http://www.gnu.org</a>. Sie können alle Aspekte des Quellcodes von Plone untersuchen und für Ihre Anwendung anpassen. Sie müssen keine Lizenzgebühren bezahlen, es gibt keine Lizenz, die irgendwann abläuft, und der gesamte Code ist offen gelegt. Diese Open-Source-Philosophie bedeutet, dass Plone bereits eine große Anzahl von Benutzern hat, und es gibt Legionen von Entwicklern, Usability-Experten, Übersetzern, Fachautoren und Grafikdesignern, die in der Lage sind, <em>an</em> Plone selbst zu arbeiten. Wenn Sie auf Plone setzen, sind Sie nicht auf eine Firma angewiesen. Es gibt etwa ein Dutzend Firmen, die Dienstleistungen zu Plone anbieten.</p>
<p>In naher Zukunft kann es durchaus sein, dass Plone unter die kommerziell vorteilhaftere LGPL gestellt wird oder sogar unter einem dualen Lizenzmodell verfügbar sein wird.</p>
<div class="section" id="verpackung">
<h4>Verpackung</h4>
<p>Zu Plone gibt es einfache Installationsprogramme für Windows, Linux und Macs. Produkte und Zusätze von Dritten verfügen ebenfalls über Installationsprogramme. Das Angebot qualitativ hochwertiger Releases für solche Produkte erleichtert ihre Installation und Verwaltung. Außerdem beinhaltet jedes neue Release einen Migrationspfad und Aktualisierungen, mit denen Ihre Plone-Site weiter funktioniert und auf dem neuesten Stand bleibt.</p>
</div>
<div class="section" id="internationalisierung">
<h4>Internationalisierung</h4>
<p>Die gesamte Benutzerschnittstelle von Plone wurde in über 25 Sprachen übersetzt, darunter Koreanisch, Japanisch, Französisch, Spanisch und Deutsch. Wenn Sie Ihre eigene Übersetzung hinzufügen möchten, so ist das einfach möglich (siehe Kapitel 4).</p>
</div>
<div class="section" id="benutzerfreundlichkeit">
<h4>Benutzerfreundlichkeit</h4>
<p>Plone bietet eine hervorragende Benutzerschnittstelle, die ein hohes Maß an Benutzerfreundlichkeit und Zugänglichkeit bietet. Dabei geht es nicht nur darum, hübschen HTML-Code darzustellen, sondern das betrifft fundamentale Teile von Plone. Die Schnittstelle von Plone ist kompatibel mit den US-Standards WAI-AAA und U.S. Section 508, die in Wirtschaft und Verwaltung gelten. Dadurch können mit Plone erstellte Sites auch von Menschen mit Sehbehinderungen benutzt werden. Zusätzlich bietet das den überraschenden, aber damit in Zusammenhang stehenden Vorteil, dass Ihre Seiten von Suchmaschinen wie Google besser indiziert werden.</p>
</div>
<div class="section" id="flexibles-aussehen">
<h4>Flexibles Aussehen</h4>
<p>In Plone ist der Inhalt von den eigentlichen Templates (dem Aussehen, oft auch <em>Skins</em> genannt) getrennt, mit denen Inhalte dargestellt werden. Dieses Aussehen wird mit dem großartigen HTML-Templatingsystem namens <em>Zope Page Templates</em> sowie einer großen Anzahl von Beschreibungen in CSS (Cascading Style Sheets) beschrieben. Mit nur wenig Ahnung von Plone können Sie ein anderes Aussehen erreichen, zwischen mehreren Erscheinungsweisen wählen und die Präsentation Ihrer Website völlig umgestalten.</p>
</div>
<div class="section" id="registrierung-und-personalisierung">
<h4>Registrierung und Personalisierung</h4>
<p>Plone beinhaltet ein vollständiges Benutzerregistrierungssystem. Benutzer können sich auf einer Plone-Site mit eigenen Benutzernamen und -passwörtern sowie mit beliebigen anderen Angaben registrieren, die Sie über die Benutzer hinzufügen möchten. Anschließend können Sie die gesamte Benutzerschnittstelle für die jeweiligen Benutzer personalisieren. Außerdem können Sie mit Hilfe von Add-Ons bereits vorhandene Angaben über Ihre Benutzer aus vielen Stellen verwenden, z.B. aus relationalen Datenbanken, aus LDAP (Lightweight Directory Access Protocol), Active Directory und aus anderen Quellen. Kapitel 8 behandelt die Registrierung und Konfiguration von Benutzern.</p>
</div>
<div class="section" id="workflow-und-sicherheit">
<h4>Workflow und Sicherheit</h4>
<p>Der Workflow steuert die Logik, nach der Inhalte auf der gesamten Site verarbeitet werden. Diese Logik können Sie über das Web mit grafischen Werkzeugen konfigurieren. Administratoren von Websites können diese so kompliziert oder einfach machen, wie sie es möchten. Man kann Benachrichtigungswerkzeuge hinzufügen, z.B. zum Verschicken von E-Mails oder Instant-Messages an Benutzer. Kapitel 8 behandelt den Workflow sehr detailliert.</p>
<p>Für jedes Stück Inhalt einer Plone-Site können Sie Listen für die Zugangskontrolle einrichten, die bestimmen, welche Benutzer Zugang zu diesem Inhalt haben und was sie damit anstellen können. Können sie ihn bearbeiten, ansehen oder kommentieren? All das lässt sich über das Web konfigurieren (siehe Kapitel 9).</p>
</div>
<div class="section" id="erweiterbarkeit">
<h4>Erweiterbarkeit</h4>
<p>Da Plone Open Source ist, kann es leicht verändert werden. Sie können fast jeden Aspekt von Plone ändern und konfigurieren, um es an Ihre Bedürfnisse anzupassen. Zahllose Pakete für Plone bieten eine große Bandbreite an Möglichkeiten für kleinere Sites sowie für große Unternehmen. Depots für solche Gratisprodukte und -erweiterungen finden Sie unter <a class="reference external" href="http://www.plone.org">http://www.plone.org</a>. Mit Entwicklungswerkzeugen wie Archetypes (siehe Kapitel 13) können Sie Plone-Code sehr leicht über das Web oder mit UML-Werkzeugen (Unified Modeling Language) erstellen. Kapitel 10 behandelt die Integration von Plone mit Enterprise-Lösungen wie LDAP, Apache, Microsoft Internet Information Services (IIS), Macromedia Dreamweaver usw.</p>
</div>
<div class="section" id="anpassung-von-inhalten">
<h4>Anpassung von Inhalten</h4>
<p>Die Benutzer einer Plone-Site können alle möglichen Inhalte hinzufügen, und die hinzugefügten Daten sind nicht in ihrer Menge oder Art eingeschränkt. Plone-Entwickler können ihre eigenen Inhaltstypen erstellen und hinzufügen, so dass fast jede Art von Inhalt verwaltet werden kann. Beschränkungen ergeben sich nur aus Ihrer Vorstellungskraft. In den Kapiteln 11 und 12 beschreibe ich, wie man Inhaltstypen anpassen kann. Kapitel 13 führt Sie in Archetypes ein, ein sehr mächtiges System für die Erstellung von Inhaltstypen, das ohne Programmierung auskommt. Sie können z.B. neue Inhaltstypen mit UML-Werkzeugen erstellen.</p>
</div>
<div class="section" id="dokumentation">
<h4>Dokumentation</h4>
<p>Das Plone-Projekt verwaltet auch Dokumentation, darunter dieses Buch, das unter der Creative Commons-Lizenz veröffentlicht wird. Der beste Ausgangspunkt zur verfügbaren Dokumentation ist <a class="reference external" href="http://www.plone.org/documentation">http://www.plone.org/documentation</a>.</p>
</div>
<div class="section" id="community">
<h4>Community</h4>
<p>Eines der besten Dinge an Plone ist seine Community von Entwicklern und Firmen, die Plone unterstützen und weiterentwickeln. Dank mehr als 60 Entwicklern weltweit, die in irgendeiner Weise daran beteiligt sind, ist es fast immer möglich, einen Plone-Entwickler zu finden, der gewillt und fähig ist, Ihnen zu helfen. Alan Runyan, Alexander Limi und Vidar Andersen haben angefangen, Plone zu entwickeln, aber es entwickelte sich schnell zu einem aktiven Projekt, nachdem weitere Entwickler sich daran beteiligten. Die Beiträge dieser Entwickler haben das Produkt Plone zu dem gemacht, was heute verfügbar ist.</p>
<div class="sidebar">
<p class="first sidebar-title">Beispiele für Plone-Sites</p>
<!-- Many Plone sites exist; some are obvious because of their looks, and some aren't. The following is just a small sample of the more diverse sites: -->
<p>Es existieren viele Plone-Sites. Bei manchen davon ist das wegen ihres Aussehens offensichtlich, bei anderen nicht. Die folgende Liste enthält lediglich Beispiele ganz verschiedener Sites:</p>
<ul class="simple">
<li><strong>Plone</strong> (<a class="reference external" href="http://www.plone.org">http://www.plone.org</a>): Die definitive Plone-Site, die alles enthält, was Sie jemals über Plone wissen möchten, inklusive Dokumentation und Installationspaketen zum Herunterladen.</li>
<li><strong>Plone Demo Site</strong> (<a class="reference external" href="http://demo.plone.org">http://demo.plone.org</a>): Eine Demo-Site für Plone mit einer großen Anzahl von Produkten.</li>
<li><strong>Zope.org</strong> (<a class="reference external" href="http://www.zope.org">http://www.zope.org</a>): Die definitive Zope-Community-Site, die von der Zope Corporation betrieben wird. Sie enthält eine Menge Informationen über Zope und ist wahrscheinlich eine der umfangreichsten Community-Sites in Plone.</li>
<li><strong>Liquidnet</strong> (<a class="reference external" href="http://www.liquidnet.com">http://www.liquidnet.com</a>): Die Website einer Investment-Firma, auf der viel Flash verwendet wird.</li>
<li><strong>Design Science Toys</strong> (<a class="reference external" href="http://www.dstoys.com">http://www.dstoys.com</a>): Eine Site, die Spielzeug verkauft und ein Open-Source-E-Commerce-Produkt für Plone verwendet.</li>
<li><strong>Give Kids the World</strong> (<a class="reference external" href="http://www.gktw.org">http://www.gktw.org</a>): Eine Site für die Beschaffung von Fördermitteln für Kinder, geschrieben in Plone unter Verwendung von viel Flash.</li>
<li><strong>Propane</strong> (<a class="reference external" href="http://www.usepropane.com">http://www.usepropane.com</a>): Eine viel besuchte Kunden-Website mit ausgeklügelter Suche, vollständig in Plone implementiert.</li>
<li><strong>Maestro Headquarters</strong> (<a class="reference external" href="http://mars.telascience.org">http://mars.telascience.org</a>): Eine NASA-Website, mit Informationen über die Mars-Rover. Ihre Benutzerschnittstelle wird in Kapitel 7 beschrieben.</li>
</ul>
<!-- More Plone sites are available at http://www.plone.org/about/sites, including sites that provide a quite different user interface. Without knowing about the development of these sites, it would in fact be hard to tell that these sites use Plone. -->
<p class="last">Weitere Plone-Sites werden unter <a class="reference external" href="http://www.plone.org/about/sites">http://www.plone.org/about/sites</a> aufgeführt, darunter solche, die über eine ganz andere Benutzerschnittstelle verfügen. Wenn man nichts über die Entwicklung dieser Sites weiß, wird man kaum erkennen, dass diese Sites Plone verwenden.</p>
</div>
</div>
</div>
<div class="section" id="beitragen-zur-plone-entwicklung">
<h3>Beitragen zur Plone-Entwicklung</h3>
<p>Obwohl Plone über eine beachtliche Liste von Eigenschaften verfügt, ist die Liste seiner gewünschten Eigenschaften noch viel beachtlicher. Aus diesem Grund sucht das Projekt ständig nach Leuten, die ein wenig von ihrer Zeit dafür aufbringen können.</p>
<p>Da Plone auf den Endbenutzer zugeschnitten ist, gibt es zum Glück Bedarf an einem breiten Spektrum von Fähigkeiten. Helfer aus ganz verschiedenen Gebieten sind willkommen, nicht nur Programmierer oder Webentwickler. Plone benötigt Entwickler von Benutzerschnittstellen, Usability-Experten, Grafikdesigner, Übersetzer, Autoren und Tester. Den aktuellen Entwicklungsstand finden Sie auf der Plone-Website unter <a class="reference external" href="http://plone.org/development">http://plone.org/development</a>, und am leichtesten kann man mitmachen, indem man eine Mailing-Liste abonniert oder die Entwickler auf einem IRC-Kanal (Internet Relay Chat) besucht.</p>
</div>
<div class="section" id="was-sind-zope-und-das-cmf">
<h3>Was sind Zope und das CMF?</h3>
<p>Plone baut auf Zope und CMF (Content Management Framework) auf. Um Plone zu verstehen, müssen Sie Zope und das CMF als darunter liegende Architektur verstehen. Aus diesem Grund werde ich in diesem Abschnitt diese beiden Dinge vorstellen und erklären, wie sie mit Plone zusammenspielen.</p>
<p>Zope ist ein mächtiger und flexibler Open-Source-Web-Application-Server, der von der Zope Corporation (<a class="reference external" href="http://www.zope.org">http://www.zope.org</a>) entwickelt wurde. Zope wurde als eigenständiges CMS entwickelt, hat aber mit der Zeit den Anforderungen seiner Benutzer nicht genügt. Dann hat die Zope Corporation das CMF als Open Source-Projekt entwickelt. Das CMF bietet Entwicklern die Werkzeuge zum Erstellen von komplexen CMS. Es bietet Workflow, Site-Aussehen und weitere Funktionen.</p>
<p>Das CMF ist ein Framework für ein System, d.h., es bietet die Werkzeuge, mit denen Entwickler ein Produkt erstellen können, statt ein fertiges System, das die Benutzer sofort einsetzen könnten. Plone verwendet diese und viele weitere Eigenschaften und verbessert sie zu einem qualitativ hochwertigen Produkt für den Benutzer. Plone ist eine Schicht über dem CMF, die eine Anwendung darstellt, die unter Zope läuft. Sie müssen das CMF verstanden haben, um Plone zu verstehen. Für die meisten Verwaltungsfunktionen wird die Verwaltungsschnittstelle von Zope benötigt, und für die Entwicklung mit Plone muss man wissen, Zope und dessen Objekte funktionieren.</p>
<p>Dieses Buch behandelt Zope nicht in seiner ganzen Tiefe, sondern gibt Ihnen genug Informationen darüber, damit Sie Aufgaben in Plone erledigen können. Durch die Lektüre dieses Buches erhalten Sie ausreichende Informationen, damit Sie fast alles in Plone anpassen und ändern können. Wenn Sie weitere Informationen zu Zope benötigen, empfehle ich Ihnen <em>The Zope Book</em>, das ursprünglich bei New Riders erschienen ist. Danach ist es online verfügbar gemacht worden und wird von Mitgliedern der Zope-Community aktualisiert. Es ist gratis online unter <a class="reference external" href="http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition">http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition</a> verfügbar.</p>
<p>Sowohl Zope als auch das CMF sind Schlüsseltechnologien, ohne die Plone nicht existieren würde. Das Plone-Team ist vor allem der Zope Corporation zu sehr großem Dank für die Weitsicht verpflichtet, Zope und das CMF als Open Source verfügbar zu machen. Die Liste derer, denen ich dort sowie in den CMF-Communities danken möchte, ist sehr lang. Danke an alle Beteiligten!</p>
</div>
<div class="section" id="was-ist-python">
<h3>Was ist Python?</h3>
<p>Zope ist in Python geschrieben, einer objektorientierten Open-Source-Programmiersprache, die mit Perl oder Tcl vergleichbar ist. Man muss Python nicht kennen, um Plone zu benutzen oder gar einfache Administrationsaufgaben damit zu erledigen. Zum Anpassen von Produkten und beim Scripting von Plone ist jedoch ein wenig Python notwendig.</p>
<p>Tommy Burnette, ein erfahrener technischer Direktor bei Industrial Light &amp; Magic, sagt Folgendes über Python (<a class="reference external" href="http://www.python.org/Quotes.html">http://www.python.org/Quotes.html</a>):</p>
<blockquote>
<em>Python spielt in unserem Produktionsprozess eine Schlüsselrolle. Ohne Python wäre ein Projekt von der Größenordnung von</em> Star Wars: Episode II <em>sehr schwer zu realisieren gewesen. Python hält alles zusammen, vom Rendering sehr vieler Objekte über die Stapelverarbeitung bis hin zum Compositing.</em></blockquote>
<p>Wenn Sie vorhaben, irgendetwas Anspruchsvolles mit Plone zu machen, sollten Sie sich zwei bis drei Tage Zeit nehmen und die Grundlagen von Python lernen. Danach werden Sie nicht nur Plone wesentlich besser anpassen können, sondern Sie werden auch allgemein mit Objekten vertraut sein und damit, wie sie mit der Plone-Umgebung zusammenspielen. Es würde den Rahmen dieses Buches sprengen, Ihnen Python beizubringen. Daher setze ich voraus, dass Sie über Grundkenntnisse in Python verfügen. Diese Grundkenntnisse sind ausreichend, um mit diesem Buch zu arbeiten und eine Plone-Installation leicht anzupassen.</p>
<p>Glücklicherweise ist Python eine sehr leicht zu erlernende Programmiersprache. Ein erfahrener Programmierer braucht durchschnittlich einen Tag, um damit produktiv zu werden. Frischgebackene Programmierer brauchen etwas länger. Wenn Sie Plone mit den Installationsprogrammen unter Windows oder auf Macs installieren, ist die passende Version von Python darin enthalten. Sie können Python separat für fast jedes Betriebssystem unter <a class="reference external" href="http://www.python.org">http://www.python.org</a> herunterladen.</p>
<p>Die beste Art, sich mit Python vertraut zu machen, ist die, es in einem Kommandointerpreter auszuprobieren. Sollten Sie eine Windows-Installation von Plone haben, dann gibt es im Startmenü einen Link zu Pythonwin, einer Python-IDE (Integrated Development Environment). Gehen Sie zu <em>Start - Programme - Plone - Pythonwin</em> (siehe Abbildung 1.2).</p>
<a class="reference external image-reference" href="img/01-02.png/image_view_fullscreen"><img alt="img/01-02.png" class="original" src="img/01-02.png" /></a>
<p>Abbildung 1.2. Der Python-Prompt unter Windows</p>
<p>Unter Linux und Mac OS X wird bei der Eingabe von <strong>python</strong> normalerweise der Python-Interpreter gestartet:</p>
<pre class="literal-block">
$ python
Pyython 2.3.2 (#1, Oct  6 2003, 10:07:16)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt;
</pre>
<p>Da Python eine interpretierte Sprache ist, müssen Sie nicht das ganze Python-Script kompilieren und ausführen, sondern können einzelne Zeilen an den Interpreter übergeben, während Sie diese schreiben. Das macht aus dem Interpreter ein überraschend nützliches Werkzeug zum Testen und zur Fehlersuche in Code. Im Interpreter hat jede Zeile, die auf eine Eingabe wartet, das Präfix <tt class="docutils literal">&gt;&gt;&gt;</tt>.</p>
<p>Das einfachste &quot;Hello World&quot;-Programm sieht z.B. wie folgt aus:</p>
<pre class="literal-block">
&gt;&gt;&gt; print &quot;Hello, world!&quot;
Hello, world!
&gt;&gt;&gt;
</pre>
<p>Um den Interpreter zu beenden, geben Sie unter Linux <strong>Strg+D</strong> (drücken Sie die Taste <strong>D</strong>, während Sie <strong>Strg</strong> gedrückt halten) bzw. <strong>Strg+Z</strong> unter Windows ein (Sie können das später auch für eine fortgeschrittenere Interaktion mit Zope und Plone verwenden). Normale Python-Scripts können Sie ausführen, indem Sie diese an den Interpreter übergeben. Mit dem folgenden Script namens <tt class="docutils literal">hello.py</tt>:</p>
<pre class="literal-block">
print &quot;Hello, world!&quot;
</pre>
<p>können Sie folgenden Befehl ausführen:</p>
<pre class="literal-block">
$ python hello.py
Hello, world!
</pre>
<p>Die Python-Website unter <a class="reference external" href="http://www.python.org">http://www.python.org</a> verfügt über eine hervorragende Dokumentation, besonders das Tutorium. Auch die folgenden Bücher bieten einen guten Überblick zu Python:</p>
<ul class="simple">
<li><strong>Dive Into Python (Apress, 2004)</strong>: Basierend auf Mark Pilgrims beliebtem webbasierten Tutorium bietet dieses Buch seinen Lesern eine schnelle Einführung in die Sprache Python. Dies ist ein tolles Buch, das für erfahrene Programmierer gedacht ist.</li>
<li><strong>Learning Python, Second Edition (O'Reilly, 2003)</strong>: Dieses Buch behandelt die Version 2.3 von Python und bietet einen guten Überblick zu Python und all seinen neuen Eigenschaften. Es eignet sich gut für relativ neue Programmierer.</li>
<li><strong>Practical Python (Apress, August 2002)</strong>: Diese sehr praxisorientierte Einführung zu Python bietet einen Überblick über die vielen Eigenschaften der Sprache. Die Leser können ihr Wissen sofort in die Praxis umsetzen, indem sie an zehn interessanten Projekten arbeiten. Dazu zählen ein webbasiertes schwarzes Brett und eine File-Sharing-Anwendung mit einer grafischen Benutzerschnittstelle.</li>
<li><strong>Python Essential Reference, Second Edition (Sams, 2001)</strong>: Ein Referenzbuch, das eine sehr gute Übersicht aller wesentlichen Bibliotheken und Funktionen bietet. Dies ist ein exzellentes Buch für erfahrene Programmierer.</li>
<li><strong>Objektorientierte Programmierung mit Python (Mitp, 2004)</strong>: Ein fundiertes Lehr- und Arbeitsbuch von Michael Weigend.</li>
<li><strong>Python für Kids (Mitp, 2003)</strong>: Ein unterhaltsames Einsteigerbuch von Gregor Lingl, das durchaus nicht nur für Kinder zu gebrauchen ist.</li>
<li><strong>Python kurz &amp; gut (O'Reilly 2002)</strong>: Ein kleines praktisches Buch von Mark Lutz mit dem Essentiellen, was man über Python wissen muss, leider nicht mehr durchgehend aktuell.</li>
<li><strong>Einführung in Python (O'Reilly, 2000)</strong>: Ein gut lesbares Einsteigerbuch von&nbsp;den erfahrenen Autoren Mark Lutz und David Ascher, leider nur in der ersten und nicht in der verbesserten zweiten Auflage auf deutsch übersetzt.</li>
</ul>
</div>
<div class="section" id="typographische-konventionen">
<h3>Typographische Konventionen</h3>
<p>In diesem Buch werden folgende typographischen Konventionen verwendet:</p>
<ul class="simple">
<li><strong>Kursiv</strong>: Neue Begriffe werden <em>kursiv</em> dargestellt. (Anhang C enthält ein umfangreiches Glossar, in dem alle Akronyme definiert werden.)s</li>
<li><strong>Fett</strong>: Falls es im Text Anweisungen gibt, die etwas beinhalten, das Sie auf Ihrer Tastatur eingeben sollen, werden diese Worte <strong>fett</strong> gedruckt.</li>
<li><strong>Code-Schrift</strong>: Eine <tt class="docutils literal">nichtproportionale Schrift</tt> wird bei Dateinamen, Verzeichnispfaden, Code, Variablen und URLs (Uniform Resource Locators) verwendet.</li>
<li><strong>Kapitälchen</strong>: <em>Kapitälchen</em> heben Menüoptionen, Tab- und Buttonbeschriftungen hervor (In der Online-Version durch <strong>Kursiv</strong> ersetzt).</li>
</ul>
<p>Dieses Buch enthält viele Bildschirmdarstellungen von Zope und Plone. Da Plone ein sich schnell weiterentwickelndes Produkt ist, können die Bildschirmdarstellungen leicht von der Version der Software abweichen, die Sie verwenden. Diese Unterschiede sollten jedoch gering sein und Ihr Verständnis von dem System nicht beeinträchtigen.</p>
</div>
<div class="section" id="verwendete-software">
<h3>Verwendete Software</h3>
<p>Für dieses Buch werden die im Folgenden genannten Software-Versionen verwendet. Auch wenn dieses Buch mit genau diesen Versionen im Hinterkopf geschrieben ist, sollte die gesamte Software auf diesen wie auch auf kommenden Versionen funktionieren.</p>
<p>Beim Schreiben dieses Buches war Plone 2.0 die aktuellste Version von Plone. Es ist die zweite große Version dieser Software, und sie bietet, verglichen mit der Version 1.0, viele neue Eigenschaften, darunter die Verwaltung von Gruppenbenutzern, eine neue Schnittstelle sowie eine verbesserte Zope-Distribution. Es wird Ihnen wärmstens empfohlen, neue Projekte mit 2.0 oder höher zu beginnen, anstatt ältere Versionen zu benutzen.</p>
<p>Plone 2.0 ist von folgenden Versionen abhängig: Zope 2.7, CMF 1.4.2 und Python 2.3.3. Alle Code-Beispiele in diesem Buch wurden speziell so entworfen, dass sie nicht von diesen Versionen oder von einem bestimmten Betriebssystem abhängig sind. Es kann jedoch Situationen geben, in denen das nicht der Fall ist. Für daraus entstehende Unannehmlichkeiten möchte ich mich entschuldigen.</p>
</div>
<div class="section" id="buchlizenz">
<h3>Buchlizenz</h3>
<p>Die Idee zu diesem Buch hatte ursprünglich eine Gruppe von Plone-Benutzern, die eine qualitativ hochwertige Dokumentation erstellen wollten. Die erste Version dieses Buchs haben wir auf der Plone-Website als Open-Source-Dokumentationsprojekt veröffentlicht. Alle Inhalte, die auf der Plone-Website hinzugefügt wurden, standen unter der Open-Publication-Lizenz.</p>
<p>Das wachsende Interesse an Plone ließ ein kommerzielles Buch vernünftig erscheinen, und so haben Apress und ich im Sommer des Jahres 2003 dieses Buch begonnen. Ich habe einiges an Material vom alten Buch mit Erlaubnis der ursprünglichen Autoren verwendet. Nach dem Wechsel zu Plone 2 habe ich große Mengen an neuem Material hinzugefügt. Dieses Buch wird nun unter der Creative-Commons-Lizenz veröffentlicht, die die Wiederverwendung dieser Arbeit gestattet, sofern der ursprüngliche Autor erwähnt wird. Für kommerzielle Zwecke dürfen Sie dieses Werk jedoch nicht verwenden. Weitere Informationen zu der Lizenz finden Sie unter <a class="reference external" href="http://creativecommons.org/licenses/by-nc-sa/1.0/">http://creativecommons.org/licenses/by-nc-sa/1.0/</a>.</p>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch2.rst">
    <title>2. Plone installieren</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch2.rst</link>
    <description>Dieses Kapitel erklärt, wie man Plone auf einer Vielzahl von Plattformen installiert und grundlegende Konfigurationseinstellungen daran vornimmt. Wenn Sie Plone nur ganz schnell testen möchten, gehen Sie das am einfachsten zur Demo-Site unter http://demo.plone.org, wo Sie Inhalte sofort hinzufügen und ändern können, ohne etwas installieren zu müssen.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Plone installieren</h2>
<p>Dieses Kapitel erklärt, wie man Plone auf einer Vielzahl von Plattformen installiert und grundlegende Konfigurationseinstellungen daran vornimmt. Wenn Sie Plone nur ganz schnell testen möchten, gehen Sie das am einfachsten zur Demo-Site unter <a class="reference external" href="http://demo.plone.org">http://demo.plone.org</a>, wo Sie Inhalte sofort hinzufügen und ändern können, ohne etwas installieren zu müssen.</p>
<p>Anders als bei den anderen Kapiteln macht es keinen großen Sinn, dieses Kapitel von vorn bis hinten durchzulesen. Ich habe es nach Betriebssystemen unterteilt, damit Sie nur die für Sie notwendigen Abschnitte lesen müssen, um Plone zu installieren. Plone lässt sich auf allen Plattformen installieren, die Zope unterstützt: Windows, Mac OS X, Linux, die meisten Unix-Versionen und Solaris.</p>
<p>Ein leistungsfähiger Computer führt natürlich dazu, dass auch Plone eine bessere Leistung zeigt. Plone ist ein komplexes System, das gewisse Ansprüche an die Verarbeitungsgeschwindigkeit und den Hauptspeicher stellt. Bei einem System in Produktion wird allgemein empfohlen, keinen Rechner mit weniger als einem 2 GHz schnellen Prozessor und weniger als 1 Gbyte RAM einzusetzen, falls es eine große Website enthält. Für kleinere Sites funktioniert Plone aber auch bei einer 500-MHz-CPU und 64 Mbyte Hauptspeicher. Für detailliertere Informationen über Plones Performance, Caching und Optimierungsmöglichkeiten lesen Sie bitte Kapitel 14. Eine Grundinstallation von Plone benötigt etwa 50 Mbyte auf der Festplatte. Falls Sie bereits Zope oder Python installiert haben, wird wesentlich weniger Platz benötigt, etwa 2 Mbyte. Sie müssen auch die Objektdatenbank von Plone berücksichtigen, die fast beliebig groß werden kann, je nachdem, welche Datenmengen Sie darin speichern.</p>
<p>Um Plone zu benutzen, brauchen Sie einen Webbrowser mit Zugriff auf den Server. Wenn sich Benutzer auf Ihrer Site anmelden wollen, dann müssen diese Cookies eingeschaltet haben. JavaScript wird nicht verlangt, bietet aber reichhaltigere Benutzungsmöglichkeiten. Da Plone ausgiebigen Gebrauch von Cascading Style Sheets (CSS) macht, stellen moderne Browser die Plone-Schnittstelle reichhaltiger und attraktiver dar, aber funktionieren sollte sie in jedem vernünftigen Browser.</p>
<p>Ich empfehle, einen der folgenden Browser zu verwenden:</p>
<ul class="simple">
<li>Microsoft Internet Explorer 5.5 oder höher</li>
<li>Netscape 7.0 oder höher (sowie jegliches Mozilla-Derivat)</li>
<li>Opera 7.0 oder höher</li>
<li>Konqueror 3.0 oder höher</li>
<li>Safari 1.0 oder höher</li>
</ul>
<p>Plone funktioniert in den folgenden Browsern ebenfalls, auch wenn es evtl. anders als im Original aussieht:</p>
<ul class="simple">
<li>Netscape 4.x</li>
<li>Microsoft Internet Explorer 5.0</li>
<li>Microsoft Internet Explorer 4.0</li>
<li>Konqueror 2.x</li>
<li>Lynx (textbasiert)</li>
<li>w3m (textbasiert)</li>
<li>AWeb</li>
<li>Links (textbasiert, optional mit Grafik)</li>
<li>Alle Browser, die eine Grundmenge an HTML (Hypertext Markup Language) sowie Cookies für Formulareingaben verstehen, darunter die meisten mobilen Browser und solche auf PDAs (Personal Digital Assistant)</li>
</ul>
<div class="section" id="plone-unter-windows-installieren">
<h3>Plone unter Windows installieren</h3>
<p>Am einfachsten kann man Plone installieren, wenn man das Windows-Installationsprogramm von Plone verwendet, das die Installation automatisiert. Zu der Installation gehören zusätzliche Pakete und Optionen, eine vorkonfigurierte Datenbank, die Einrichtung von Diensten sowie Python-Pakete für Windows. Sie können dieses Installationsprogramm unter <a class="reference external" href="http://www.plone.org/download">http://www.plone.org/download</a> herunterladen.</p>
<div class="section" id="das-installationsprogramm-verwenden">
<h4>Das Installationsprogramm verwenden</h4>
<p>Das Installationsprogramm wurde unter Windows 9x, ME, NT 3.51+, 2000 und XP getestet, sollte aber auch unter anderen Windows-Versionen funktionieren. Sie sollten Administratorzugang zum gewünschten Rechner haben, da das Programm versucht, in der Windows-Registry Dienste einzurichten und Einstellungen vorzunehmen. Falls Sie bereits Zope oder Python installiert haben, sollten Sie vielleicht den Quellcode separat installieren, um Plattenplatz zu sparen.</p>
<p>Bevor Sie Plone installieren, sollten Sie darauf achten, ob weitere Webserver gerade laufen. Neuere Windows-Versionen installieren und starten beispielsweise automatisch IIS (Microsoft Internet Information Services), der Port 80 abhört. Das Installationsprogramm startet Plone auf den Ports 80 und 8080. Am einfachsten testet man, ob schon jemand Port 80 verwendet, indem man in einem Browser die Adresse <a class="reference external" href="http://127.0.0.1/">http://127.0.0.1/</a> eingibt und wartet, ob eine Seite gefunden wird. Diesen Webserver können Sie deaktivieren oder aber die Ports für Plone ändern. Siehe dazu den Abschnitt &quot;Server-Konfiguration&quot; weiter unten in diesem Kapitel. Falls Sie Plone hinter IIS oder Plone und IIS gleichzeitig auf dem gleichen Server betreiben möchten, erfahren Sie in Kapitel 14 mehr dazu. Im Moment ist es am einfachsten, diesen Webserver zu deaktivieren.</p>
<p>Nachdem Sie das Installationsprogramm heruntergeladen haben, beginnen Sie die Installation mit einem Doppelklick darauf (siehe Abbildung 2.1).</p>
<a class="reference external image-reference" href="img/02-01.png/image_view_fullscreen"><img alt="img/02-01.png" class="original" src="img/02-01.png" /></a>
<p>Abbildung 2.1. Start des Plone-Installationsprogramms</p>
<p>Das Installationsprogramm führt die bei der Software-Installation üblichen Schritte aus. Klicken Sie auf <em>Weiter</em>, um fortzufahren oder auf <em>Abbrechen</em>. Das Programm erlaubt Ihnen die Auswahl eines Installationsverzeichnisses, wobei die Voreinstellung <tt class="docutils literal"><span class="pre">C:\Programme\Plone</span> 2</tt> ist (siehe Abbildung 2.2).</p>
<a class="reference external image-reference" href="img/02-02.png/image_view_fullscreen"><img alt="img/02-02.png" class="original" src="img/02-02.png" /></a>
<p>Abbildung 2.2. Wahl eines Verzeichnisses</p>
<p>Wenn Sie zum <em>Passwort</em>-Bildschirm gelangen (siehe Abbildung 2.3), müssen Sie einen Benutzernamen und ein Passwort eingeben. Dadurch wird für Sie ein Benutzer erstellt, und es wird eine Plone-Site unter diesem Benutzer angelegt. Oftmals erstellen die Leute dafür einen Benutzer namens <em>admin</em> oder so ähnlich. Diesen Benutzernamen und dieses Passwort benötigen Sie später, d.h., Sie sollten sich beides merken. Sollten Sie das Passwort verlieren, können Sie jedoch später ein neues erstellen.</p>
<a class="reference external image-reference" href="img/02-03.png/image_view_fullscreen"><img alt="img/02-03.png" class="original" src="img/02-03.png" /></a>
<p>Abbildung 2.3. Eingabe eines Benutzernamens und -passworts</p>
<p>Die Installation dauert etwa fünf Minuten, je nachdem, wie schnell Ihr Rechner ist. Am Ende der Installation werden einige Aufgaben erledigt, z.B. die Kompilierung aller Python-Dateien und die Einrichtung der Datenbank. Sobald alles beendet ist, erscheint eine Meldung, um Sie davon in Kenntnis zu setzen (siehe Abbildung 2.4).</p>
<a class="reference external image-reference" href="img/02-04.png/image_view_fullscreen"><img alt="img/02-04.png" class="original" src="img/02-04.png" /></a>
<p>Abbildung 2.4. Letzter Einrichtungsbildschirm</p>
<p>Um Plone zu starten, gehen Sie zum Plone-Controller, indem Sie auf <em>Start - Programme - Plone - Plone</em> klicken. Der Controller ist eine Anwendung mit einer hübschen Oberfläche zum Starten und Anhalten von Plone. Er beginnt mit einer Statusseite, auf der Sie Ihre Plone-Installation ganz leicht starten oder anhalten können (siehe Abbildung 2.5).</p>
<a class="reference external image-reference" href="img/02-05.png/image_view_fullscreen"><img alt="img/02-05.png" class="original" src="img/02-05.png" /></a>
<p>Abbildung 2.5. Plone läuft nicht.</p>
<p>Wie in Abbildung 2.5 zu sehen ist, zeigt dieser Bildschirm den Status Ihrer Plone-Installation an. Plone startet nicht automatisch, sondern Sie müssen auf <em>Start</em> klicken, um es zu starten. Danach müssen Sie evtl. eine Minute warten, bis der Startvorgang abgeschlossen ist (siehe Abbildung 2.6).</p>
<a class="reference external image-reference" href="img/02-06.png/image_view_fullscreen"><img alt="img/02-06.png" class="original" src="img/02-06.png" /></a>
<p>Abbildung 2.6. Nun läuft Plone.</p>
<p>Nachdem Plone gestartet wurde, können Sie auf die Plone-Site zugreifen, indem Sie auf den <em>View</em>-Button klicken. Dabei wird ein Browser gestartet, der auf die Plone-Site zugreift. Dann sollten Sie die Willkommensseite von Plone sehen können. Beachten Sie, dass die Adresse im Browser <a class="reference external" href="http://localhost/">http://localhost/</a> lautet. Mit dieser Adresse greifen Sie auf Ihre Plone-Site zu. Ein Klick auf den <em>Zope Management Interface</em>-Button startet einen Browser und greift auf die Management-Schnittstelle zu. Die Adresse im Browser dafür lautet <a class="reference external" href="http://localhost:8080/manage">http://localhost:8080/manage</a>. Mit ihr haben Sie Zugriff auf den darunter liegenden Application Server. Wenn Sie den <em>Manage</em>-Button anklicken und auf Plone zugreifen, werden Sie nach einem Benutzernamen und Passwort gefragt. Das sind diejenigen, die Sie im Installationsprogramm eingegeben haben.</p>
<p>Der Controller weiß, ob Sie Plone als Dienst installiert haben oder nicht. Wenn Plone als Windows-Dienst installiert wurde, können Sie Plone mit den Standard-Management-Werkzeugen und -Befehlen unter <em>Systemsteuerung - Verwaltung - Dienste</em> starten und anhalten. Ansonsten können Sie in der Task-Leiste ein kleines Icon sehen. An diesem Punkt werden Sie Inhalte eingeben wollen. Blättern Sie dazu zu Kapitel 3 weiter.</p>
</div>
<div class="section" id="server-konfiguration-unter-windows">
<h4>Server-Konfiguration unter Windows</h4>
<p>Die Konfigurationsdaten von Plone befinden sich in einer Textdatei, die Sie bearbeiten können, um Ihre Plone-Instanz zu konfigurieren. Sie können die Ports, die Plone abhört, die verwendeten Logdateien und eine Menge anderer Einstellungen ändern. Unter Windows sind einige wesentliche Eigenschaften über den Controller und eine GUI (Graphical User Interface) verfügbar. Wenn Sie andere Einstellungen ändern möchten, lesen Sie bitte Anhang A mit der vollständigen Liste der Konfigurationsoptionen. Um auf den Controller zuzugreifen, wählen Sie <em>Start - Programme - Plone - Plone</em>. Dann wird der Controller gestartet.</p>
<p>Wie bereits erwähnt wurde, sehen Sie zuerst die Statusseite, auf der Sie Plone starten oder anhalten können. Im linken Teil des Controllers befinden sich einige weitere Bildschirme, die ich nun beschreiben werde.</p>
<div class="section" id="andern-der-ports">
<h5>Ändern der Ports</h5>
<p>Bei der Auswahl der Ports, wie sie in Abbildung 2.7 zu sehen ist, können Sie die Ports angeben, die Plone nach eintreffenden Verbindungen wie HTTP, FTP (File Transfer Protocol) und WebDAV (Web-based Distributed Authoring and Versioning) abhört.</p>
<a class="reference external image-reference" href="img/02-07.png/image_view_fullscreen"><img alt="img/02-07.png" class="original" src="img/02-07.png" /></a>
<p>Abbildung 2.7. Die Ports-Seite zeigt die Ports an, auf denen Plone läuft.</p>
<p>Wie bei der Installation schon erwähnt wurde, möchten Sie wahrscheinlich sicherstellen, dass kein anderer Server wie IIS, Apache und PWS (Personal Web Server) den gleichen Port 80 abhört wie der Plone-Server. Beim Schreiben sind nur die Plone HTTP- und Zope Management HTTP-Ports aktiviert. Um diese zu aktivieren, müssen Sie sie in einer Textdatei konfigurieren. Folgende vier Felder kommen auf der Ports-Seite vor:</p>
<ul class="simple">
<li><strong>Plone</strong> <strong>HTTP</strong>: Dieses Feld gibt den Port an, mit dem der Benutzer auf Plone zugreift. Der Vorgabewert dafür ist 80, der Standardwert für einen Webserver. Obwohl dieser Port nicht benötigt wird, könnten Sie in einem Webbrowser ohne ihn nicht auf Plone zugreifen. Wenn dieser Port aktiviert ist und Plone läuft, ist der <em>View</em>-Button auf der Statusseite aktiviert.</li>
<li><strong>Zope Management</strong> <strong>HTTP</strong>: Dieses Feld gibt den Port an, mit dem man als Administrator auf Plone zugreift. Der Vorgabewert dafür ist 8080. Über diesen Port haben Sie Zugriff über das ZMI (Zope Management Interface) auf die Wurzel von Zope. Sie können dort immer noch über den HTTP-Port hinkommen, aber es ist einfacher und bequemer, einen eigenen Port dafür zu haben. Wenn dieser Port aktiviert ist und Plone läuft, ist der <em>Manage</em>-Button auf der Statusseite aktiviert.</li>
<li><strong>FTP Access</strong>: Dieses Feld gibt den Port an, mit dem man via FTP auf Plone zugreifen kann. Die Vorgabe dafür ist leer, d.h., er ist nicht aktiviert. Wenn Sie ihn aktivieren möchten: Der übliche Wert dafür beträgt 21. Über FTP können Sie große Dateien an und von Plone übertragen.</li>
<li><strong>WebDAV Source</strong>: Dieses Feld gibt den Port an, mit dem man über WebDAV auf Plone zugreift. Die Vorgabe dafür ist leer, d.h., er ist nicht aktiviert. Der übliche Wert dafür beträgt 8081. WebDAV ist ein Protokoll für die entfernte Bearbeitung von Plone-Inhalten. Damit können Sie Ihren Plone-Server z.B. auf einen Windows-Laufwerksbuchstaben abbilden.</li>
</ul>
</div>
<div class="section" id="verwenden-der-emergency-user-page">
<h5>Verwenden der Emergency User Page</h5>
<p>Die Emergency User Page wird in Kapitel 9 behandelt, aber kurz gesagt erhalten Sie damit einen Systemzugriff in Notfällen, wenn Sie Ihren Benutzernamen oder Ihr Passwort vergessen haben.</p>
</div>
<div class="section" id="plone-im-debug-modus-starten">
<h5>Plone im Debug-Modus starten</h5>
<p>Bisher haben Sie Plone im Produktionsmodus gestartet und angehalten. Dies ist die schnellste und empfohlene Art, Plone auszuführen. Bei der Entwicklung von Add-Ons oder bei der Fehlersuche müssen Sie Plone im Debug-Modus starten. Das ist die empfohlene Methode, Plone auszuführen, wenn Sie Produkte und Aussehen (Skins) entwickeln, wie Sie es in späteren Kapiteln tun werden. Dies ist nicht die Standardmethode, weil Plone dabei etwa zehnmal langsamer als normal ist.</p>
<p>Um Plone im Debug-Modus zu starten, wählen Sie <em>Start - Programme - Plone - Plone (Debug Mode)</em>, wonach eine Eingabeaufforderung erscheint, in deren Fenster alle Log-Informationen ausgegeben werden (siehe Abbildung 2.8).</p>
<a class="reference external image-reference" href="img/02-08.png/image_view_fullscreen"><img alt="img/02-08.png" class="original" src="img/02-08.png" /></a>
<p>Abbildung 2.8. Plone von der Kommandozeile aus starten</p>
<p>Wenn Sie testen möchten, ob Plone läuft, starten Sie einen Browser und geben <a class="reference external" href="http://localhost/">http://localhost/</a> ein. Wenn Plone erfolgreich installiert ist, können Sie die Willkommensseite von Plone sehen.</p>
</div>
</div>
</div>
<div class="section" id="plone-unter-mac-os-x-unix-und-linux-installieren">
<h3>Plone unter Mac OS X, Unix und Linux installieren</h3>
<p>Unter Mac OS X, Unix und Linux unterscheidet sich die Installation leicht, aber ihre Konfiguration ist sehr ähnlich. Für die verschiedenen Betriebssysteme sind unterschiedliche Installationspakete vorhanden, z.B. für Mac OS X, Debian, Gentoo, FreeBSD, OpenBSD sowie RPM-Paketmanager (RPMs) für Red Hat, SuSE und Mandrake. In den folgenden Abschnitten behandle ich einige der weiter verbreiteten: Mac OS X, Red Hat und Debian. Weitere Informationen zu Ihrem eigenen Betriebssystem finden Sie in den systemspezifischen Installationsanweisungen.</p>
<div class="section" id="installation-unter-mac-os-x">
<h4>Installation unter Mac OS X</h4>
<p>Das Installationsprogramm automatisiert die Installation von Plone unter Mac OS X und wurde unter den Versionen 10.2.3 und höher getestet. Auf dem Zielrechner, wo die Installation erfolgen soll, benötigen Sie Administratorrechte. Sie können das Programm unter <a class="reference external" href="http://www.plone.org/download">http://www.plone.org/download</a> herunterladen. Anschließend führen Sie einen Doppelklick darauf aus, um das Archiv auszupacken. Doppelklicken Sie auf das dann entstandene Installationspaket, um die Installation zu beginnen. Danach sollten Sie den Bildschirm aus Abbildung 2.9 sehen.</p>
<a class="reference external image-reference" href="img/02-09.png/image_view_fullscreen"><img alt="img/02-09.png" class="original" src="img/02-09.png" /></a>
<p>Abbildung 2.9. Genehmigen der Installation mit Ihrem Mac OS X-Passwort</p>
<p>Geben Sie das Passwort Ihres Mac OS X-Kontos ein, um die Installation zu genehmigen. Dazu muss Ihr Konto über Administratorrechte verfügen. Wenn das nicht der Fall sein sollte, melden Sie sich ab und wieder als jemand mit solchen Rechten an, um dann das Installationsprogramm wieder zu starten. Möglicherweise möchten Sie das Installationspaket nach <tt class="docutils literal">/Users/Shared</tt> verschieben, bevor Sie sich abmelden, damit Sie von einem anderen Benutzerkonto darauf zugreifen können. Nach erfolgter Genehmigung können Sie den Bildschirm aus Abbildung 2.10 sehen.</p>
<a class="reference external image-reference" href="img/02-10.png/image_view_fullscreen"><img alt="img/02-10.png" class="original" src="img/02-10.png" /></a>
<p>Abbildung 2.10. Willkommen im Installationsprogramm</p>
<p>Das Installationsprogramm führt die üblichen Schritte einer Software-Installation durch. Wenn nötig, klicken Sie auf die unteren <em>Weiter</em>- und <em>Zurück</em>-Buttons. Die meisten Schritte sind selbsterklärend, aber bei der Auswahl der Installationspartition für Plone müssen Sie jene Partition wählen, auf der Mac OS X installiert ist (siehe Abbildung 2.11).</p>
<a class="reference external image-reference" href="img/02-11.png/image_view_fullscreen"><img alt="img/02-11.png" class="original" src="img/02-11.png" /></a>
<p>Abbildung 2.11. Auswahl der Startpartition</p>
<p>Die Installation benötigt ca. fünf Minuten, je nachdem, wie schnell Ihr Rechner ist. Nach der Installation wird Plone nicht automatisch gestartet. Die Datei <tt class="docutils literal">ReadMe.rtf</tt> in <tt class="docutils literal">/Applications/Plone</tt> enthält eine Menge Informationen darüber, wie Sie Ihre Plone-Installation betreiben und verwalten, und auch darüber, wie Sie Plone starten. Mit dem folgenden Befehl z.B. wird Plone gestartet:</p>
<pre class="literal-block">
sudo /Library/StartupItems/Plone/Plone start
</pre>
<p>Um festzustellen, ob Plone ausgeführt wird, gehen Sie mit einem Webbrowser zu <a class="reference external" href="http://localhost:9090/">http://localhost:9090/</a>, wo Sie eine Willkommensseite von Plone sehen. In der <tt class="docutils literal">ReadMe</tt>-Datei finden Sie den Benutzernamen und das Passwort, das Plone für den Zugriff auf den Server durch Sie eingerichtet hat.</p>
</div>
<div class="section" id="installation-mit-einem-rpm">
<h4>Installation mit einem RPM</h4>
<p>RPMs gibt es für die Distributionen von Red Hat, Mandrake und SuSE. Die neuesten Pakete finden Sie unter <a class="reference external" href="http://www.plone.org/download">http://www.plone.org/download</a>. RPM setzt bei Python die Version 2.3 voraus. Um festzustellen, welche Python-Version Sie verwenden, führen Sie den folgenden Befehl in einer Shell aus:</p>
<pre class="literal-block">
$ python -V
Python 2.3.2
</pre>
<p>In diesem Fall ist Python 2.3.2 installiert. Wenn Sie diese Version nicht haben, finden Sie RPMs auf der Python-Website unter <a class="reference external" href="http://www.python.org">http://www.python.org</a>. Nach dem Herunterladen der Dateien installieren Sie sie mit dem normalen <tt class="docutils literal">rpm</tt>-Befehl. Zum Glück werden bei der Installation von Plone einige nützliche Informationen ausgegeben, z.B.:</p>
<pre class="literal-block">
[root&#64;lappi i386]# rpm -ivh Plone2-2.0.0rh-2.i386.rpm
Preparing... ###########################################
[100%]
Making group plone (not altered if already exists).
Making user plone.
~ 1:Plone2 ###########################################
[100%]
Creating initial 'main' instance...
Instance created. Listening on 127.0.0.1:8080, initial user: 'plone'
with password: 'plone'.
Setup of initial database in 'main' instance...
/usr/lib/plone2/lib/python/AccessControl/Owned.py:79:
DeprecationWarning: Owned.getOwner(1) is deprecated; please use
getOwnerTuple() instead.
~ DeprecationWarning)
Created initial database content.
look at /etc/plone2/main/zope.conf.
Run then &quot;/etc/rc.d/init.d/plone2 start&quot; to start Plone2.
you may create new Plone instances with mkploneinstance.
</pre>
<p>Wie in der vorherigen Ausgabe zu sehen ist, können Sie Plone wie folgt starten:</p>
<pre class="literal-block">
/etc/rc.d/init.d/plone2 start
</pre>
<p>Um festzustellen, ob Plone funktioniert, gehen Sie mit einem Webbrowser zu <a class="reference external" href="http://localhost:9090/">http://localhost:9090/</a>, wo Sie eine Willkommensseite von Plone sehen. Der Benutzername <em>plone</em> und das Passwort <em>plone</em> wurden für Sie eingerichtet.</p>
</div>
<div class="section" id="installation-unter-debian-linux">
<h4>Installation unter Debian Linux</h4>
<p>Unter Debian ist Plone ein Standardpaket und geht durch den standardisierten Release-Prozess, d.h., Sie können sich entweder eine stabile oder eine instabile Version von Plone holen, je nachdem, wie Ihre Debian-Installation konfiguriert ist. Plone installieren Sie einfach mit Debians <em>apt</em>-System. Hier ist ein Beispiel einer Installation:</p>
<pre class="literal-block">
agmweb:/home/andy# apt-get install plone
Reading Package Lists... Done
Building Dependency Tree... Done
The following extra packages will be installed:
  zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
  zope-formulator zopectl
Suggested packages:
  zope-cmfwiki python-unit zope-devguide zope-book
Recommended packages:
  zope-cmfforum zope-localizer
The following NEW packages will be installed:
  plone zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
  zope-formulator zopectl
0 upgraded, 11 newly installed, 0 to remove and 49 not upgraded.
Need to get 4743kB of archives.
After unpacking 24.9MB of additional disk space will be used.
Do you want to continue? [Y/n]
</pre>
<p>Geben Sie <strong>Y</strong> ein, um fortzufahren und alle benötigten Pakete zu installieren. Um Zope zu starten und anzuhalten, wurde ein Installationsscript namens <tt class="docutils literal">zope</tt> im Verzeichnis <tt class="docutils literal">init.d</tt> erstellt. Geben Sie Folgendes ein, um Plone zu starten:</p>
<pre class="literal-block">
/etc/init.d/zope start
</pre>
<p>Das Debian-Installationsprogramm startet Zope auf dem ungewöhnlichen Port 9673. Weil das so ist, wird empfohlen, die Dokumentation zu lesen, die unter <tt class="docutils literal">/usr/share/doc/zope</tt> und <tt class="docutils literal"><span class="pre">/usr/share/doc/zope-cmfplone</span></tt> zu finden ist.</p>
</div>
</div>
<div class="section" id="installation-aus-den-quellen">
<h3>Installation aus den Quellen</h3>
<p>Alternativ zur Benutzung eines Installationsprogramms oder -pakets können Sie die Installation auch aus einem Quellcode-Archiv vornehmen. Wenn Sie mit einer Quellcode-Installation nicht vertraut sind: Sie ist nicht schwierig, aber man muss sich ein wenig mit einfachen Werkzeugen wie <tt class="docutils literal">tar</tt> auskennen. Die folgenden Abschnitte beschreiben, wie man den Quellcode unter Linux installiert.</p>
<p>Diese Art der Installation setzt voraus, dass Sie mit grundlegenden Operationen wie dem Auspacken und Verschieben von Dateien vertraut sind. Es wird auch eine funktionierende Zope-Installation benötigt.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Um Zope zu installieren, lesen Sie die Installationsanweisungen zu Zope in der Datei <tt class="docutils literal">doc/INSTALL.txt</tt> Ihres heruntergeladenen Zope-Archivs. Weitere Informationen finden Sie unter <a class="reference external" href="http://zope.org/Documentation/Books/ZopeBook/2_6Edition/InstallingZope.stx">http://zope.org/Documentation/Books/ZopeBook/2_6Edition/InstallingZope.stx</a>).</p>
</div>
<p>Führen Sie folgende Schritte aus, um Plone zu installieren:</p>
<ol class="arabic simple">
<li>Laden Sie Plone 2 von <a class="reference external" href="http://www.plone.org/download">http://www.plone.org/download</a> herunter, und wählen Sie die Quellcode-Datei.</li>
<li>Packen Sie das Archiv mit <tt class="docutils literal">tar xzf CMFPlone2.0.tar.gz</tt> aus.</li>
<li>Sie werden feststellen, dass ein Verzeichnis namens <tt class="docutils literal"><span class="pre">CMFPlone-xxx</span></tt> erstellt wurde, wobei <tt class="docutils literal">xxx</tt> die Version ist (z.B. <tt class="docutils literal"><span class="pre">CMFPlone-2.0</span></tt>).</li>
<li>Bewegen Sie den Inhalt dieses Verzeichnisses in das Produktverzeichnis Ihrer Zope-Installation. Wenn das Zope-Produktverzeichnis z.B. <tt class="docutils literal">/var/zope</tt> ist, dann führen Sie Folgendes aus: <tt class="docutils literal">mv CMFPlone2.0/ /var/zope/Products</tt></li>
</ol>
<p>Nach dem Ende der Installation starten Sie Zope neu. Anschließend greifen Sie auf Zope zu, indem Sie in einem Browser <a class="reference external" href="http://localhost:8080/manage">http://localhost:8080/manage</a> eingeben. Dafür benötigen Sie einen Benutzernamen und ein Passwort (z.B. jene, die Sie bei der Zope-Installation eingegeben haben).</p>
<p>Im ZMI gibt es in der oberen rechten Ecke eine Dropdown-Liste für Produkte, die Sie hinzufügen können. Überprüfen Sie, dass eines davon Plone Site ist. Wenn das der Fall ist, dann ist Ihre Installation vollständig (siehe Abbildung 2.12).</p>
<a class="reference external image-reference" href="img/02-12.png/image_view_fullscreen"><img alt="img/02-12.png" class="original" src="img/02-12.png" /></a>
<p>Abbildung 2.12. Der Plone-Site-Eintrag in der Dropdown-Liste</p>
<div class="section" id="installation-aus-cvs">
<h4>Installation aus CVS</h4>
<p>Der Zugriff per CVS (Concurrent Versioning System) wird nur erfahrenen Benutzern und Entwicklern empfohlen. Informationen zum Zugriff per CVS finden Sie unter <a class="reference external" href="http://ww.plone.org/development/cvs">http://ww.plone.org/development/cvs</a>. Der aktuelle CVS-Checkout-Befehl lautet wie folgt:</p>
<pre class="literal-block">
cvs -d:pserver:anonymous&#64;cvs.sf.net:/cvsroot/plone login
cvs -d:pserver:anonymous&#64;cvs.sf.net:/cvsroot/plone co CMFPlone
</pre>
<p>Plone 2 bringt eine ganze Reihe von Abhängigkeiten mit sich (z.B. DCWorkflow, Formulator, Group User Folder usw.), die nicht im Plone-CVS enthalten sind. Das heißt, die Benutzer müssen diese Abhängigkeiten selbst auflösen. Wenn Sie Plone starten, gibt es eventuelle Fehler aus, die sich auf nicht gefundene Pakete beziehen, z.B.:</p>
<pre class="literal-block">
2003-11-21T12:23:11 ERROR(200) Plone Dependency
CMFActionIcons not found.  Please download it from http://cvs.zope.org/Products/
</pre>
</div>
<div class="section" id="hinzufugen-einer-plone-site">
<h4>Hinzufügen einer Plone-Site</h4>
<p>Nachdem Sie Plone aus seinen Quellen installiert haben, müssen Sie eine Plone-Instanz erstellen. Dazu müssen Sie sich beim ZMI anmelden und dort die Plone-Site hinzufügen. Dorthin gelangen Sie, wenn Sie die URL (Uniform Resource Locator) für die Management-Schnittstelle eingeben, normalerweise <a class="reference external" href="http://localhost:8080/manage">http://localhost:8080/manage</a> (dieser Port variiert je nach Installation). Für den Zugriff auf den ZMI benötigen Sie einen Benutzernamen und ein Passwort eines Managers, die bei der Zope-Installation angelegt werden.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Falls Sie das Plone-Passwort vergessen haben, das bei der Installation erstellt wurde, keine Panik! Sie können ein neues erstellen; lesen Sie dazu Kapitel 9.</p>
</div>
<p>Alle Objekte fügen Sie mit der Dropdown-Liste in der oberen rechten Ecke hinzu, die in Abbildung 2.12 zu sehen ist. Scrollen Sie die Liste runter, bis Sie Plone Site finden, und klicken Sie auf <em>Hinzufügen</em>.</p>
<p>Nach Auswahl des Eintrags Plone Site erscheint ein Formular, in dem weitere Angaben verlangt werden (siehe Abbildung 2.13):</p>
<ul class="simple">
<li><strong>Id</strong>: Dies ist die eindeutige ID der Plone-Site (geben Sie z.B. <strong>Plone</strong> oder <strong>Site</strong> ein).</li>
<li><strong>Title</strong>: Dies ist der Titel der Plone-Site (geben Sie z.B. <strong>Plone-Demo-Site</strong> ein).</li>
<li><strong>Membership source</strong>: Belassen Sie dies vorläufig beim Vorgabewert, [[Create a New User Folder in the Portal]. Damit können Sie eine Benutzeridentifikation außerhalb des Portals verwenden (siehe Kapitel 9).</li>
<li><strong>Description</strong>: Dies ist eine Beschreibung des Portals, die Mitglieder in E-Mails sehen können (geben Sie z.B. <strong>Das ist eine Demo-Site für Plone</strong> ein). Machen Sie sich jetzt nicht viele Gedanken darüber. Sie können sie später in den Portal-Eigenschaften ändern.</li>
</ul>
<a class="reference external image-reference" href="img/02-13.png/image_view_fullscreen"><img alt="img/02-13.png" class="original" src="img/02-13.png" /></a>
<p>Abbildung 2.13. Hinzufügen einer Plone-Site</p>
<p>Nach einem Klick auf <em>Add Plone Site</em> wird eine Plone-Site erstellt. Auf langsamen Rechnern kann das ein bis zwei Minuten in Anspruch nehmen, da viele Daten dabei verarbeitet werden. Dieser Bildschirm führt Sie dann zur Willkommensseite von Plone weiter.</p>
</div>
</div>
<div class="section" id="konfigurieren-des-webservers">
<h3>Konfigurieren des Webservers</h3>
<p>Nach der Installation von Plone sollten Sie die Plone-Site so konfigurieren, dass sie unter einem anderen Port läuft, über FTP verfügt, Logdaten in eine andere Datei schreibt usw. Von diesen grundlegenden Einrichtungsmöglichkeiten handelt dieser Abschnitt. Beachten Sie, dass Sie nicht die Plone-Sites selbst konfigurieren, sondern den darunter liegenden Webserver.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Falls Sie das Windows-Installationsprogramm benutzt haben, erfolgt der Großteil dieser Konfiguration mit einem Programm, das eine nette Benutzerschnittstelle hat; siehe den Abschnitt &quot;Server-Konfiguration unter Windows&quot; weiter oben in diesem Kapitel.</p>
</div>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Falls Sie das Installationsprogramm unter Mac OS X oder Windows verwendet haben, werden Sie eine weitere Datei finden (<tt class="docutils literal">plone.conf</tt>), die Port-Definitionen enthält, die in Zopes Hauptkonfigurationsdatei verwendet werden.</p>
</div>
<p>Zope 2.7 erstellt eine Konfigurationsdatei in jeder einzelnen installierten Instanz. Diese Datei enthält die gesamte Konfiguration für diesen Server. Anhang A enthält eine vollständige Liste der Konfigurationsmöglichkeiten. Sie finden die Datei, wenn Sie nach <tt class="docutils literal">zope.conf</tt> im Verzeichnis <tt class="docutils literal">etc</tt> Ihrer Plone-Installation suchen. Manche Installationsprogramme (z.B. bei Mac OS X und Windows) erstellen eine zweite Konfigurationsdatei namens <tt class="docutils literal">plone.conf</tt>, die Plone-spezifische Konfigurationen enthält. Falls Ihre Installation eine Datei <tt class="docutils literal">plone.conf</tt> enthält, dann nehmen Sie dort Änderungen an der Konfiguration vor. Sie werden dann in der Hauptkonfigurationsdatei importiert.</p>
<p>Die Konfigurationsdatei ist extrem ausführlich und enthält eine große Menge hilfreicher Kommentare und Beispiele. Wenn Sie mit Unix-Konfigurationsdateien z.B. für Apache vertraut sind, wird Ihnen die Zope-Konfigurationsdatei bekannt vorkommen. Um die Konfiguration von Zope zu ändern öffnen Sie einen Texteditor und ändern die Dateien nach Bedarf. Danach müssen Sie Zope neu starten.</p>
<p>Man kann Plone 2.0 mit einer Zope-Version kleiner als 2.7 betreiben, allerdings bietet Zope 2.7 eine erhöhte Stabilität und neue Eigenschaften wie eine einfachere Konfiguration. Wenn Sie eine frühere Zope-Version als 2.7 verwenden, müssen Sie in der Dokumentation nachlesen, wie Sie die Konfiguration ändern.</p>
<div class="section" id="id1">
<h4>Ändern der Ports</h4>
<p>Einen Port ändern Sie dadurch, dass Sie eine Adresszeile dafür hinzufügen. Um Plone z.B. auf Port 80 statt auf dem Vorgabeport 8080 zu betreiben, ändern Sie die entsprechende Zeile in <tt class="docutils literal">zope.conf</tt>:</p>
<pre class="literal-block">
&lt;http-server&gt;
  # valid keys are &quot;address&quot; and &quot;force-connection-close&quot;
  address 8080
  # force-connection-close on
&lt;/http-server&gt;
</pre>
<p>auf Folgendes:</p>
<pre class="literal-block">
&lt;http-server&gt;
  # valid keys are &quot;address&quot; and &quot;force-connection-close&quot;
  address 80
  # force-connection-close on
&lt;/http-server&gt;
</pre>
<p>Wenn Sie das Windows- oder Mac OS X-Installationsprogramm verwendet haben, dann finden Sie diese Port-Definitionen in <tt class="docutils literal">plone.conf</tt>. Diese Werte werden dann in die Hauptkonfigurationsdatei importiert. Um also den Port auf einem Mac zu ändern, ändern Sie in <tt class="docutils literal">plone.conf</tt> diesen Teil:</p>
<pre class="literal-block">
## PLONE_WEBSERVER_PORT
## --------------------
## This is the port you will access your Plone site from.  Set this to a port
## number above 1024 not used for any other server on your computer.
%define PLONE_WEBSERVER_PORT 8080
</pre>
<p>wie folgt:</p>
<pre class="literal-block">
%define PLONE_WEBSERVER_PORT 80
</pre>
</div>
<div class="section" id="verwenden-des-debug-modus">
<h4>Verwenden des Debug-Modus</h4>
<p>In Zope 2.7 ist der Debug-Modus standardmäßig eingeschaltet. Beachten Sie aber, dass Plone im Debug-Modus wesentlich langsamer läuft, und zwar etwa 10- bis 20-mal langsamer. Fügen Sie folgende Zeile in der Konfigurationsdatei hinzu, um den Debug-Modus abzuschalten:</p>
<pre class="literal-block">
debug-mode off
</pre>
<p>Um das erstmalige Benutzererlebnis für Windows-Anwender eindrucksvoller zu machen (der Debug-Modus verlagsamt Plone unter Windows noch mehr als unter Linux), wird hier Zope bereits mit abgeschaltetem Debug-Modus ausgeliefert. Wenn Sie für eine laufende Plone-Site wissen möchten, ob der Debug-Modus eingeschaltet ist, gehen Sie im ZMI zu <em>portal_migration</em> und sehen sich die dort aufgelisteten Variablen an.</p>
</div>
<div class="section" id="verwenden-von-logs">
<h4>Verwenden von Logs</h4>
<p>In Plone gibt es standardmäßig zwei Logmechanismen: einen für Zugriffe, mit dem man Site-Statistiken produzieren kann, und einen für Ereignisse, der Debug-Informationen über Plone-Produkte enthält. Für Plone-Fehler und -Meldungen ist der Ereignis-Log zuständig. Die Standardkonfiguration sieht wie folgt aus:</p>
<pre class="literal-block">
&lt;eventlog&gt;
  level all
  &lt;logfile&gt;
    path $INSTANCE/log/event.log
    level INFO
  &lt;/logfile&gt;
&lt;/eventlog&gt;

&lt;logger access&gt;
  level WARN
  &lt;logfile&gt;
    path $INSTANCE/log/Z2.log
    format %(message)s
  &lt;/logfile&gt;
&lt;/logger&gt;
</pre>
<p>Hier können Sie den Pfad zur Datei ändern, indem Sie eine neue Datei angeben. Die geloggten Werte basieren auf einem Level, der mit den Fehlermeldungen mitgeschickt wird. Ernstere Fehler werden mit höherem Level geschickt. Standardmäßig werden nur Informationen und die vorherige Meldung ans Log geschickt, aber dieser Wert könnte einer der folgenden sein: <tt class="docutils literal">CRITICAL</tt>, <tt class="docutils literal">ERROR</tt>, <tt class="docutils literal">WARN</tt>, <tt class="docutils literal">INFO</tt>, <tt class="docutils literal">DEBUG</tt> und <tt class="docutils literal">ALL</tt>. Wenn Sie nur Fehler loggen möchten, würden Sie <tt class="docutils literal">level INFO</tt> auf <tt class="docutils literal">level ERROR</tt> ändern.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch3.rst">
    <title>3. Inhalte hinzufügen und bearbeiten</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch3.rst</link>
    <description>"Inhalte hinzufügen und bearbeiten" bedeutet eine arge Vereinfachung der vielfältigen Möglichkeiten, die Plone zur Verfügung stellt. Mit Plone lassen sich sehr leicht Webseiten erstellen, die verschiedenste Inhalte und Möglichkeiten bieten. Falls Sie Plone lokal installiert haben, erfahren Sie in diesem Kapitel, wie Plone unmittelbar funktioniert. Wenn Sie es nicht installiert haben, müssen Sie sich aber nicht etwa beeilen, denn dann können Sie Plone unter http://demo.plone.org online ausprobieren.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Inhalte hinzufügen und bearbeiten</h2>
<p>&quot;Inhalte hinzufügen und bearbeiten&quot; bedeutet eine arge Vereinfachung der vielfältigen Möglichkeiten, die Plone zur Verfügung stellt. Mit Plone lassen sich sehr leicht Webseiten erstellen, die verschiedenste Inhalte und Möglichkeiten bieten. Falls Sie Plone lokal installiert haben, erfahren Sie in diesem Kapitel, wie Plone unmittelbar funktioniert. Wenn Sie es nicht installiert haben, müssen Sie sich aber nicht etwa beeilen, denn dann können Sie Plone unter <a class="reference external" href="http://demo.plone.org">http://demo.plone.org</a> online ausprobieren.</p>
<p>Bevor Sie eine Plone-Site ändern oder bearbeiten können, müssen Sie sich auf der Plone-Site erst einmal anmelden. Sofern Sie Plone installiert haben, sollten Sie über den Benutzernamen und das Passwort verfügen, die bei der Installation eingerichtet wurden. Dieser Benutzer hat die Rolle eines Administrators, mit Hilfe derer Sie sich anmelden und beliebige Inhalte ändern können. Die meisten Benutzer einer Plone-Site registrieren sich auf der Site und melden sich über den Mechanismus an, der im Abschnitt &quot;Registrierung auf einer Site&quot; beschrieben wird. Natürlich können Benutzer eine Plone-Site auch anschauen, ohne sich zu registrieren, aber dann können sie keine Inhalte hinzufügen oder ändern.</p>
<p>In diesem Kapitel beschreibe ich schrittweise, was ein Benutzer tun muss, um auf einer Plone-Site Inhalte zu erstellen. Zuerst behandle ich, wie man Mitglied wird und sich anmeldet. Danach beschreibe ich, wie man ein Dokument erstellt und es bearbeitet. Und schließlich zeige ich Ihnen, wie Sie nach diesem Inhalt suchen und ihn veröffentlichen können. Kurz gesagt: Dieses Kapitel beschreibt, wie man Plone benutzt.</p>
<div class="section" id="registrierung-auf-einer-site">
<h3>Registrierung auf einer Site</h3>
<p>Wenn Sie sich auf einer Plone-Site registrieren, erstellen Sie auf dem Server ein Benutzerkonto. Mit diesem Konto erhalten Sie als Mitglied das Recht, Inhalte wie Bilder, Dokumente usw. hinzuzufügen. Um sich auf einer Site zu registrieren, klicken Sie oben rechts auf der Webseite auf den Link <em>Mitglied werden</em> (siehe Abbildung 3.1).</p>
<a class="reference external image-reference" href="img/03-01.png/image_view_fullscreen"><img alt="img/03-01.png" class="original" src="img/03-01.png" /></a>
<p>Abbildung 3-1. Klick auf <em>Mitglied werden</em> in der oberen rechten Ecke der Seite</p>
<p>Damit gelangen Sie zu einem Registrierungsformular, das Sie ausfüllen müssen (siehe Abbildung 3.2). Da dies das erste Plone-Formular ist, dem Sie begegnen, sollten Sie auf Folgendes achten:</p>
<blockquote>
<ul class="simple">
<li>Manche Felder müssen ausgefüllt werden. Auf solche Felder weist ein kleiner roter Punkt neben dem Text hin.</li>
<li>Zu den meisten Feldern gibt eine graue Texthilfestellung unter dem Feldnamen an, was Sie darin eingeben sollen.</li>
</ul>
</blockquote>
<a class="reference external image-reference" href="img/03-02.png/image_view_fullscreen"><img alt="img/03-02.png" class="original" src="img/03-02.png" /></a>
<p>Abbildung 3-2. Die Registrierungsseite</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Da die meisten Plone-Seiten ziemlich groß sind, wurden die Abbildungen in diesem Buch so beschnitten, dass nur die wesentlichen Teile (in diesem Fall das Formular) dargestellt werden und nicht das Plone-Logo oder die Fußzeilen. Diese Elemente sind ebenfalls vorhanden, aber hierbei unwichtig.</p>
</div>
<p>Füllen Sie das Formular aus, indem Sie folgende Angaben in den jeweiligen Feldern machen:</p>
<ul class="simple">
<li><strong>Vor- und Nachname</strong>: Geben Sie Ihren vollständigen Namen ein. Dieses Feld ist optional.</li>
<li><strong>Benutzername</strong>: Geben Sie den gewünschten Benutzernamen ein. Die meisten Leute wählen einen alphanumerischen Wert ohne Leerzeichen, z.B. <tt class="docutils literal">bob</tt> oder <tt class="docutils literal">jane97</tt>. Dieser Benutzername wird auf der gesamten Website als Kürzel für Sie verwendet. Dieses Feld muss ausgefüllt werden.</li>
<li><strong>E-Mail</strong>: Eine gültige E-Mail-Adresse ist ebenfalls notwendig. Damit kann der Site-Administrator Sie kontaktieren und Ihnen ein Passwort schicken. Diese E-Mail-Adresse können Sie später in Ihren Mitgliedervoreinstellungen ändern. Dieses Feld muss ausgefüllt werden.</li>
<li><strong>Passwort</strong> und <strong>Passwort bestätigen</strong>: Dies ist das gewünschte Passwort. Es muss mehr als vier Zeichen haben und darf aus Buchstaben, Zahlen und Unterstrichen (_) bestehen. Die Schreibweise in Passwörtern ist wichtig (d.h. <tt class="docutils literal">EinPasswort</tt> ist etwas anderes als <tt class="docutils literal">einpasswort</tt>). Diese Felder müssen ausgefüllt werden.</li>
<li><strong>Passwort zuschicken?</strong>: Kreuzen Sie dieses Feld an, wenn Ihr Passwort an Ihre angegebene E-Mail-Adresse geschickt werden soll. Dieses Feld ist optional.</li>
</ul>
<p>Wenn Sie das Formular vollständig ausgefüllt haben, klicken Sie auf <em>Registrieren</em>, um Ihre Angaben abzuschicken. Wenn Sie Fehler beim Ausfüllen des Formulars gemacht haben, sehen Sie oben einen Hinweis und eine Hervorhebung in den betroffenen Feldern. In Abbildung 3.3 habe ich ein Passwort eingegeben, aber das Feld <em>Passwort bestätigen</em> nicht ausgefüllt. Dies ist die normale Art, wie Ihnen Fehler in Plone-Formularen angezeigt werden.</p>
<a class="reference external image-reference" href="img/03-03.png/image_view_fullscreen"><img alt="img/03-03.png" class="original" src="img/03-03.png" /></a>
<p>Abbildung 3.3. Fehler in einem Formular</p>
<p>Falls Sie das Formular korrekt ausgefüllt haben, erhalten Sie die Möglichkeit, sich sofort anzumelden. Dazu klicken Sie auf den Button <em>Log in</em>. Dann sehen Sie die Seite in Abbildung 3.4.</p>
<a class="reference external image-reference" href="img/03-04.png/image_view_fullscreen"><img alt="img/03-04.png" class="original" src="img/03-04.png" /></a>
<p>Abbildung 3.4. Nach der Registrierung</p>
<p>Wenn Sie bereits einen Benutzernamen und ein Passwort haben oder zu einer Site kommen, auf der Sie schon registriert sind, können Sie Benutzernamen und Passwort in der linken Spalte der Seite eingeben und auf den Button <em>Log in</em> klicken.</p>
<div class="sidebar">
<p class="first sidebar-title">Cookies aktivieren</p>
<!-- To log into a Plone site, you must have cookies enabled. If you access a Plone site and try to log in with cookies disabled, you'll get a friendly message telling you that cookies must be enabled with a link to more information. To enable cookies, perform the following steps, depending on your browser. -->
<p>Um sich auf einer Plone-Site anzumelden, müssen Sie Cookies aktiviert haben. Wenn Sie versuchen, ohne Cookie-Unterstützung auf eine Plone-Site zuzugreifen, erhalten Sie einen freundlichen Hinweis darauf, dass Sie Cookies einschalten müssen, sowie einen Link auf weitere Informationen. Je nach verwendetem Browser können Sie Cookies wie folgt einschalten:</p>
<!-- Internet Explorer 6.x -->
<!-- 1.      Select Tools > Internet Options. -->
<!-- 2.      Click the Privacy tab at the top of the screen. -->
<!-- 3.      Move the slider to Medium, and click OK. -->
<p><strong>Internet Explorer 6.x</strong></p>
<ol class="arabic simple">
<li>Wählen Sie <em>Tools - Internet Options</em>.</li>
<li>Klicken Sie oben auf dem Bildschirm auf den <em>Privacy</em>-Reiter.</li>
<li>Bewegen Sie den Rollbalken auf <em>Medium</em>, und klicken Sie auf <em>OK</em>.</li>
</ol>
<!-- Internet Explorer 5.x -->
<!-- 1.      Select Tools > Internet Options. -->
<!-- 2.      Click the Security tab at the top of the screen. -->
<!-- 3.      Click Custom Level, and scroll down to the Cookies section. -->
<!-- 4.      Set Allow Per-Session Cookies to Enable, and click OK. -->
<p><strong>Internet Explorer 5.x</strong></p>
<ol class="arabic simple">
<li>Wählen Sie <em>Tools - Internet Options</em>.</li>
<li>Klicken Sie oben auf dem Bildschirm auf den <em>Security</em>-Reiter.</li>
<li>Klicken Sie auf <em>Custom Level</em>, und scrollen Sie bis zum <em>Cookies</em>-Abschnitt hinunter.</li>
<li>Setzen Sie <em>Allow Per-Session Cookies</em> auf <em>Enable</em>, und klicken Sie auf <em>OK</em>.</li>
</ol>
<!-- Internet Explorer 4.x -->
<!-- 1.      Select View > Internet Options. -->
<!-- 2.      Click the Security tab at the top of the screen. -->
<!-- 3.      Click Custom Level, and scroll down to the Cookies section. -->
<!-- 4.      Select Always Accept Cookies or Prompt Before Accepting Cookies, and click OK. -->
<p><strong>Internet Explorer 4.x</strong></p>
<ol class="arabic simple">
<li>Wählen Sie <em>View - Internet Options</em>.</li>
<li>Klicken Sie oben auf dem Bildschirm auf den <em>Security</em>-Reiter.</li>
<li>Klicken Sie auf <em>Custom Level</em>, und scrollen Sie bis zum <em>Cookies</em>-Abschnitt hinunter.</li>
<li>Wählen Sie <em>Always Accept Cookies</em> oder <em>Prompt Before Accepting Cookies</em>, und klicken Sie auf <em>OK</em>.</li>
</ol>
<!-- Mozilla 1.x -->
<!-- 1.      Select Edit > Preferences. -->
<!-- 2.      Find Privacy & Security in the menu on the left. If there?Äôs a plus sign (+) to the left of Privacy & Security, click it. -->
<!-- 3.      Select Cookies under Advanced. -->
<!-- 4.      Select Enable Cookies for the Originating Web Site Only or Enable All Cookies, and click OK. -->
<p><strong>Mozilla 1.x</strong></p>
<ol class="arabic simple">
<li>Wählen Sie <em>Edit - Preferences</em>.</li>
<li>Finden Sie im linken Menü <em>Privacy &amp; Security</em>. Wenn ein Pluszeichen (+) links von <em>Privacy &amp; Security</em> steht, klicken Sie darauf.</li>
<li>Wählen Sie <em>Cookies</em> unter <em>Advanced</em>.</li>
<li>Wählen Sie <em>Enable Cookies for the Originating Web Site Only</em> oder <em>Enable All Cookies</em>, und klicken Sie auf <em>OK</em>.</li>
</ol>
<!-- Opera -->
<!-- 1.      Press F12. -->
<!-- 2.      Click Enable Cookies. -->
<p><strong>Opera</strong></p>
<ol class="arabic simple">
<li>Drücken Sie <strong>F12</strong>.</li>
<li>Klicken Sie auf <em>Enable Cookies</em>.</li>
</ol>
<!-- Netscape Navigator 6.x -->
<!-- 1.      Select Edit > Preferences. -->
<!-- 2.      Find Privacy & Security in the menu on the left. If there?Äôs a triangle pointing to the right next to Privacy & Security, click it. -->
<!-- 3.      Select Cookies under Privacy & Security. -->
<!-- 4.      Select Enable Cookies for the Originating Web Site Only or Enable All Cookies, and click OK. -->
<p><strong>Netscape Navigator 6.x</strong></p>
<ol class="last arabic simple">
<li>Wählen Sie <em>Edit - Preferences</em>.</li>
<li>Finden Sie im linken Menü <em>Privacy &amp; Security</em>. Wenn ein nach rechts zeigendes Dreieck neben <em>Privacy &amp; Security</em> steht, klicken Sie darauf.</li>
<li>Wählen Sie <em>Cookies</em> unter <em>Privacy &amp; Security</em>.</li>
<li>Wählen Sie <em>Enable Cookies for the Originating Web Site Only</em> oder <em>Enable All Cookies</em>, und klicken Sie auf <em>OK</em>.</li>
</ol>
</div>
<p>Falls Sie irgendwann einmal Ihr Passwort vergessen, können Sie es sich an die E-Mail-Adresse schicken lassen, die Sie bei Ihrer Registrierung an der Plone-Site angegeben haben. Damit Ihnen das Passwort per E-Mail geschickt wird, klicken Sie auf den Link <em>Passwort vergessen?</em> in der linken Spalte der Seite. Dann wird das Formular <em>Passwort vergessen?</em> angezeigt, das in Abbildung 3.5 zu sehen ist. Geben Sie Ihren Benutzernamen ein, und Sie erhalten ein Passwort per E-Mail.</p>
<a class="reference external image-reference" href="img/03-05.png/image_view_fullscreen"><img alt="img/03-05.png" class="original" src="img/03-05.png" /></a>
<p>Abbildung 3.5. Anfordern eines vergessenen Passworts</p>
<p>Wenn Sie allerdings keinen Zugriff mehr auf diese E-Mail-Adresse haben oder gar Ihren Benutzernamen vergessen haben, müssen Sie leider einen Site-Administrator kontaktieren. Mit den in Kapitel 9 beschriebenen Techniken kann der Administrator Ihre E-Mail-Adresse ändern und Ihr Benutzerkonto ausfindig machen. Wenn Sie einmal bei einer Plone-Site angemeldet sind, sehen Sie in der oberen rechten Ecke einen Link namens <em>Ausloggen</em>. Wenn Sie Ihre Arbeit beendet haben, ist es ratsam, sich von einer Plone-Site abzumelden, besonders dann, wenn Sie von einem Rechner darauf zugreifen, der sehr wahrscheinlich von anderen Leuten benutzt wird.</p>
</div>
<div class="section" id="ihren-ordner-und-ihre-voreinstellungen-einrichten">
<h3>Ihren Ordner und Ihre Voreinstellungen einrichten</h3>
<p>Nachdem Sie sich angemeldet haben, ändert sich die Mitgliederleiste oben rechts, um die für Sie als Site-Mitglied verfügbaren Optionen darzustellen (siehe Abbildung 3.6).</p>
<a class="reference external image-reference" href="img/03-06.png/image_view_fullscreen"><img alt="img/03-06.png" class="original" src="img/03-06.png" /></a>
<p>Abbildung 3.6. Ihre persönlichen Optionen in der oberen rechten Ecke haben sich verändert.</p>
<p>Eine dieser Optionen besteht in der Einrichtung eines Ordners für jedes Mitglied, das sich bei einer Site registriert. Für diesen Ordner ist eine spezielle Sicherheitsstufe eingestellt, damit nur dieses Mitglied (und Administratoren) Inhalte darin hinzufügen und bearbeiten können. Um auf Ihren persönlichen Ordner zuzugreifen, klicken Sie auf den Link <em>Mein Ordner</em> in der persönlichen Leiste oben rechts auf der Seite. Dort sehen Sie auch einen Link namens <em>Meine Einstellungen</em>. Ein Klick darauf öffnet eine Liste persönlicher Einstellungen. Im Moment gibt es dort zwei Einträge. Sie können Ihr Passwort ändern, oder Sie können zu den persönlichen Voreinstellungen gehen und wesentliche Einstellungen an Ihrer Site ändern.</p>
<p>Mit dem Formular <em>Passwort verändern</em> können Sie, nun ja, Ihr Passwort verändern. Um das Formular auszufüllen, geben Sie zunächst das aktuelle Passwort und dann zweimal das neue ein. Nach dieser Änderung wird das neue Passwort sofort gültig. Sie müssen sich nicht erneut anmelden, aber Sie sollten sich das neue Passwort merken, wenn Sie wiederkommen.</p>
<p>In dem Formular <em>Einzelheiten zum Mitglied</em> können Sie eine Reihe von Voreinstellungen setzen, die die Art und Weise verändern, wie Sie die Site sehen. Diese Voreinstellugnen werden auf dem Server gespeichert, d.h., sie bleiben über die einzelnen Sitzungen hinaus erhalten (siehe Abbildung 3.7).</p>
<a class="reference external image-reference" href="img/03-07.png/image_view_fullscreen"><img alt="img/03-07.png" class="original" src="img/03-07.png" /></a>
<p>Abbildung 3.7. Ändern der Voreinstellungen</p>
<p>Folgende Optionen stehen zur Verfügung:</p>
<ul class="simple">
<li><strong>Vor- und Nachname</strong>: Dies ist der vollständige Name, den Sie bei Ihrer Registrierung auf der Site angegeben haben.</li>
<li><strong>E-Mail</strong>: Dies ist die mit Ihrer Mitgliedschaft verbundene E-Mail-Adresse, die an einer Reihe von Stellen in einer Plone-Site verwendet wird. Insbesondere ist es die Adresse, an die das System ein verlorenes oder vergessenes Passwort schickt.</li>
<li><strong>Texteditor</strong>: Beim Bearbeiten komplexer Inhalte benötigen Sie evtl. die Hilfe eines Editors. Falls Ihr Site-Administrator einen Editor verfügbar gemacht hat, können Sie ihn hier auswählen. Er wird dann benutzt, wenn Sie auf den Reiter <em>Bearbeiten eines Objekts</em> klicken. Wenn Sie unsicher sind, lassen Sie die Voreinstellung unverändert.</li>
<li><strong>Auflistungsstatus</strong>: Diese Eigenschaft gibt an, ob Ihr Profil unter dem Reiter <em>Mitglieder</em> und dann erscheint, wenn jemand die Mitgliederliste durchsucht.</li>
<li><strong>Zeige Namen an</strong>: Objekte haben eine ID bzw. eine Kurzname-Eigenschaft, der für die interne Darstellung des Inhaltsobjekts verwendet wird. Sie erscheint auch in der Webadresse dieses Elements und in dessen URL (Uniform Resource Locator). Standardmäßig sehen sie ungefähr aus wie z.B. <tt class="docutils literal"><span class="pre">News_Item.2002-11-16.4102</span></tt>, aber Sie könnten sie auch viel einfacher machen, z.B. <tt class="docutils literal">november_news</tt>, indem Sie einfach den Wert des Kurznamens ändern.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p>Wenn Sie den Namen eines Objekts ändern, wird alles ungültig, was auf den alten Namen verweist. Das führt dazu, dass die Seite nicht gefunden wird. Am besten ändern Sie den Namen nicht mehr, nachdem Sie ein Objekt zur Überprüfung einreichen bzw. von anderswo darauf verweisen. Aus diesem Grund empfehle ich, diese Option auf <em>Nein</em> zu setzen.</p>
<p class="last">Sollten Sie diese Option dennoch aktivieren, so empfiehlt sich, keine Umlaute, Sonderzeichen oder etwa Leerzeichen für den Namen zu verwenden, da dieser, wie bereits erwähnt, in der URL des Objekts vorkommt. Auch wenn Ihr Browser damit umgehen kann, sollten Sie daran denken, dass andere Browser dies evtl. nicht unterstützen. Im Titel eines Objekts sind Ihrer Kreativität dagegen keine Grenzen gesetzt.</p>
</div>
<ul class="simple">
<li><strong>Porträt</strong>: In großen Organisationen ebenso wie auf Community-Websites ist es hilfreich, Bilder von anderen Mitgliedern sehen zu können. Mit dem <em>Porträt</em>-Feld können Sie ein Bild von sich selbst hochladen. Das Bild sollte 75 mal 100 Pixel groß sein.</li>
</ul>
<p>Nachdem Sie die gewünschten Änderungen vorgenommen haben, klicken Sie auf den Button <em>Speichern</em>. Nun, da Sie angemeldet sind, ist es Zeit, Inhalte hinzuzufügen und zu bearbeiten.</p>
</div>
<div class="section" id="dokumente-hinzufugen-und-bearbeiten">
<h3>Dokumente hinzufügen und bearbeiten</h3>
<p>Wie schon gesagt, verfügen Sie nun, da Sie Mitglied auf der Site sind, über einen Ordner, in dem Sie Inhalte speichern können. Natürlich können Sie Inhalte in allen Ordnern erstellen, in denen Sie vom Administrator die entsprechenden Rechte erhalten haben, aber standardmäßig kann jeder Benutzer etwas in seinem eigenen Ordner speichern.</p>
<p>Sie können unterschiedliche Arten von Inhalt erstellen und diesen auf verschiedene Weise bearbeiten und anschauen. Aus diesem Grund werden diese verschiedenen Inhaltstypen in Plone unterschiedlich bezeichnet, z.B. Bilder, Links, Dokumente usw. Von sich aus bietet Plone folgende Inhaltstypen:</p>
<ul class="simple">
<li><strong>Dokument</strong>: Dieses Element stellt irgendeine statische Information für den Benutzer dar. Dies ist der am häufigsten erstellte Inhaltstyp und entspricht noch am ehesten einer typischen Webseite.</li>
<li><strong>Nachricht</strong>: Dies ist ein Dokument, das unter dem <em>Nachrichten</em>-Reiter erscheint, z.B. eine Pressemitteilung.</li>
<li><strong>Link</strong>: Dies ist ein Verweis auf ein anderes Element, das intern oder extern zu einer anderen Website sein kann.</li>
<li><strong>Bild</strong>: Dies ist ein Bild, z.B. eine GIF- oder JPEG-Datei.</li>
<li><strong>Termin</strong>: Dies ist ein kommender Termin, z.B. eine Besprechung, Konferenz, oder etwas anderes.</li>
<li><strong>Ordner</strong>: Dies entspricht einem Ordner auf einer Festplatte. Es ist ein Ort für Inhalte, die man später leicht wiederfinden kann.</li>
<li><strong>Thema</strong>: Dies ist eine Gruppierung anderer Inhalte. Im Grunde genommen ist es ein gespeichertes Suchkriterium, das Sie später wiederverwenden können. Nur privilegierte Benutzer können Themen erstellen.</li>
<li><strong>Datei</strong>: Dies ist ein weiteres Stück Inhalt, z.B. ein Film, ein Sound-Clip, eine Textdatei, ein Spreadsheet, eine komprimierte Datei oder irgendetwas anderes, was Sie hochladen möchten.</li>
</ul>
<p>Ich werde die Liste all dieser Elemente durchgehen, wobei ich das Dokument als Beispiel nehmen werde, um zu zeigen, wie man Dokumente einfach und schnell erstellt und bearbeitet. Mit Hilfe dieser einfachen Inhaltstypen werde ich zeigen, wie man im Browser eine dynamische Site baut, ohne das Geringste programmieren zu müssen.</p>
<p>Tatsächlich verfügen Sie in Plone über viele Möglichkeiten, Inhalte zu erstellen und zu bearbeiten, nicht nur den Webbrowser. Der Zugang über FTP, WebDAV sowie über Scripts ist ebenfalls möglich. In Kapitel 10 beschreibe ich, wie man das jeweils einrichtet. Bis dahin behandle ich vorläufig nur die Browser-Schnittstelle. In den Kapiteln 11 bis 13 beschreibe ich, wie man einen neuen speziellen Inhaltstyp erstellt, den Sie auf die Bedürfnisse einer speziellen Site zuschneiden können.</p>
<div class="section" id="was-sind-dokument-inhaltstypen">
<h4>Was sind Dokument-Inhaltstypen?</h4>
<p>Anstatt für alle verschiedenen verfügbaren Inhaltstypen detailliert zu beschreiben, wie sie erstellt und bearbeitet werden, behandle ich die Erstellung eines Typs, nämlich eines Dokuments, im Detail. Nach der Erstellung und Bearbeitung einiger solcher Dokumente sollte Ihnen das Prinzip ihrer Erstellung in Fleisch und Blut übergegangen sein, und Sie werden ohne weiteres andere Inhalte erstellen können.</p>
<p>Ein <em>Dokument</em> ist eine Seite mit einem Inhalt, normalerweise ein eigenständiger Text. Zwar kann man auf jedes in Plone erstellte Element mit einer Webseite zugreifen, und wenn Sie an einen Inhaltstyp für eine Webseite denken, dann ist dies genau ein solcher. Die Standard-Homepage einer Plone-Site, die Sie bereits gesehen haben - die berühmte Plone-Willkommensseite - ist ein Beispiel für ein Dokument (siehe Abbildung 3.8).</p>
<a class="reference external image-reference" href="img/03-08.png/image_view_fullscreen"><img alt="img/03-08.png" class="original" src="img/03-08.png" /></a>
<p>Abbildung 3.8. Willkommen in Plone, ein einfaches Dokument</p>
</div>
<div class="section" id="erstellen-eines-dokuments">
<h4>Erstellen eines Dokuments</h4>
<p>Mit einem Webbrowser können Sie auf zweierlei Weise beliebige Inhalte erstellen. Zuerst müssen Sie angemeldet sein, da nur solche Benutzer Inhalte erstellen dürfen. Klicken Sie dann auf den Link <em>Mein Ordner</em> in der Navigationsleiste oben rechts. Dadurch gelangen Sie zu Ihrem eigenen Ordner, einem Bereich, den Sie selbst kontrollieren. Wenn Sie Inhalte in einem Ordner erstellen dürfen, dann erscheint dieser Ordner mit einem grünen Rahmen am oberen Rand (siehe Abbildung 3.9).</p>
<a class="reference external image-reference" href="img/03-09.png/image_view_fullscreen"><img alt="img/03-09.png" class="original" src="img/03-09.png" /></a>
<p>Abbildung 3.9. Mein Inhalt</p>
<p>Falls der grüne Rahmen nicht zu sehen ist, können Sie keine Inhalte hinzufügen. Dieser Rahmen enthält die Aktionen, die Sie an dieser Stelle ausführen dürfen. In Abbildung 3.9 sehen Sie, dass die Seite den Inhalt des Ordners anzeigt, weil das der gewählte Reiter ist. Es sind auch andere Reiter zu sehen wie <em>Anzeigen</em>, <em>Zugriffsrechte</em> und <em>Eigenschaften</em>, die weitergehende Möglichkeiten bieten. In der oberen rechten Ecke des grünen Rahmens sehen Sie die zwei Dropdown-Menüs <em>Neuen Artikel hinzufügen</em> und <em>Status</em>. Klicken Sie auf das erste Menü, um die Liste von Elementen darin zu sehen (siehe Abbildung 3.10).</p>
<a class="reference external image-reference" href="img/03-10.png/image_view_fullscreen"><img alt="img/03-10.png" class="original" src="img/03-10.png" /></a>
<p>Abbildung 3.10. Erstellen eines Dokuments aus dem grünen Dropdown-Menü</p>
<p>Wählen Sie <em>Dokument</em>, um ein neues Dokument zu erstellen. Alternativ dazu sehen Sie im Rumpf der Seite eine weitere Dropdown-Box namens <em>Neuen Artikel hinzufügen</em>. Klicken Sie auch hier auf den Pfeil nach unten, um eine Liste von Elementen zu erhalten, und wählen Sie dann das gewünschte Element (siehe Abbildung 3.11).</p>
<a class="reference external image-reference" href="img/03-11.png/image_view_fullscreen"><img alt="img/03-11.png" class="original" src="img/03-11.png" /></a>
<p>Abbildung 3.11. Erstellen eines Dokuments aus dem <em>Inhalte</em>-Menü des Hauptordners</p>
<p>Es ist sehr praktisch, die Liste unter <em>Neuen Artikel hinzufügen</em> zu benutzen, da sie fast immer verfügbar ist.</p>
<div class="admonition-achtung admonition">
<p class="first admonition-title">Achtung</p>
<p class="last">Falls Sie mit Zope vertraut sind, sollten Sie wirklich nie, nie, nie Inhalte aus dem ZMI (Zope Management Interface) heraus hinzufügen. Je nachdem, wie Sie Plone installiert haben, haben Sie das ZMI evtl. bereits gesehen und für die Anpassung und Entwicklung von Plone übers Web benutzt. Beim Erstellen von Inhalten mit dem ZMI werden jedoch Inhaltselemente generiert, die für Plone unvollständig sind und in Plone demnach nicht korrekt funktionieren.</p>
</div>
<div class="sidebar">
<p class="first sidebar-title">Wo sollen Dokumente erstellt werden?</p>
<p>Zu Beginn erstellt man Dokumente am einfachsten im Benutzerordner eines Site-Mitglieds, der über den Link <em>Mein Ordner</em> verfügbar ist. Das ist zwar nützlich, aber wahrscheinlich nicht der beste Ansatz für eine langfristige Lösung. Insbesondere werden dabei lange URLs erzeugt, z.B. <tt class="docutils literal"><span class="pre">/Members/andy/Docum...</span></tt>. Es führt auch dazu, dass Ihr Inhalt in der Navigationsleiste nicht richtig erscheint.</p>
<p class="last">Wie Sie später noch sehen werden, gibt es hierfür eine Reihe von Lösungen. Meistens erzeugt man einen Ordner und gibt gewissen Benutzern das Recht, darauf zuzugreifen. Dieser Ordner kann z.B. <tt class="docutils literal">Help</tt> oder <tt class="docutils literal">News</tt> heißen. Im Abschnitt &quot;Ordner benutzen&quot;, der weiter unten folgt, wird die Erstellung von Ordnern besprochen, und Kapitel 9 behandelt Arbeitsbereiche und Sicherheit für Gruppen.</p>
</div>
</div>
<div class="section" id="bearbeiten-eines-dokuments">
<h4>Bearbeiten eines Dokuments</h4>
<p>Nach einem Klick zur Erstellung eines Dokuments gelangen Sie auf die Seite <em>Dokument bearbeiten</em> mit einem Hinweis darauf, dass das Dokument erzeugt wurde. Wenn das nicht passiert, können Sie auf ein Dokument und dann auf den Reiter <em>Bearbeiten</em> klicken. Wieder sehen Sie, dass der Reiter <em>Bearbeiten</em> grün hinterlegt wird (siehe Abbildung 3.12).</p>
<a class="reference external image-reference" href="img/03-12.png/image_view_fullscreen"><img alt="img/03-12.png" class="original" src="img/03-12.png" /></a>
<p>Abbildung 3.12. Bearbeiten eines Dokuments</p>
<p>Nun können Sie das Dokument in Ihrem Webbrowser mit dem vorhandenen Formular bearbeiten. Wenn Sie sich die URL in der Adressleiste Ihres Browsers anschauen, bemerken Sie, dass für Sie ein Kurzname für das Objekt erzeugt wurde, der so ähnlich wie <tt class="docutils literal"><span class="pre">Document.2003-12-29.43787</span></tt> aussieht. Folgende Liste führt die Felder und ihre Bedeutung auf:</p>
<ul class="simple">
<li><strong>Kurzname</strong>: Der Kurzname wird ein Teil der Dokument-URL, d.h., Sie sollten ihn kurz und selbsterklärend halten, am besten ohne Leerzeichen. Bei Beachtung dieser Regeln lassen sich URLs leichter lesen. Benutzen Sie z.B. etwas wie <tt class="docutils literal"><span class="pre">audit-report-2003</span></tt>. Wenn Sie keinen Namen angeben, erzeugt Plone einen für Sie.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Dieses Feld erscheint nicht, wenn Sie auf der Seite mit Ihren Voreinstellungen <em>Nein</em> für die Kurznamen gewählt haben.</p>
</div>
<ul class="simple">
<li><strong>Titel</strong>: Dies ist der Titel des Elements, der überall auf der Site angezeigt wird (z.B. oben auf der Seite, in der Suchschnittstelle, im Browsertitel usw.). Dieses Feld muss ausgefüllt werden.</li>
<li><strong>Beschreibung</strong>: Dies ist eine kurze Erklärung zum Dokument, normalerweise etwa 20 Worte als Einleitung zum Dokument und als Aufmacher für den Rest des Dokuments. Das ist bei Seiten nützlich, die eine Zusammenfassung des Dokuments anzeigen, z.B. bei Suchergebnissen und Ordnerinhalten.</li>
<li><strong>Haupttext</strong>: Dies ist der Rumpf des Dokuments. Das Format des Inhalts wird mit dem <em>Format</em>-Feld eingestellt (wird anschließend beschrieben).</li>
<li><strong>Format</strong>: Für das Format des Haupttextes haben Sie drei Möglichkeiten: <em>Strukturierter Text</em>, <em>HTML</em> und <em>Einfacher Text</em>. Diese Textarten werden im Abschnitt &quot;Wahl eines Textformats&quot; beschrieben. Wenn Sie sich nicht sicher sind, lassen Sie das Feld so, wie es ist, und geben Sie den Haupttext ganz normal ein.</li>
<li><strong>Inhalt hochladen</strong>: Falls Ihr Dokument als Datei auf Ihrem Rechner vorliegt, können Sie es hochladen, anstatt den Inhalt ins <em>Haupttext</em>-Feld einzutippen. Benutzen Sie den Button <em>Datei auswählen</em> unten auf der Seite, um eine Datei auszuwählen. Der Inhalt einer hochgeladenen Datei <em>ersetzt</em> den gesamten Inhalt im <em>Haupttext</em>-Feld.</li>
</ul>
<p>Wenn Sie mit der Bearbeitung Ihres Dokuments fertig sind, klicken Sie auf den Button <em>Speichern</em>, um Ihre Änderungen zu bestätigen. Sie gelangen dann zu dem Reiter <em>Anzeigen</em>, wo Sie sehen können, wie das Dokument für die Benutzer aussehen wird (siehe Abbildung 3.13). Um es weiterzubearbeiten, klicken Sie auf den Reiter <em>Bearbeiten</em>.</p>
<a class="reference external image-reference" href="img/03-13.png/image_view_fullscreen"><img alt="img/03-13.png" class="original" src="img/03-13.png" /></a>
<p>Abbildung 3.13. Beim Speichern des Inhalts gelangen Sie zum Reiter <em>Anzeigen</em>.</p>
<p>Wenn Sie in dem <em>Bearbeiten</em>-Formular fehlerhafte Eingaben machen, kommen Sie beim Abspeichern zur <em>Bearbeiten</em>-Seite zurück, wo die Fehler dann hervorgehoben sind. In dem Moment sind Ihre Änderungen nicht wirksam, d.h. Sie müssen die Fehler korrigieren und auf <em>Speichern</em> klicken, damit die Änderungen festgehalten werden. Der in Abbildung 3.13 gezeigte Reiter <em>Anzeigen</em> zeigt das erzeugte Dokument an. Wie Sie sehen, werden Titel, Beschreibung und Inhalt in jeweils anderen Stilen angezeigt. Unten auf der Seite ist eine Fußzeile mit Angaben zum Autor des Dokuments sowie dem Datum, an dem das Dokument erzeugt wurde.</p>
<p>Sie werden bemerken, dass Sie dann, wenn Sie zum Inhalt von Ordnern zurückgehen, nachdem Sie Ihre Änderungen gespeichert haben, zwei Dokumente in Ihrem Ordner sehen werden: das vorhandene, das für Sie erzeugt wurde, und das neue, das Sie gerade erstellt haben. Sie können beide Dokumente bearbeiten, indem Sie darauf klicken und somit den Reiter <em>Anzeigen</em> öffnen, von wo aus Sie den Reiter <em>Bearbeiten</em> wählen können.</p>
<div class="sidebar">
<p class="first sidebar-title">Wahl eines Textformats</p>
<!-- As mentioned previously, you can edit the document content in at least three formats: structured text, HTML, and plain text. This rather confusing state of affairs is brought about by trying to produce easy systems for users to write rich marked-up content in plain text without having to use fancy editors. -->
<p>Wie schon erwähnt wurde, können Sie den Dokumentinhalt in mindestens drei Formaten bearbeiten: <em>strukturierter Text</em>, <em>HTML</em> und <em>einfacher Text</em>. Dieser etwas verwirrende Umstand kommt daher, dass man versucht, einfache Systeme zu produzieren, mit denen die Benutzer angereicherte, ausgezeichnete Inhalte in Form von einfachem Text schreiben können, ohne spezielle Editoren zu verwenden.</p>
<!-- Unfortunately, in most cases, this really doesn't work; training is required to understand the formatting. Structured text requires quite a bit of understanding in itself because it has a frustrating syntax and doesn't internationalize well. If I had to pick one format that I'd recommend over all the others, I'd pick HTML because it's widely understood, and you can use What You See Is What You Get (WYSIWYG) editors such as Epoz to produce it. -->
<p>In den meisten Fällen funktioniert das aber leider nicht, und man benötigt einiges an Training, um die Formatierung zu begreifen. Strukturierter Text verlangt für sich genommen schon einiges an Verständnis, weil er eine frustrierende Syntax verwendet und Mängel bei fremdsprachigen Texten aufweist. Wenn ich ein Format allen anderen vorziehen müsste, dann wäre das HTML, weil es von vielen verstanden wird and weil Sie es mit WYSIWYG-Editoren (What You See Is What You Get) wie Epoz generieren können.</p>
<p><strong>HTML</strong></p>
<!-- HTML is the most standard format; if a document is entered as HTML, it will be rendered in the same format. This HTML shouldn't be a complete page but rather a snippet. For example: -->
<!-- :: -->
<!-- <p>Here is a sample in <i>HTML</i> for a demonstration.</p> -->
<p>HTML ist das am weitesten standardisierte Format. Wenn ein Dokument in HTML eingegeben wird, wird es im gleichen Format dargestellt. Dieses HTML sollte keine komplette Seite sein, sondern ein Ausschnitt. Beispiel:</p>
<pre class="literal-block">
&lt;p&gt;Hier ist ein Beispiel in &lt;i&gt;HTML&lt;/i&gt; als Demonstration.&lt;/p&gt;
</pre>
<!-- Ideally the HTML should also be valid Extensible HTML (XHTML) to comply with the rest of the Plone system; if it's not, your pages don't comply with Web standards. Entering text as XHTML isn't for the faint of heart, so in Chapter 9, you'll see how into integrate rich-editing tools into Plone that allow users to easily write content in XHTML. The following screen shot shows Plone using Epoz so users don't have to understand HTML: -->
<p>Idealerweise sollte das HTML auch gültiges XHTML (Extensible HTML) sein, damit es zum Rest des Plone-Systems passt. Ansonsten genügen Ihre Seiten nicht den geltenden Webstandards. Die Eingabe von Text in XHTML ist nichts für Unbedarfte, deswegen werden Sie in Kapitel 9 sehen, wie Sie umfangreiche Editorwerkzeuge in Plone integrieren können, mit denen Benutzer sehr einfach XHTML schreiben können. Die folgende Abbildung zeigt den Einsatz von Epoz in Plone, damit Benutzer kein HTML kennen müssen.</p>
<!-- ***production: please note that I've named sidebar graphics with an 's?Äù*** -->
<a class="reference external image-reference" href="img/03-13a.png/image_view_fullscreen"><img alt="img/03-13a.png" class="original" src="img/03-13a.png" /></a>
<p><strong>Einfacher Text</strong></p>
<!-- Plain text is simple. It does no major conversion or manipulation of the text entered; it's just plain text. The only modification made is that new lines are converted into HTML when rendered so that new lines appear in the Web browser. No other altering happens. For example: -->
<!-- :: -->
<!-- Here is a sample in plain text for a demonstration -->
<p>Einfacher Text ist leicht zu verstehen, denn dabei wird keine große Umwandlung oder Manipulation des eingegebenen Textes vorgenommen. Es ist lediglich einfacher Text. Die einzige Änderung, die vorgenommen wird, ist die, dass Zeilenenden nach HTML umgewandelt werden, damit sie in einem Webbrowser als solche dargestellt werden. Sonst wird nichts verändert. Beispiel:</p>
<pre class="literal-block">
Hier ist ein Beispiel in einfachem Text als Demonstration
</pre>
<p><strong>Strukturierter Text</strong></p>
<!-- Structured text is a system for writing plain-text documents in a particular format, which can then be interpreted in different ways. For example, if a piece of text needs to be highlighted, then it can written as **italics**; this will then be shown as *italics*. This series of rules means that a user can write a page that contains formatting information easily. For a full list of structured text rules and examples, please see Appendix A. The following is a sample of structured text: -->
<!-- :: -->
<!-- Here is a sample in *structured text* for a demonstration -->
<p>Strukturierter Text ist ein System zum Schreiben von Dokumenten mit einfachem Text in einem bestimmten Format, das dann auf verschiedene Weisen interpretiert werden kann. Wenn ein Textteil z.B. hervorgehoben werden soll, dann kann er als <strong>kursiv</strong> geschrieben werden, was dann <em>kursiv</em> angezeigt wird. Mit diesen Regeln kann der Benutzer leicht eine Seite schreiben, die Formatierungsanweisungen enthält. Eine vollständige Liste von Regeln und Beispielen für strukturierten Text finden Sie im Anhang A. Hier ein Beispiel:</p>
<pre class="last literal-block">
Hier ist ein Beispiel in *strukturiertem Text* als Demonstration
</pre>
</div>
</div>
<div class="section" id="dokument-metadaten-setzen">
<h4>Dokument-Metadaten setzen</h4>
<p>Jeder Brocken an Inhalt kann mit einer Reihe von Eigenschaften versehen werden. Diese Eigenschaften werden auch als <em>Metadaten</em> bezeichnet und enthalten Angaben z.B. zu Stichworten, Copyright und beteiligten Personen.</p>
<p>Diese gesamte Menge an Eigenschaften ist optional und wird normalerweise nur dann benutzt, wenn es spezielle Anforderungen an diesen Inhalt gibt, besonders deswegen, weil die Person, die diesen Inhalt sieht, diese Informationen normalerweise nicht zu sehen bekommt. Daher ist der Hauptgrund für die Eingabe solcher Informationen der, Aufgaben wie Suche und Kategorisierung zu erleichern.</p>
<p>Wenn Sie auf den grünen Reiter <em>Eigenschaften</em> klicken, können Sie auf die Eigenschaften eines Objekts zugreifen. Dieses <em>Eigenschaften</em>-Formular verfügt über folgende Felder, die bei allen Inhaltstypen gleich sind:</p>
<ul class="simple">
<li><strong>Diskussion erlauben</strong>: Das ermöglicht, dass das Dokument von Benutzern diskutiert oder kommentiert werden kann, die das Recht dazu haben. Wenn der voreingestellte Wert beibehalten wird, wird für diesen Inhaltstyp die Regelung der gesamten Site übernommen.</li>
<li><strong>Stichworte</strong>: Jedes Element kann über Stichworte verfügen, mit denen eine Gruppierung und Sortierung möglich ist. Ein Artikel über die letzten Ereignisse in der Politik könnte z.B. die Stichworte <em>Politik</em> und <em>Premierminister</em> enthalten. Stichworte sind flexibel, und Sie können beliebige Stichworte aus der angegebenen Liste verwenden. Das Plone-System enthält zu Beginn keine Stichworte, aber die Site-Administratoren können neue Stichworte hinzufügen, damit andere Benutzer sie verwenden können.</li>
<li><strong>Sperrfrist</strong>: Das Sperrfrist-Datum ist der Tag, an dem ein bestimmter Inhalt erstmalig verfügbar sein soll. Dieses Datum können Sie durch Angabe der Werte im Formular oder durch einen Klick auf das Kalender-Icon, das dann einen Kalender öffnet, und Auswahl eines Datums eingeben (siehe Abbildung 3.14).</li>
</ul>
<a class="reference external image-reference" href="img/03-14.png/image_view_fullscreen"><img alt="img/03-14.png" class="original" src="img/03-14.png" /></a>
<p>Abbildung 3.14. Eingabe einer Sperrfrist</p>
<ul class="simple">
<li><strong>Löschdatum</strong>: Das Löschdatum ist der letzte Tag, an dem ein bestimmter Inhalt verfügbar sein soll. Normalerweise werden die Felder für Sperrfrist und Löschdatum leer gelassen.</li>
<li><strong>Format</strong>: Das ist der MIME-Typ (Multipurpose Internet Mail Extensions) des Elements. Der Begriff <em>MIME-Typ</em> bedeutet eine Computer-Definition des Inhaltstyps (z.B. <em>application/msword</em> oder <em>image/jpeg</em>). Dafür ist ein Vorgabewert gesetzt. Wenn Sie bei diesem Feld unsicher sind, ignorieren Sie es einfach.</li>
<li><strong>Sprache</strong>: Dies ist die Sprache, in der das Dokument geschrieben ist. Der Vorgabewert hierfür lautet Englisch.</li>
<li><strong>Urheberrechte</strong>: Dies sind Informationen zu den Urheberrechten des Inhalts. Dieses Feld ist üblicherweise leer.</li>
<li><strong>Beitragende</strong>: Hierzu gehören die Namen von Leuten außerhalb des Plone-Systems, die etwas zu dem Objekt beigetragen haben. Der Name jeder Person sollte in einer eigenen Zeile stehen.</li>
</ul>
<p>Nach der Eingabe der Werte in diesem Formular klicken Sie auf <em>Speichern</em>, um die Änderungen zu bestätigen. Wie gesagt: Sie müssen die Werte unter diesem Reiter normalerweise nicht ändern. Die Änderung dieser Werte hängt normalerweise von den Anforderungen an Ihre Site sowie von der Art von Site ab, die Sie entwickeln.</p>
<div class="sidebar">
<p class="first sidebar-title">Was sind Sperrfristen und Löschdaten?</p>
<p>Jedes Element im Plone-System verfügt über eine Sperrfrist und ein Löschdatum, wenn die bearbeitende Person das wünscht. Beide sind optional, und wenn Sie die Felder leer lassen, dann werden diese Werte nicht gesetzt.</p>
<p>Ein Beispiel für ein Element mit einer Sperrfrist ist eine Pressemitteilung. In einer idealen Welt würde die Mitteilung in Plone konzipiert, vorbereitet und überprüft. Aber nehmen Sie einmal an, die Mitteilung müsste auf der Website um Mitternacht veröffentlicht werden, was aber genau dann ist, wenn Sie eigentlich schlafen möchten. Das ist kein Problem, wenn Sie der Pressemitteilung eine Sperrfrist bis Mitternacht geben. Bis dahin wird sie weder im Kalender noch im Navigationsteil noch bei Suchvorgängen oder auf Seiten sichtbar sein, die eine Suche als Liste unter dem Reiter <em>Nachrichten</em> benutzen. Alle, die von der Pressemitteilung wissen, können direkt auf die Seite zugreifen. Sobald die Sperrfrist vorbei ist, erscheint das Element an allen vorher genannten Orten und wird für alle Welt veröffentlicht.</p>
<p>Ähnlich verhält es sich mit Löschdaten. Wenn Sie ein Sonderangebot haben, das an einem bestimmten Tag verfällt, könnten Sie das Löschdatum auf diesen Tag setzen. Nach diesem Datum wird es nicht mehr im Kalender, in der Navigation, in Suchoperationen usw. erscheinen.</p>
<p class="last">Sperrfristen und Löschdaten ändern nicht den Status des Elements im Workflow (siehe Kapitel 7 für weitere Informationen zum Workflow). Sie ändern lediglich, wo es dargestellt wird. Sie können Sperrfristen und Löschdaten auch im <em>Status</em>-Reiter setzen, über den Sie im nächsten Abschnitt gleich mehr erfahren.</p>
</div>
</div>
<div class="section" id="veroffentlichen-ihres-dokuments">
<h4>Veröffentlichen Ihres Dokuments</h4>
<p>Wenn ein Dokument erstellt wird, wird ihm ein Anfangszustand namens <em>Sichtbar</em> zugewiesen. Inhalte werden standardmäßig nicht automatisch veröffentlicht und aller Welt zugänglich gemacht. Andere können Ihr Dokument sehen, aber es erscheint nicht bei Suchoperationen oder im Navigationsbaum. Dieser Zustand ist nützlich, weil Sie andere Benutzer auf diesen Inhalt hinweisen können, aber da er nicht in der Navigation oder bei Suchvorgängen erscheint, ist er so lange unsichtbar, bis die Benutzer davon erfahren.</p>
<p>Jedes Element in Ihrer Plone-Site hat zu jedem Zeitpunkt einen bestimmten Zustand. Dieser beschreibt dessen Rechte und Rollen innerhalb der Plone-Site. Mit Hilfe von Zuständen kann jedem Element eine andere Sicherheitseinstellung zugewiesen werden. Ein Element kann z.B. manchmal ein oder zwei Wochen brauchen, bis es vorbereitet ist, und es kann dabei in mehreren Versionen vorliegen. Und schließlich möchten Sie den Inhalt veröffentlichen, damit er für alle Benutzer sichtbar wird und in der Navigation und bei der Suche erscheint.</p>
<p>Sie können den Inhalt veröffentlichen, indem Sie das Dropdown-Menü <em>Status</em> in der Hauptnavigation oben rechts verwenden (siehe Abbildung 3.15).</p>
<a class="reference external image-reference" href="img/03-15.png/image_view_fullscreen"><img alt="img/03-15.png" class="original" src="img/03-15.png" /></a>
<p>Abbildung 3.15. <em>Status</em>-Dropdown-Menü</p>
<p>Um ein Objekt zu veröffentlichen, wählen Sie im Dropdown-Menü <em>Einreichen</em>. Standardmäßig können Sie Inhalte nicht direkt veröffentlichen, aber Sie können Sie zur Überprüfung einreichen. Dann erhalten sie den Status <em>Einreichen</em>. Dies ist ein Zwischenzustand zwischen <em>Sichtbar</em> und <em>Veröffentlicht</em>. Er erlaubt die Überprüfung von Inhalten durch Benutzer Ihrer Site, die die Rolle eines Prüfers haben, bevor die Inhalte für alle Welt veröffentlicht werden. Nachdem Sie den Inhalt eingereicht haben, werden Sie feststellen, dass der Inhalt im Zustand <em>Einreichen</em> ist, wenn Sie in die Box in der oberen rechten Ecke schauen. Sie werden auch bemerken, dass es keinen <em>Bearbeiten</em>-Reiter mehr gibt, wie in Abbildung 3.16 zu sehen ist.</p>
<a class="reference external image-reference" href="img/03-16.png/image_view_fullscreen"><img alt="img/03-16.png" class="original" src="img/03-16.png" /></a>
<p>Abbildung 3.16. Der Inhalt wurde eingereicht, der Zustand hat sich auf <em>Offen</em> geändert, und der <em>Bearbeiten</em>-Reiter ist verschwunden.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Wenn Sie als Manager angemeldet sind, werden Sie bemerken, dass es in der Dropdown-Veröffentlichungsliste eine weitere Option namens <em>Veröffentlichen</em> gibt. Damit versetzen Sie Inhalte ohne Zwischenschritt direkt in den Zustand <em>Veröffentlicht</em>.</p>
</div>
<p>In der Workflow-Dropdown-Liste in der oberen rechten Ecke gibt es auch eine Option namens <em>Erweitert</em>, die das Statusformular öffnet, mit dem der Status eines Objekts verändert werden kann. Das Formular ist mit jenem identisch, das beim Klick auf den <em>Status</em>-Reiter erscheint, und enthält folgende Felder:</p>
<ul class="simple">
<li><strong>Sperrfrist</strong>: Dies ist identisch mit dem <em>Sperrfrist</em>-Feld in den <em>Eigenschaften</em> (siehe den Abschnitt 'Dokument-Metadaten setzen').</li>
<li><strong>Löschdatum</strong>: Dies ist identisch mit dem <em>Löschdatum</em>-Feld in den <em>Eigenschaften</em> (siehe den Abschnitt 'Dokument-Metadaten setzen').</li>
<li><strong>Kommentare</strong>: Dies sind irgendwelche Kommentare, die Sie zu dieser Zustandsänderung machen möchten, die in der Historie festgehalten werden. Sie könnten z.B. Folgendes schreiben: <strong>Erster Entwurf. Bob, sieh dir bitte den zweiten Absatz an</strong>.</li>
<li><strong>Status verändern</strong>: Dies entspricht den verfügbaren Optionen im Dropdown-Menü. Beispieloptionen sind <em>Veröffentlichen</em>, <em>Einreichen</em> usw. Eine weitere Option, <em>Keine Veränderung</em>, ist für den Fall verfügbar, dass keine Änderung nötig ist.</li>
</ul>
<p>Wählen Sie den gewünschten Zustandswechsel, und klicken Sie auf <em>Speichern</em>, um dies festzuhalten.</p>
<div class="section" id="welche-workflow-zustande-gibt-es">
<h5>Welche Workflow-Zustände gibt es?</h5>
<p>An dieser Stelle fragen Sie sich vielleicht, was dieser <em>Workflow</em> überhaupt ist und was die Zustände bedeuten? Wie in Kapitel 7 beschrieben wird, versteht man unter Workflow die Möglichkeit, Inhalten verschiedene Zustände zuzuweisen. Es gibt folgende Standardzustände:</p>
<ul class="simple">
<li><strong>Sichtbar</strong>: Neue Inhalte werden im sichtbaren Zustand erzeugt. Alle Benutzer können sichtbare Inhalte über die Suchfunktion finden und direkt darauf zugreifen, indem sie die URL des Objekts besuchen. Sichtbare Inhalte erscheinen nicht im Navigationsbaum, können aber von ihren Besitzern und von Site-Managern bearbeitet werden.</li>
<li><strong>Offen</strong>: Offene Inhalte sind Elemente, die von Site-Mitgliedern zur Veröffentlichung eingereicht wurden. Aus Sicht eines Benutzers verhalten sich offene Inhalte wie solche im sichtbaren Zustand. Der Unterschied ist der, dass offene Elemente zur Überprüfung markiert sind. Die Site-Redakteure werden aufgefordert, offene Elemente zu veröffentlichen oder zurückzuweisen. Nur Manager und Redakteure können offene Elemente bearbeiten.</li>
<li><strong>Veröffentlicht</strong>: Veröffentlichte Elemente sind für alle Besucher der Site sichtbar. Sie erscheinen in Suchergebnissen sowie im Navigationsbaum. Sie können auch in anderen Bereichen auftauchen, die speziell für diesen Typ existieren (Nachrichten z.B. erscheinen auch dann, wenn Sie auf den <em>Nachrichten</em>-Reiter klicken). Nur Manager können veröffentlichte Elemente bearbeiten, aber ihre Besitzer können sie zur weiteren Bearbeitung zurückziehen (durch das Zurückziehen wird ein Element in den Zustand <em>öffentlicher Entwurf</em> versetzt).</li>
<li><strong>Privat</strong>: Elemente im privaten Zustand können nur von ihren Besitzern gesehen und bearbeitet werden - sowie von anderen mit Managerrechten an dem Ordner, in dem sie liegen. Bei anderen Benutzern erscheinen sie nicht in Suchergebnissen oder im Navigationsbaum. Private Elemente können von Managern bearbeitet werden.</li>
</ul>
</div>
<div class="section" id="wie-werden-inhalte-gepruft">
<h5>Wie werden Inhalte geprüft?</h5>
<p>Wenn Sie Redakteur sind, erscheint bei Ihrer ersten Anmeldung in der rechten Spalte Ihrer Homepage eine neue Revisionsliste. Dies ist eine Liste von Elementen, die zur Veröffentlichung eingereicht wurden und von Ihnen oder einem anderen Redakteur geprüft werden müssen (siehe Abbildung 3.17).</p>
<a class="reference external image-reference" href="img/03-17.png/image_view_fullscreen"><img alt="img/03-17.png" class="original" src="img/03-17.png" /></a>
<p>Abbildung 3.17. Die Revisionsliste</p>
<p>Diese Revisionsliste erscheint immer dann auf der rechten Seite, wenn Sie sich als Benutzer in der Rolle eines Redakteurs anmelden und es zu prüfende Elemente gibt. In meinem Fall habe ich mich als admin angemeldet, d.h. als der Benutzer, der beim Installationsvorgang angelegt wurde. Wenn Ihr Name in der Mitgliederleiste erscheint, wissen Sie, dass Sie angemeldet sind. Die Revisionsliste enthält zu prüfende Elemente; in diesem Fall müssen Sie das Testdokument prüfen. Klicken Sie auf das Dokument, um es zu öffnen. Unmittelbar danach können Sie dreierlei Dinge mit dem Dokument machen:</p>
<ul class="simple">
<li><strong>Ablehnen</strong>: Sie lehnen ein Dokument ab, indem Sie im Dropdown-Menü <em>Ablehnen</em> wählen. Dadurch wird der Inhalt wieder in den sichtbaren Zustand versetzt, was bedeutet, dass Sie als Redakteur damit nicht glücklich sind. Üblicherweise werden Sie auf <em>Erweitert</em> klicken, um das Kommentarformular zu öffnen und eine Begründung dafür zu schreiben, warum Sie es ablehnen.</li>
<li><strong>Genehmigen</strong>: Sie genehmigen ein Dokument durch die Wahl von <em>Veröffentlichen</em>, was den Inhalt in den Zustand <em>Veröffentlicht</em> versetzt. Dabei wird der Inhalt öffentlich verfügbar.</li>
<li><strong>Nichts tun</strong>: Indem Sie nichts tun, lassen Sie ein Dokument so, wie es ist. Dabei bleibt der Inhalt weiter in der Schwebe, was manchmal passieren kann, wenn Sie Informationen überprüfen oder mit anderen darüber reden müssen. Irgendwann sollten Sie hierher zurückkommen, um etwas mit diesem Inhalt zu machen, weil er so lange in Ihrer Liste erscheint, bis Sie eine der oben genannten Aktionen ausführen.</li>
<li><strong>Bearbeiten</strong>: Bearbeiten Sie das Dokument, und führen Sie dann eine der vorherigen Aktionen darauf aus. Als Redakteur können Sie beliebige Änderungen vornehmen, tun Sie das also, indem Sie auf den Reiter <em>Bearbeiten</em> klicken.</li>
</ul>
<p>Sobald Sie den Inhalt einmal aus dem Prüfzustand in den Zustand der Veröffentlichung oder Ablehnung versetzt haben, erscheint er nicht mehr in der Revisionsliste. Natürlich wird dabei angenommen, dass auf Ihrer Site jemand als Redakteur zur Verfügung steht. Normalerweise (aber nicht immer) ist das jener Benutzer, der als Administrator die Plone-Site erstellt hat. In Kapitel 8 beschreibe ich, wie man Benutzer hinzufügt, bearbeitet und einigen Benutzern die Rolle des Redakteurs gibt.</p>
</div>
<div class="section" id="wie-bearbeitet-man-ein-veroffentlichtes-dokument">
<h5>Wie bearbeitet man ein veröffentlichtes Dokument?</h5>
<p>Wenn ein Dokument einmal veröffentlicht ist, muss es zurückgezogen werden, damit es erneut bearbeitet werden kann. Wählen Sie dazu im Dropdown-Menü <em>Zurückziehen</em>, wodurch das Element wieder in den sichtbaren Zustand gelangt. Sobald es wieder sichtbar ist, können Sie es wieder bearbeiten und erneut in die Revisionsschlange bringen.</p>
<p>Dieser etwas störende Schritt ist notwendig, um sicherzustellen, dass alle Inhalte diesen Prüfschritt durchlaufen. Sie müssen z.B. sicherstellen, dass alle Bearbeitungsschritte an einer Seite korrekt sind, indem Sie ihren Inhalt prüfen. Benutzer mit der Manager-Rolle können Inhalte jederzeit bearbeiten, d.h., sie können schnell einen Tippfehler korrigieren, ohne diesen Prüfschritt durchzumachen. Dabei nimmt man an, dass Benutzer mit der Manager-Rolle vertrauenswürdig sind! Wenn Sie ein Manager sind, wie er in Kapitel 9 beschrieben wird, können Sie jedes Stück Inhalt anwählen und sehen dort den Reiter <em>Bearbeiten</em>. Klicken Sie darauf, um Ihre Änderungen vorzunehmen.</p>
</div>
</div>
<div class="section" id="zugriffsrechte-an-ihrem-dokument">
<h4>Zugriffsrechte an Ihrem Dokument</h4>
<p>Hiermit können Sie weitere Rechte an Ihrem Dokument an andere Benutzer oder Benutzergruppen im System vergeben. Dies ist eine Möglichkeit für Fortgeschrittene, die in Kapitel 9 detaillierter behandelt wird.</p>
</div>
</div>
<div class="section" id="andere-inhaltstypen-hinzufugen-und-andern">
<h3>Andere Inhaltstypen hinzufügen und ändern</h3>
<p>Soeben habe ich im Detail behandelt, wie Dokumente hinzugefügt und bearbeitet werden. Bei allen anderen Inhaltstypen ist es ähnlich. Sie alle verfügen über die gleichen oder ähnliche Aktionen zu ihrer Bearbeitung. Es ändern sich lediglich die Formulare und die Daten darin. In den folgenden Abschnitten werden einige andere dieser Inhaltstypen behandelt. Für alle folgenden Inhaltstypen gilt der gleiche Workflow-Prozess, d.h., sie müssen genauso wie Dokumente veröffentlicht werden.</p>
<div class="section" id="bilder-erstellen-und-bearbeiten">
<h4>Bilder erstellen und bearbeiten</h4>
<p>Bilder sind grafische Inhalte, die Sie durch die Auswahl von <em>Bild</em> in der Dropdown-Liste hinzufügen. Wenn Sie ein Bild hinzufügen, wechselt der Name des Inhalts zum Namen der Bilddatei. Wenn Sie also ein Bild namens <tt class="docutils literal">photo.gif</tt> hinzufügen, kann man in Plone unter <tt class="docutils literal">photo.gif</tt> darauf zugreifen. Beim Erstellen und Hochladen eines neuen Bildes können Sie das Bild auf Ihrer Festplatte auswählen, indem Sie auf den Button <em>Datei auswählen</em> klicken (siehe Abbildung 3.18).</p>
<a class="reference external image-reference" href="img/03-18.png/image_view_fullscreen"><img alt="img/03-18.png" class="original" src="img/03-18.png" /></a>
<p>Abbildung 3.18. Ein Bild hochladen</p>
<p>Die Dateinamen von Bildern haben üblicherweise eine Endung, z.B. <tt class="docutils literal">.gif</tt>, <tt class="docutils literal">.jpg</tt>, <tt class="docutils literal">.jpeg</tt>, <tt class="docutils literal">.png</tt> oder <tt class="docutils literal">.pict</tt>. Unter Plone können Sie Bilder auf einer Webseite darstellen, ohne sie auf Ihren lokalen Rechner herunterladen zu müssen, falls der entsprechende Bildtyp auf dem Webbrowser des Benutzers angezeigt werden kann. Die häufigsten Bildtypen sind <tt class="docutils literal">.gif</tt>, <tt class="docutils literal">.jpg</tt> und <tt class="docutils literal">.png</tt>, die auf fast allen Rechnern angezeigt werden können. Abbildung 3.19 zeigt ein Bild des Plone-Logos.</p>
<a class="reference external image-reference" href="img/03-19.png/image_view_fullscreen"><img alt="img/03-19.png" class="original" src="img/03-19.png" /></a>
<p>Abbildung 3.19. Anzeigen des Bildes</p>
<p>Ein Bild können Sie nicht direkt bearbeiten. Sie können es aber auf Ihrer Festplatte speichern und dort z.B. mit einem Programm wie Adobe Photoshop oder GIMP (GNU Image Manipulation Program) bearbeiten. Wenn Sie damit fertig sind, können Sie Ihr neues Bild mit einem Klick auf den Reiter <em>Bearbeiten</em> in Plone hochladen. Falls Sie sehr viel Bildbearbeitung machen, sollten Sie sich Kapitel 10 anschauen, das ein Werkzeug namens <em>External Editor</em> behandelt, mit dem Sie Bilder bearbeiten können, ohne diese hoch- und herunterladen zu müssen.</p>
</div>
<div class="section" id="dateien-hinzufugen-und-bearbeiten">
<h4>Dateien hinzufügen und bearbeiten</h4>
<p>Eine Datei ist jede beliebige Datei, die Sie von Ihrer lokalen Festplatte hochladen. Dazu wählen Sie <em>Datei</em> in der Dropdown-Liste. Im Reiter <em>Bearbeiten</em> sehen Sie den Button <em>Datei auswählen</em>, mit dem Sie eine Datei von Ihrer Festplatte auswählen können. Das könnte ein beliebiges Element sein, z.B. ein Microsoft Word-Dokument, eine Microsoft Excel-Tabelle, ein ausführbares Programm, ein Adobe Acrobat-Dokument usw. Wenn Sie eine Datei hinzufügen, wechselt in Plone der Name des Elements auf den Namen der hochgeladenen Datei. Wenn Sie also eine Datei namens <tt class="docutils literal">book.pdf</tt> hochladen, so ist sie in Plone unter <tt class="docutils literal">book.pdf</tt> verfügbar. Abbildung 3.20 zeigt eine Datei mit einfachem Text.</p>
<a class="reference external image-reference" href="img/03-20.png/image_view_fullscreen"><img alt="img/03-20.png" class="original" src="img/03-20.png" /></a>
<p>Abbildung 3.20. Hinzufügen einer Datei mit einfachem Text</p>
<p>Wenn erkannt wird, dass die Datei Text enthält, wird der Dateiinhalt auf der Webseite angezeigt und kann dort unter dem <em>Bearbeiten</em>-Reiter editiert werden. Ansonsten kann die Datei auf die lokale Festplatte heruntergeladen werden, was man tun muss, um sie dort zu bearbeiten. Danach kann man sie ins System hochladen. Sie werden bemerken, dass eine Datei auch einen zusätzlichen <em>Download</em>-Reiter hat, mit dem Sie die Datei direkt herunterladen können.</p>
</div>
<div class="section" id="termine-hinzufugen-und-bearbeiten">
<h4>Termine hinzufügen und bearbeiten</h4>
<p>Ein Termin ist etwas, das in der Zukunft passieren wird oder in der Vergangenheit schon passiert ist. In Plone können Sie Termine hinzufügen, die im Kalender erscheinen. Dazu wählen Sie in der Dropdown-Liste <em>Termin</em>. Bei Terminen gibt es mehr Angaben als bei den meisten anderen Plone-Objekten. Die meisten davon sind allerdings selbsterklärend (siehe Abbildung 3.21)</p>
<a class="reference external image-reference" href="img/03-21.png/image_view_fullscreen"><img alt="img/03-21.png" class="original" src="img/03-21.png" /></a>
<p>Abbildung 3.21. Hinzufügen eines Termins</p>
<p>Wie üblich ist auch hier <em>Titel</em> das einzig notwendige Feld, aber wenn Sie möchten, dass der Termin im Kalender erscheint, dann müssen Sie einen Start- und Endzeitpunkt angeben. Termine können mehrere Tage umfassen oder aber in der Vergangenheit liegen, solange nur der Start- vor dem Endzeitpunkt liegt. Um einen Termin einzugeben, wählen Sie die passenden Daten im Dropdown-Menü, oder Sie klicken auf das Datums-Icon, mit dem Sie eine grafische Auswahlmöglichkeit haben.</p>
<p>Nachdem der Termin veröffentlicht ist, erscheint er im Kalender. Wenn Sie die Maus über den Eintrag im Kalender bewegen, erscheinen der Start und das Ende des Termins ebenso wie sein Titel (siehe Abbildung 3.22).</p>
<a class="reference external image-reference" href="img/03-22.png/image_view_fullscreen"><img alt="img/03-22.png" class="original" src="img/03-22.png" /></a>
<p>Abbildung 3.22. Anzeige von Terminen im Kalender</p>
</div>
<div class="section" id="links-hinzufugen-und-bearbeiten">
<h4>Links hinzufügen und bearbeiten</h4>
<p>Der Inhaltstyp <em>Link</em> ist für die Benutzer die wichtigste Art, Links weiterzugeben. Diese URLs können Ressourcen im Internet oder im Intranet sein, eine interne Ressource oder irgendetwas, worauf Benutzer Zugriff haben. Links fügen Sie durch Auswahl von <em>Link</em> im Dropdown-Menü hinzu.</p>
<p>Wenn Sie einen Link zu einer Ressource im Internet erstellen, sollten Sie bei Ihrem Link das passende Protokoll voranstellen, z.B. <tt class="docutils literal"><span class="pre">http://</span></tt>. Wenn ich z.B. eine interessante Seite auf der BBC-Website besuche, die ich an andere weitergeben möchte, könnte ich einen Link darauf erstellen. Der Wert des URL ist der Text in der Adressleiste, z.B. <a class="reference external" href="http://news.bbc.co.uk">http://news.bbc.co.uk</a>, wie in Abbildung 3.23 zu sehen ist.</p>
<a class="reference external image-reference" href="img/03-23.png/image_view_fullscreen"><img alt="img/03-23.png" class="original" src="img/03-23.png" /></a>
<p>Abbildung 3-23. Hinzufügen eines Links</p>
</div>
<div class="section" id="nachrichten-hinzufugen-und-bearbeiten">
<h4>Nachrichten hinzufügen und bearbeiten</h4>
<p>Nachrichten werden auf Websites oft dazu verwendet, dem Leser interessante Neuigkeiten anzuzeigen. Tatsächlich enthält eine Nachricht die gleiche Information wie ein Dokument. Der einzige wirkliche Unterschied ist der, dass eine Nachricht dann angezeigt wird, wenn ein Besucher auf den <em>Nachrichten</em>-Reiter klickt (nachdem die Nachricht veröffentlicht worden ist), was in Abbildung 3.24 zu sehen ist.</p>
<a class="reference external image-reference" href="img/03-24.png/image_view_fullscreen"><img alt="img/03-24.png" class="original" src="img/03-24.png" /></a>
<p>Abbildung 3.24. Eine Liste von Nachrichten</p>
<p>Wenn ich eine Webseite schreiben würde, die langfristig von Bedeutung sein soll, z.B. Anfahrtsskizzen zum Büro meiner Firma, würde ich ein Dokument verwenden. Wenn ich hingegen gern eine Seite zu meinem neuen aufregenden Produkt hätte, die Aufmerksamkeit erregen soll, dann würde ich eine Nachricht wählen. Die Nachricht wäre unter dem Reiter <em>Nachrichten</em> sichtbar, und wenn neuere Nachrichten hinzukommen, würde sie sich langsam nach unten bewegen.</p>
</div>
</div>
<div class="section" id="inhalte-organisieren">
<h3>Inhalte organisieren</h3>
<p>Bislang haben Sie gesehen, wie man Inhalte in einer Plone-Site erstellt und bearbeitet, aber ohne eine klare Organisation kann das sehr schnell unübersichtlich werden. Im Wesentlichen haben Sie zwei Möglichkeiten, Ihre Inhalte zu organisieren: Ordner und Themen. Ein <em>Ordner</em> ist der einfachste und mächtigste Mechanismus zur Organisation von Inhalten und funktioniert wie ein Ordner bzw. ein Verzeichnis auf der Festplatte eines Rechners. Ein Ordner kann beliebige Inhalte umfassen, Inhalte können zwischen Ordnern hin- und herkopiert und -bewegt werden, und Ordner dürfen natürlich auch andere Ordner enthalten.</p>
<p>Um Inhalte zu organisieren, die über die ganze Site verteilt sind, kann man einen weiterentwickelten und seltener verwendeten Mechanismus in Form von <em>Themen</em> verwenden. Ein Thema durchsucht Ihre Website und findet alle Objekte, die auf ein bestimmtes Kriterium passen. Dadurch können Sie viele verschiedene Inhalte zusammenfassen.</p>
<div class="section" id="ordner-benutzen">
<h4>Ordner benutzen</h4>
<p>Ein Ordner verhält sich wie ein Ordner bzw. ein Verzeichnis auf einer Festplatte, mit dem Unterschied, dass er samt Inhalt innerhalb von Plone existiert. Diese Ordner können Sie genauso benutzen, indem Sie Inhalte gruppieren und in einen Ordner legen, etwa um sie zu kategorisieren oder um mehr Struktur zu schaffen. Einen Ordner fügen Sie auf Ihrer Site hinzu, indem Sie in der Dropdown-Liste <em>Ordner</em> wählen. Dadurch wird ein Ordner hinzugefügt und Sie gelangen zum Formular für die Eingabe der Ordnereigenschaften. Ein Ordner hat nur drei einfache Attribute, die der Benutzer bearbeiten kann: Name, Titel und Beschreibung. Diese habe ich bereits für Dokumente beschrieben, und sie unterscheiden sich nicht von denen für Ordner.</p>
<p>Order verfügen über zwei grüne Reiter, die leicht unterschiedliche Ansichten bieten: <em>Inhalte</em> und <em>Anzeigen</em>. Vielleicht haben Sie sogar schon bemerkt, dass es zu jedem auf der Site erstellten Inhalt einen <em>Inhalte</em>-Reiter gibt. Als Sie z.B. ein Dokument bearbeitet haben, gab es dort auch einen <em>Inhalte</em>-Reiter. Mit diesem Reiter gelangen Sie immer zum Inhalt eines Ordners.</p>
<div class="section" id="inhalt-eines-ordners-anzeigen">
<h5>Inhalt eines Ordners anzeigen</h5>
<p>Ein Ordner kennt das Konzept einer Standardseite, d.h. einer Seite, die der Benutzer sieht, wenn er einen Ordner anzeigen lässt. Dieses Konzept stammt von Websites, bei denen beim Anzeigen eines Ordners eine Standardseite angezeigt wird, sofern eine vorhanden ist. Der Name dieser Standardseite lautet oftmals <tt class="docutils literal">index.htm</tt> oder <tt class="docutils literal">index.html</tt>. Falls ein Ordner über eine Standardseite verfügt, wird beim Klick auf den Reiter <em>Anzeigen</em> diese Standardseite angezeigt. Wenn der Ordner keine Standardseite hat, wird eine Liste mit dem Inhalt dieses Ordners angezeigt. Bei der Suche nach einer anzuzeigenden Standardseite sucht Plone im Ordner nach einem Inhalt mit einem bestimmten Namen und zeigt diesen an. Der Seitenname lautet üblicherweise <tt class="docutils literal">index.html</tt> oder <tt class="docutils literal">index_html</tt>, allerdings kann der Site-Administrator diese Namen ändern oder welche hinzufügen.</p>
<p>Mit dieser Inhaltsansicht eines Ordners kann der Benutzer eine Vielzahl von Aufgaben erledigen, z.B. Inhalte verschieben, umbenennen, löschen, veröffentlichen und seine Reihenfolge in der Liste ändern. Wie Abbildung 3.25 zeigt, sehen Sie auch eine einfache Tabelle des Ordnerinhalts. Jede Zeile enthält den Titel des Inhalts (plus Icon), seinen Typ, seine Größe, eine Angabe dazu, wann er zuletzt geändert wurde, seinen aktuellen Workflow-Status sowie Sortierkriterien. Mit den Kästchen links können Sie die gewünschten Einträge auswählen, auf die Sie eine der unten aufgeführten Aktionen ausführen: <em>Umbenennen</em>, <em>Ausschneiden</em>, <em>Kopieren</em>, <em>Löschen</em> und <em>Status ändern</em>. Diese sind alle recht selbsterklärend, und sie können auf mehrere Objekte gleichzeitig angewendet werden, wenn die entsprechenden Kästchen angekreuzt sind.</p>
<a class="reference external image-reference" href="img/03-25.png/image_view_fullscreen"><img alt="img/03-25.png" class="original" src="img/03-25.png" /></a>
<p>Abbildung 3.25. Inhalt eines Ordners, nachdem einige der vorher beschriebenen Inhaltstypen hinzugefügt worden sind</p>
<p>Um einen Inhalt z.B. schnell umzubenennen, klicken Sie auf das Kästchen dieses Eintrags und klicken auf <em>Umbenennen</em>. Danach gelangen Sie zum <em>Umbenennen</em>-Formular, in dem Sie den Titel jedes Listeneintrags ändern können. Klicken Sie auf <em>Alle umbenennen</em>, damit die Änderung wirksam wird. Mit den Buttons <em>Ausschneiden</em> und <em>Kopieren</em> können Sie Inhalte zwischen verschiedenen Ordnern kopieren oder verschieben. Mit dem Button <em>Löschen</em> können Sie Elemente aus Plone entfernen. Genau wie auf Ihrer Festplatte werden beim Kopieren, Verschieben oder Löschen eines Ordners dessen Inhalte mitkopiert, -verschoben oder -gelöscht.</p>
<p>Eine neue Eigenschaft von Plone 2 besteht darin, die Standardreihenfolge von Ordnerinhalten zu ändern. Standardmäßig werden Ordnerinhalte in der Reihenfolge angezeigt, in der sie erstellt wurden. Falls ein Eintrag besonders wichtig ist und nach oben verschoben werden muss, können Sie das mit den Pfeilen rechts in der Tabelle tun. Die folgenden Eigenschaften erscheinen in Ordnerinhalten nur dann, wenn bestimmte Bedingungen erfüllt sind:</p>
<ul class="simple">
<li>Falls für den Inhalt ein Löschdatum gesetzt ist, das verstrichen ist, erscheint das Wort <em>abgelaufen</em> in Rot neben dem Eintrag.</li>
<li>Falls auf dem Server External Editor installiert ist, können Sie den Bleistift anklicken, um eine Bearbeitung in External Editor vorzunehmen (siehe Kapitel 10).</li>
<li>Falls der Inhalt gesperrt ist, erscheint ein Schloss-Icon neben dem Inhalt.</li>
</ul>
</div>
<div class="section" id="veroffentlichen-eines-ordners">
<h5>Veröffentlichen eines Ordners</h5>
<p>Ordner verfügen über einen wesentlich einfacheren Workflow als Dokumente. Weiter oben in diesem Kapitel haben Sie gesehen, wie man Inhalte veröffentlicht, damit sie öffentlich sichtbar werden, weil Benutzer Inhalte auf diese Weise erzeugen und bearbeiten können, bevor sie allgemein freigeschaltet werden. Ordner sind allerdings insofern anders, als sie weitere Inhalte umfassen, aber selbst keinen Inhalt haben. Aus diesem Grund haben Ordner keinen Zustand <em>Überprüfen</em>. Jeder kann private Ordner direkt veröffentlichen oder anlegen, d.h., es gibt drei Zustände: <em>Privat</em>, <em>Sichtbar</em> und <em>Veröffentlicht</em>.</p>
<p>Wählen Sie in der Dropdown-Liste <em>Veröffentlichen</em>, nachdem Sie einen Ordner hinzugefügt haben. Danach erscheint er in der Navigation. Gemäß früherer Workflow-Regeln erscheint er erst dann in der Navigation, wenn er veröffentlicht wurde.</p>
</div>
</div>
<div class="section" id="themen-verwenden">
<h4>Themen verwenden</h4>
<p>Mit einem Thema können Sie Inhalte aus verschiedenen Orten der gesamten Plone-Site sammeln und an einem Ort anzeigen. Themen funktionieren über ein Kriterium, das auf alle Objekte zutrifft, die Sie sammeln möchten. Ein Kriterium könnte z.B. sein: alle Bilder oder Nachrichten, bei denen <em>Plone</em> im Text vorkommt. Da Themen ein ziemlich komplexer Inhaltstyp sind, können sie zu Beginn nur von Managern erstellt werden. Wenn Sie in der Liste der zu erstellenden Elemente kein Thema sehen, so haben Sie keine Berechtigung dafür.</p>
<p>Um ein Thema hinzuzufügen, wählen Sie im Dropdown-Menü <em>Thema</em>. Danach können Sie die Schlüsselkriterien unter dem Reiter <em>Kriterien</em> eingeben. Die Listen für die Kriterien und ihre Typen ist in den Dropdown-Menüs unten auf der Seite zu sehen. Diese Liste ist ziemlich verwirrend, deswegen versuche ich erst gar nicht, sie hier zu behandeln. Was diese Begriffe bedeuten und wofür sie stehen, basiert leider ganz extrem auf der darunter liegenden Technologie von Katalogindizes und Objektattributen. Aus diesem Grund wird das in Kapitel 11 behandelt.</p>
<p>Um beispielsweise ein Thema zu erstellen, das alle Bilder anzeigt, müssen Sie ein Kriterium hinzufügen, das nach Inhalten sucht, die auf <tt class="docutils literal">portal_type</tt> basieren. Wählen Sie dazu den Feldnamen <tt class="docutils literal">portal_type</tt> sowie den Kriterientyp <tt class="docutils literal">String Criterion</tt>, und klicken Sie dann auf <em>Hinzufügen</em>. Diese Kriterien werden oben auf der Seite hinzugefügt. Geben Sie im Feld neben <tt class="docutils literal">portal_type</tt> den Wert <strong>Image</strong> ein, und klicken Sie auf <em>Speichern</em>. Nun haben Sie die Themenkriterien, mit denen alle Bildinhalte angezeigt werden. Wenn Sie zurück zum Reiter <em>Anzeigen</em> gehen, können Sie alle Bilder auf der Site sehen.</p>
<p>Themen sind, wie schon erwähnt wurde, sehr komplex, haben eine recht unfreundliche Schnittstelle und sind nur erfahrenen Benutzern zu empfehlen. Viele Leute finden Themen sehr hilfreich, was der Grund dafür ist, dass sie noch immer in Plone existieren. Ein benutzerfreundlicheres System wird aber in Zukunft noch entwickelt werden.</p>
</div>
</div>
<div class="section" id="inhalte-diskutieren-und-finden">
<h3>Inhalte diskutieren und finden</h3>
<p>Inhalte in Plone zu erstellen und zu bearbeiten wird wesentlich nützlicher, wenn die Leute diese Inhalte finden und dann auch diskutieren können. Am ehesten finden Benutzer Inhalte über die Suche und Navigation. Plone richtet Suche und Navigation glücklicherweise automatisch für die Benutzer ein, d.h., man kann die erstellten Inhalte auf einfache Weise finden.</p>
<div class="section" id="inhalte-kommentieren">
<h4>Inhalte kommentieren</h4>
<p>Feedback von Benutzern ist ein wichtiger Bestandteil jeder Website. Dadurch, dass Benutzer Kommentare hinzufügen können, ermöglichen Sie es ihnen, Feedback zu geben, Tippfehler zu korrigieren oder die Inhalte anderweitig zu diskutieren. In Plone können Sie fast alle Inhalte diskutieren, mit Ausnahme von Ordnern und Themen.</p>
<p>Diskussionen können Sie auf zweierlei Arten ermöglichen. Zum einen kann der Besitzer eines Inhalts (d.h. derjenige, der ihn erstellt hat) die Diskussionsmöglichkeit einschalten, indem er den <em>Eigenschaften</em>-Reiter des Objekts anklickt und unter <em>Diskussion erlauben</em> den Punkt <em>Eingeschaltet</em> wählt, wie in Abbildung 3.26 zu sehen ist. Zum anderen bestimmt die vom Site-Administrator definierte Standardeinstellung die Regelung für diesen Inhaltstyp. Diese Einstellung durch den Administrator wird in Kapitel 10 beschrieben.</p>
<a class="reference external image-reference" href="img/03-26.png/image_view_fullscreen"><img alt="img/03-26.png" class="original" src="img/03-26.png" /></a>
<p>Abbildung 3.26 Diskussionen einschalten</p>
<p>Nachdem Diskussionen eingeschaltet sind, klicken Sie auf den Button <em>Kommentieren</em>, um den Inhalt zu diskutieren. Dazu erscheint dann ein Formular (siehe Abbildung 3.27).</p>
<a class="reference external image-reference" href="img/03-27.png/image_view_fullscreen"><img alt="img/03-27.png" class="original" src="img/03-27.png" /></a>
<p>Abbildung 3.27. Kommentar zu einem Inhalt erstellen</p>
<p>Geben Sie das Stichwort und den Haupttext Ihres Kommentars ein. Der Text wird als einfacher Text eingegeben, d.h., Sie können ihn ganz normal eintippen. Kommentare werden nicht vom Workflow-System erfasst, d.h., sie erscheinen gleich nach ihrer Eingabe. Nachdem ein Kommentar eingegeben wurde, kann man darauf antworten, was eine verkettete Liste von Kommentaren zu einem Inhalt ergibt. Außerdem werden Kommentare im Katalog eingetragen, wodurch in ihnen auch gesucht werden kann.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Administratoren, die als Manager angemeldet sind, können einzelne Antworten oder gesamte Ketten entfernen. Beim Ausschalten von Antworten werden die Kommentare nicht gelöscht, sie werden lediglich nicht mehr angezeigt. Das heißt,  durch das erneute Einschalten von Kommentaren werden vorhandene Kommentare wieder sichtbar.</p>
</div>
</div>
<div class="section" id="nach-inhalten-suchen">
<h4>Nach Inhalten suchen</h4>
<p>Plone verfügt über eine mächtige Suchmaschine auf der Basis von Zopes ZCatalog. Mit dieser Suchmaschine kann Inhalt auf mehrere Arten katalogisiert werden, und es kann effizient und schnell danach gesucht werden. Kapitel 10 enthält Details darüber, wie sie funktioniert und abgefragt werden kann.</p>
<p>Wenn ein Benutzer nach Inhalten sucht, werden ihm solche dann angezeigt, wenn sie sich in einem von zwei Zuständen befinden: <em>Veröffentlicht</em> oder <em>Sichtbar</em>. Ganz oben auf einer Plone-Seite befindet sich ein Suchfeld, mit dem man - wie bei einer Internet-Suchmaschine - sehr leicht eine einfache Textsuche durchführen kann (siehe Abbildung 3.28). Geben Sie z.B. <strong>Tuesday</strong> ein, um alle Inhalte zu finden, die das Wort <em>Tuesday</em> enthalten. Dann wird das Ergebnis aller passenden Inhalte angezeigt. Klicken Sie auf einen Titel, um dorthin zu gelangen.</p>
<a class="reference external image-reference" href="img/03-28.png/image_view_fullscreen"><img alt="img/03-28.png" class="original" src="img/03-28.png" /></a>
<p>Abbildung 3.28. Eine Suche nach <em>Tuesday</em> auf Plone.org</p>
<p>Diese Suche bietet recht ausgefeilte Möglichkeiten, die denen der meisten Suchmaschinen recht ähnlich sind. Diese einfache Abfrage können Sie um einiges komplexer machen, indem Sie z.B. die folgenden Optionen verwenden:</p>
<ul class="simple">
<li><strong>Platzhalter</strong>: Verwenden Sie einen Stern für beliebig viele Buchstaben. So passt z.B. <tt class="docutils literal">Dien*</tt> auf <tt class="docutils literal">Dienstag</tt> und <tt class="docutils literal">Dienstage</tt>. Am Wortanfang können Sie allerdings keinen Stern verwenden.</li>
<li><strong>Einzelne Joker</strong>: Verwenden Sie ein Fragezeichen für einen Buchstaben. Beispiel: <tt class="docutils literal"><span class="pre">Ro?e</span></tt> passt auf <tt class="docutils literal">Rose</tt>, <tt class="docutils literal">Robe</tt>, <tt class="docutils literal">Rote</tt> usw. Am Wortanfang können Sie jedoch kein Fragezeichen verwenden.</li>
<li><strong>And</strong>: Mit <tt class="docutils literal">and</tt> geben Sie an, dass die beiden Begriffe rechts und links davon vorhanden sein müssen. Beispiel: <tt class="docutils literal">Rom and Dienstag</tt> gibt nur Ergebnisse zurück, in denen beide Wörter im Inhalt vorkommen.</li>
<li><strong>Or</strong>: Mit <tt class="docutils literal">or</tt> geben Sie an, dass mindestens einer der Begriffe vorkommen muss. Beispiel: <tt class="docutils literal">Rom or Dienstag</tt> gibt Ergebnisse zurück, falls eines der Wörter im Inhalt vorkommt.</li>
<li><strong>Not</strong>: Mit <tt class="docutils literal">not</tt> erhalten Sie Ergebnisse, in denen der Begriff nicht vorkommt (es wird ein <tt class="docutils literal">and</tt> als Präfix benötigt). Beispiel: <tt class="docutils literal">Willkommen and not Seite</tt> würde passende Seiten zurückgeben, in denen <tt class="docutils literal">Willkommen</tt>, aber nicht <tt class="docutils literal">Seite</tt> vorkommt.</li>
<li><strong>Sätze</strong>: Sätze können Sie in doppelten Anführungszeichen (&quot;) setzen, um damit mehrere Wörter nacheinander anzugeben. Beispiel: <tt class="docutils literal">&quot;Diese Seite&quot;</tt> passt auf <tt class="docutils literal">Diese Seite führt Sie ins Plone <span class="pre">Content-Management-System</span> ein.</tt>, aber nicht auf <tt class="docutils literal">Diese Startseite <span class="pre">von...</span></tt>.</li>
<li><strong>Negierte Sätze</strong>: Sie können einem Satz ein Minuszeichen (-) als Präfix voranstellen. Beispiel: <tt class="docutils literal">Start <span class="pre">-&quot;Diese</span> Seite&quot;</tt> passt auf alle Seiten, in denen <tt class="docutils literal">Start</tt> vorkommt, aber nicht <tt class="docutils literal">Diese Seite</tt>.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Bei jeder Suche ist die Groß-/Kleinschreibung nicht von Bedeutung.</p>
</div>
<p>Bei großen Sites kann es eine Menge Ergebnisse geben, daher werden nur jeweils 20 auf einmal angezeigt. Um durch die Ergebnisse blättern zu können, erscheinen Navigationsleisten oben und unten auf den Suchergebnisseiten. Die Werte eines Objekts, die bei der Suche verwendet werden, sind dessen Titel, Beschreibung und Haupttext (falls der Inhaltstyp einen hat, das trifft z.B. auf Nachrichten und Dokumente zu).</p>
</div>
<div class="section" id="durchfuhren-einer-erweiterten-suche">
<h4>Durchführen einer erweiterten Suche</h4>
<p>Sie können die Suchergebnisse dadurch einschränken, dass Sie eine erweiterte Suche durchführen, die bei den Suchergebnissen einer Standardsuche verfügbar ist. Auf alten Plone-Sites gelangten die Benutzer mit einem <em>Suchen</em>-Reiter dorthin. Wenn Sie möchten, können Sie diesen reaktivieren, was in Kapitel 4 beschrieben wird. Mit Hilfe des Formulars <em>Erweiterte Suche</em> kann der Benutzer mit einer Reihe von Attributen nach Inhalten suchen, darunter Titel, Stichworte, Beschreibung, Revisionsstatus, Erstellungsdatum, Inhaltstyp und sogar Autor - neben dem Suchtext, der auch bei der schnellen Suche mit dem Feld in der oberen rechten Ecke verwendet wird (siehe Abbildung 3.29).</p>
<a class="reference external image-reference" href="img/03-29.png/image_view_fullscreen"><img alt="img/03-29.png" class="original" src="img/03-29.png" /></a>
<p>Abbildung 3.29. Erweiterte Suche</p>
<p>Obwohl die Angabe im Suchtextfeld sowohl im Titel als auch in der Beschreibung gesucht wird, möchten Sie evtl. nur im Titel oder nur in der Beschreibung suchen. Aus diesem Grund gibt es diese Felder im Formular <em>Erweiterte Suche</em>. Joker, Platzhalter und alle weiteren Optionen einer erweiterten Suche können Sie bei der Suche in Titeln und Beschreibungen nicht verwenden. Jedes Suchergebnis passt in der Eingabe (sofern vorhanden) auf alle Felder, und das Ergebnis ist die Schnittmenge aller Begriffe.</p>
</div>
</div>
<div class="section" id="beispiel-erstellen-der-website-zum-plone-buch">
<h3>Beispiel: Erstellen der Website zum Plone-Buch</h3>
<p>Um Ihnen ein Beispiel für eine Plone-Site und eine Reihe einzelner Beispiele zu geben, habe ich für dieses Buch eine Website eingerichtet. Es ist eine Plone-Site mit einigen wenigen Änderungen. Bei der Behandlung der einzelnen Buchteile werde ich auf diese Site verweisen und neue Eigenschaften hinzufügen, wie sie im Buch behandelt werden, darunter neue Templates, Skins usw. Die Website zu diesem Buch finden Sie unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a>. Zu Beginn habe ich sie auf einem Windows-Server erstellt, wie in Kapitel 2 beschrieben ist. Später habe ich sie jedoch auf Linux übertragen.</p>
<p>Diese Site hat folgende Ziele:</p>
<ul class="simple">
<li>Sie bietet Leuten einen Ort, an dem sie Informationen zum Buch erhalten und darüber, wo sie es kaufen können.</li>
<li>Sie bietet einfachen Zugang zur Software, die in diesem Buch benutzt wird.</li>
<li>Sie bietet Code-Beispiele und ermöglicht den Benutzern, die Beispiele im Buch auszuprobieren.</li>
<li>Sie enthält Errata oder sonstige Probleme, die nach der Publikation gefunden werden.</li>
</ul>
<p>Nach der Einrichtung einer Plone-Site habe ich folgende einfache Order- und Seitenstruktur erstellt:</p>
<pre class="literal-block">
Home
 |_ Software
 |_ Chapters
    |_ Chapter 1
    |_ Chapter 2
    ...
</pre>
<p>Dazu habe ich mich als jener Benutzer angemeldet, der vom Installationsprogramm angelegt wurde. In meinem Fall ist das der Benutzer admin. Nach der Anmeldung bin ich auf die Homepage gegangen, habe auf den <em>Bearbeiten</em>-Reiter geklickt und habe etwas Text für die Homepage geschrieben. Dann habe ich Links zu den Ordnern <tt class="docutils literal">Chapters</tt> und <tt class="docutils literal">Software</tt> hinzugefügt. Und schließlich habe ich auf den <em>Inhalte</em>-Reiter geklickt und zwei Ordner hinzugefügt, wie in Abbildung 3.30 zu sehen ist.</p>
<a class="reference external image-reference" href="img/03-30.png/image_view_fullscreen"><img alt="img/03-30.png" class="original" src="img/03-30.png" /></a>
<p>Abbildung 3.30. Der Ordnerinhalt mit meiner Homepage und den neuen Ordnern</p>
<p>Anschließend bin ich in den Ordner <tt class="docutils literal">Chapters</tt> gegangen und habe angefangen, für jedes Kapitel einen Ordner zu erstellen. Da ich keine Standardseite erstellt habe, erstellt Plone von sich aus eine Liste mit allen Kapiteln. Die Beschreibung eines Kapitels besteht jeweils aus dem Kapitelnamen (z.B. <em>Einführung in Plone und dieses Buch</em>), und der Kurzname besteht aus der Kapitelnummer - dadurch bleiben meine URLs hübsch kurz (z.B. <tt class="docutils literal">/Chapters/3</tt>). Ich habe alles im sichtbaren Zustand belassen, damit man anschließend sofort Inhalte erstellen kann.</p>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch4.rst">
    <title>4. Einfache Anpassungen vornehmen</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch4.rst</link>
    <description>Nachdem Sie nun wissen, wie Sie Inhalte erstellen und bearbeiten, werden Sie anfangen wollen, Ihre Site anzupassen. In diesem Kapitel erfahren Sie, wie Sie mit den Möglichkeiten eines Systemadministrators einfache Anpassungen in Plone vornehmen können. Zu diesem Zweck muss ein Benutzer mit Manager-Rechten angemeldet sein (siehe dazu auch Kapitel 2).</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Einfache Anpassungen vornehmen</h2>
<p>Nachdem Sie nun wissen, wie Sie Inhalte erstellen und bearbeiten, werden Sie anfangen wollen, Ihre Site anzupassen. In diesem Kapitel erfahren Sie, wie Sie mit den Möglichkeiten eines Systemadministrators einfache Anpassungen in Plone vornehmen können. Zu diesem Zweck muss ein Benutzer mit Manager-Rechten angemeldet sein (siehe dazu auch Kapitel 2).</p>
<p>All diese Anpassungen sind Optionen bei der Konfiguration, die Sie über das Web einstellen können. Anstatt sie alle im Detail einzeln zu erklären, soll dieses Kapitel Ihnen einen Überblick über viele Themenbereiche geben und erklären, wie man gewisse Aufgaben löst. Währenddessen sollen Sie einen Blick unter die Haube werfen können. Diese Themen werden dann im weiteren Verlauf des Buches noch erweitert und erklärt.</p>
<p>Am Anfang bringt es am meisten, ins Plone-Control Panel (den Konfigurationsbereich von Plone) zu schauen. Alle Teile einer Plone-Site sind so entworfen, dass sie sich leicht ändern und anpassen lassen. Die blauen Reiter, die Sie am oberen Rand der Seite sehen können, sind leicht hinzuzufügen und zu entfernen. Andere Beispiele hierfür sind die Kästen in der linken und rechten Spalte, die auch <em>Portlets</em> genannt werden. Plone enthält mehrere Portlets, und Sie können ganz leicht angeben, wo diese angezeigt werden sollen.</p>
<p>Am Ende dieses Kapitels erfahren Sie, wie Sie auch Cascading Style Sheets (CSS) und Bilder in Plone anpassen können. In einer Plone-Site wird alles durch CSS beinflusst. Tatsächlich werden Sie in diesem Kapitel sehen, dass alle Farben, alle Positionsangaben und viele der Bilder, die Sie sehen können, dadurch bestimmt werden. Wenn Sie in der Lage sind, CSS-Code zu verändern, dann können Sie fast das komplette Look-and-Feel einer Plone-Site ändern. Alle in diesem Kapitel behandelten Optionen zeigen Ihnen, welch große Gestaltungsmöglichkeiten Sie in Ihrer Plone-Site haben.</p>
<div class="section" id="sites-verwalten">
<h3>Sites verwalten</h3>
<p>Als Erstes sollten sich Systemadministratoren das Plone-Control Panel anschauen. Hiermit hat man Zugriff auf einige der Verwaltungsfunktionen einer Site, z.B. auf den Namen und die Beschreibung Ihrer Plone-Site, die Benutzer- und Gruppenverwaltung sowie auf irgendwelche eventuellen Fehler in Ihrer Site.</p>
<p>Der Begriff <em>Control Panel</em> wird sehr häufig benutzt, daher sollten Sie ihn nicht mit dem Control Panel im Zope Management Interface (ZMI) verwechseln, das die unteren Schichten der ZMI-Optionen anzeigt. Das Plone-Control Panel wird kontinuierlich weiterentwickelt, um eine benutzerfreundlichere Schnittstelle zu den Funktionen im ZMI zu bieten. Das da Projekt sehr aktiv ist, kann man nur schwer voraussagen, welche Funktionalität in Zukunft vorhanden sein wird. Daher empfehle ich, dass Sie einfach ins Control Panel gehen und sich anschauen, welche Funktionen momentan verfügbar sind. Wenn Sie Ihre Aufgabe dort nicht lösen können, müssen Sie ins ZMI gehen.</p>
<p>Um auf das Control Panel zuzugreifen, melden Sie sich bei Plone als Benutzer mit Manager-Rechten an. Wenn Sie keinen solchen Benutzer haben, aber selbst Site-Administrator sind, sollten Sie schnell in Kapitel 9 nachsehen, wie Sie das machen. Wenn Sie kein Site-Administrator sind, aber einen solchen Zugriff haben möchten, sollten Sie Ihren Site-Administrator darum bitten. Um zum Control Panel zu gelangen, klicken Sie auf <em>Plone Konfiguration</em> oben auf der Seite (siehe Abbildung 4.1).</p>
<a class="reference external image-reference" href="img/04-01.png/image_view_fullscreen"><img alt="img/04-01.png" class="original" src="img/04-01.png" /></a>
<p>Abbildung 4.1. Zugriff auf das Control Panel</p>
<p>Danach wird das Control Panel geöffnet (siehe Abbildung 4.2).</p>
<a class="reference external image-reference" href="img/04-02.png/image_view_fullscreen"><img alt="img/04-02.png" class="original" src="img/04-02.png" /></a>
<p>Abbildung 4.2. Das Plone-Control Panel</p>
<p>Im Control Panel sind folgende Funktionen verfügbar:</p>
<ul class="simple">
<li><strong>Produkte hinzufügen/löschen</strong>: Mit einem Klick auf diesen Link können Sie die Installation von Produkten automatisieren (das wird detailliert in Kapitel 10 behandelt).</li>
<li><strong>Fehlerprotokoll</strong>: Mit diesem Link gelangen Sie zum Fehlerprotokoll der Plone-Site.</li>
<li><strong>E-Mail Einstellungen</strong>: Hiermit können Sie den SMTP-Server (Simple Mail Transfer Protocol) ändern, mit dem Plone E-Mails verschickt.</li>
<li><strong>Portal Einstellungen</strong>: Hiermit können Sie Portal-Einstellungen ändern (siehe den Abschnitt &quot;Titel, Beschreibung und E-Mail-Adressen ändern&quot; in diesem Kapitel).</li>
<li><strong>Aussehen</strong>: Hiermit können Sie die aktuelle Skin (in der deutschen Lokalisierung auch Aussehen genannt) ändern (siehe Kapitel 7).</li>
<li><strong>Benutzer- und Gruppenverwaltung</strong>: Hiermit können Sie Benutzer und Gruppen verändern (siehe Kapitel 8).</li>
<li><strong>Zope Management Interface</strong>: Mit diesem Link gelangen Sie zum ZMI.</li>
</ul>
<p>Im weiteren Verlauf dieses Buchs beziehe ich mich auf das Plone-Control Panel, wann immer die jeweilige Eigenschaft dort verfügbar ist. Ansonsten wird das ZMI verwendet, um Eigenschaften zu verändern.</p>
<div class="sidebar">
<p class="first sidebar-title">Das ZMI verwenden</p>
<!-- The ZMI is the basic interface that gives you access to Plone's underlying Zope interface. Before Plone existed, the ZMI was the main way to access, edit, and manage a Zope site and its content. This was originally the Web interface for the content management system. Of course, nowadays Zope isn't really an out-of-the-box content management system but instead is an application that sits under a system such as Plone. After quickly playing with the ZMI, you'll see why it isn't suited as an interface to a content management system. -->
<p>Das ZMI ist die grundlegende Schnittstelle, mit der Sie Zugriff auf die darunter liegende Zope-Schnittstelle von Plone haben. Vor Plone war das ZMI die wichtigste Methode, um auf eine Zope-Site zuzugreifen und sie und ihren Inhalt zu bearbeiten und zu verwalten. Das ZMI war ursprünglich die Webschnittstelle für das Content-Management-System. Natürlich ist Zope kein solches Fertig-CMS wie Plone, sondern eine Anwendung, die unter einem System wie Plone läuft. Nach einigem Herumspielen mit dem ZMI werden Sie sehen, warum es als Schnittstelle zu einem CMS ungeeignet ist.</p>
<!-- One thing the ZMI does provide is a simple interface to the underlying Plone and Zope infrastructure. You can find many of the basic features mentioned in this chapter through Plone, but you'll need to use the ZMI eventually. If you haven't gone to the ZMI before, then you'll find that there are a few simple ways to get there; the easiest way is to log in as a user with the manager role, click *plone setup*, and then click *Zope Management Interface*. You'll note that the address of the ZMI is the uniform resource locator (URL) of your Plone site with */manage* on the end of it. The ZMI for your Plone site should look like this: -->
<p>Eines bietet das ZMI aber doch, nämlich eine Schnittstelle zur darunter liegenden Infrastruktur von Plone und Zope. Viele der in diesem Kapitel genannten grundlegenden Eigenschaften finden Sie über Plone, aber irgendwann müssen Sie das ZMI doch benutzen. Wenn Sie noch nicht ins ZMI gegangen sind, werden Sie sehen, dass es einige einfache Wege dorthin gibt. Der einfachste ist, sich als Benutzer mit Manager-Rechten anzumelden und auf <em>Plone Konfiguration</em> und dann auf <em>Zope Management Interface</em> zu klicken. Sie werden feststellen, dass die Adresse des ZMI der URL Ihrer Plone-Site mit einem <tt class="docutils literal">/manage</tt> am Ende ist. Das ZMI für Ihre Plone-Site sollte etwa wie folgt aussehen:</p>
<a class="reference external image-reference" href="img/04-02a.png/image_view_fullscreen"><img alt="img/04-02a.png" class="original" src="img/04-02a.png" /></a>
<!-- You may have a problem with virtual hosting, which occurs with the Windows and Mac installers. Virtual hosting is the ability to have the Plone site as the root object rather than the root of your Zope instance. For more information on virtual hosting, see Chapter 10. So, to get to the root, you need to access the manage port. On Windows, select Start - Plone - Plone - Manage Root. You'll note that this sets the address to http://localhost:8080/manage. For information on virtual hosting with your installation, see the specific documentation. -->
<p>Eventuell haben Sie ein Problem mit dem Virtual Hosting, was bei den Windows- und Mac-Installationsprogrammen vorkommen soll. <em>Virtual Hosting</em> bezeichnet die Möglichkeit, die Plone-Site und nicht die Wurzel Ihrer Zope-Instanz als Wurzelobjekt zu benutzen. Weitere Angaben zum Virtual Hosting finden Sie in Kapitel 10. Um also zur Wurzel zu gelangen, müssen Sie auf den Management-Port zugreifen. Unter Windows wählen Sie <em>Start - Plone - Plone - Manage Root</em>. Sie werden feststellen, dass die Adresse der Site auf <a class="reference external" href="http://localhost:8080/manage">http://localhost:8080/manage</a> eingestellt wird. Weitergehende Informationen zum Virtual Hosting bei Ihrer Installation finden Sie in der entsprechenden Dokumentation.</p>
<!-- You'll need to get to the root of your Zope installation for two reasons. First, you'll need to get to the Zope control panel. Second, you'll need to get to the root of your Plone site to make, rename, and copy Plone sites. The Zope control panel gives you database information and access to products and other add-ons (you'll need access to this for Chapter 10), as shown here: -->
<p>Sie müssen aus zwei Gründen zur Wurzel Ihrer Zope-Installation gelangen können: Erstens müssen Sie zum Zope-Control Panel kommen können, und zweitens müssen Sie zur Wurzel Ihre Plone-Site gelangen können, um Plone-Sites zu erstellen, umzubenennen und zu kopieren. Im Zope-Control Panel erhalten Sie Zugriff auf Datenbankinformationen und auf Produkte und andere Add-Ons (diesen Zugriff benötigen Sie in Kapitel 10), wie Sie hier sehen können:</p>
<a class="last reference external image-reference" href="img/04-02b.png/image_view_fullscreen"><img alt="img/04-02b.png" class="original" src="img/04-02b.png" /></a>
</div>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Bei der Arbeit mit dem ZMI finde ich es sehr hilfreich, zwei verschiedene Browser gleichzeitig geöffnet zu haben. Ich zum Beispiel verwende Mozilla und Firefox. Nebenbei bemerkt empfiehlt es für Site-Administratoren immer, zwei verschiedene Browser zu haben, damit man testen kann, ob Änderungen in mehr als einem Browser funktionieren.</p>
</div>
<div class="section" id="titel-beschreibung-und-e-mail-adressen-andern">
<h4>Titel, Beschreibung und E-Mail-Adressen ändern</h4>
<p>Titel, Beschreibung und E-Mail-Adressen werden als Objekteigenschaften in einer Plone-Site gespeichert. Auf diese Felder können Sie zugreifen, indem Sie im Plone-Control Panel auf <em>Portal Einstellungen</em> klicken (siehe Abbildung 4.3).</p>
<a class="reference external image-reference" href="img/04-03.png/image_view_fullscreen"><img alt="img/04-03.png" class="original" src="img/04-03.png" /></a>
<p>Abbildung 4.3. Portal-Einstellungen</p>
<p>Folgende Portal-Einstellungen sind vorhanden:</p>
<ul class="simple">
<li><strong>Name des Portals</strong>: Dies ist der Name der Site, der im Titel von Browsern, der Pfadnavigation, der Navigation, E-Mails usw. erscheint. Der voreingestellte Wert lautet Portal.</li>
<li><strong>Beschreibung des Portals</strong>: Dies ist die Beschreibung des Portals, die im Momement nur bei der Syndizierung verwendet wird.</li>
<li><strong>Absendername des Portals</strong>: Dieses Feld wird in mehreren Zusammenhängen verwendet, z.B. bei einem verlorenen Passwort oder bei der Funktion &quot;Einem-Freund-empfehlen&quot;. Plone verschickt E-Mails unter diesem Namen, dessen Vorgabewert <em>Portal Administrator</em> lautet.</li>
<li><strong>Absenderadresse des Portals</strong>: Unter dieser Adresse verschickt Plone seine E-Mails. Die Voreinstellung lautet <em>postmaster&#64;localhost</em>.</li>
<li><strong>Voreingestellte Sprache</strong>: Dies ist die Standardsprache, die in den Eigenschaften eines Objekts verwendet wird.</li>
<li><strong>Passworteigenschaften</strong>: Neue Benutzer haben zwei Möglichkeiten: Entweder wählen sie ein Passwort selbst oder sie bekommen eines per E-Mail geschickt. Zwar müssen sie in beiden Fällen ein Passwort angeben, aber im zweiten Fall ist es schwerer, Scheinkonten einzurichten.</li>
<li><strong>Externe Editoren ermöglichen</strong>: Dies erlaubt es, externe Editoren, also ein fortgeschrittenes Bearbeitungswerkzeug, zu benutzen. Dazu muss das Produkt External Editor auf dem Rechner des Benutzers installiert sein. Kapitel 10 behandelt dies im Detail.</li>
</ul>
<p>Nach der Auswahl der gewünschten Optionen klicken Sie auf <em>Speichern</em>, damit die Änderungen sofort wirksam werden.</p>
</div>
<div class="section" id="einen-mail-server-einrichten">
<h4>Einen Mail-Server einrichten</h4>
<p>Plone verschickt E-Mails mit Hilfe des Objekts <em>MailHost</em>, das eine Schnittstelle zum SMTP-Server bietet und es dem Entwickler ermöglicht, Formulare und Werkzeuge zu schreiben, mit denen E-Mails verschickt werden können. Diese Einstellung wird bei der Funktion &quot;Einem-Freund-empfehlen&quot; und zum Verschicken eines vergessenen Passworts verwendet.</p>
<p>Die Standardkonfiguration ist ein Mailserver auf dem lokalen Rechner auf Port 25. Falls der SMTP-Server sich anderswo im Netzwerk befindet, können Sie auf das Formular zugreifen, indem Sie auf <em>Plone Konfiguration</em> und <em>E-Mail-Einstellungen</em> klicken und dann den Server und den Port entsprechend ändern. In meinem Netzwerk befindet sich der Mailserver auf <em>monty.clearwind.ca</em> auf Port 1025, d.h., ich stelle den Server ein, wie es in Abbildung 4.4 zu sehen ist. In den meisten Fällen jedoch müssen Sie hier nichts ändern (solange man den Server nicht unter Windows betreibt, da hier standardmäßig kein SMTP-Server vorausgesetzt werden kann).</p>
<a class="reference external image-reference" href="img/04-04.png/image_view_fullscreen"><img alt="img/04-04.png" class="original" src="img/04-04.png" /></a>
<p>Abbildung 4.4. Einrichten des Mailservers</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Das Objekt <tt class="docutils literal">MailHost</tt> ist ein Zope-Objekt, auf das man mit dem ZMI zugreifen kann. Dieses Objekt kann momentan noch keine Identifizierung mit dem Server vornehmen. Wenn das nötig sein sollte, müssen Sie die Einstellungen auf dem Server ändern.</p>
</div>
</div>
<div class="section" id="fehlermeldungen-protokollieren">
<h4>Fehlermeldungen protokollieren</h4>
<p>Im Fehlerprotokoll werden Fehler festgehalten, die in einer Plone-Site möglicherweise auftreten. Dazu gehören Fehler wie Page Not Found (404), Autorisierungsfehler usw. Es dient nicht dazu, Fehler in Formularen abzufangen. Wenn z.B. jemand in einem Feld keinen Wert eingibt, wo einer eingegeben werden muss, so wird das hier nicht festgehalten. Das ist kein Fehler, denn es wird vom Validierungs-Framework abgefangen. Dieses Fehlerprotokoll soll nur mögliche interne Server-Fehler abfangen.</p>
<p>Klicken Sie in der Plone-Schnittstelle auf <em>Plone Konfiguration</em> und dann auf  <em>Fehlerprotokoll</em>, um die Fehler auf der Plone-Site zu sehen. Klicken Sie auf den Fehler in der Liste (sofern es eine gibt), um den Fehler zu sehen. Abbildung 4.5 zeigt einen Fehler, der beim fehlerhaften Ausfüllen des Formulars für die E-Mail-Einstellungen auftrat. Es ist eine lange Seite, die einen kompletten Python-Traceback sowie die Eingangsabfrage enthält.</p>
<a class="reference external image-reference" href="img/04-05.png/image_view_fullscreen"><img alt="img/04-05.png" class="original" src="img/04-05.png" /></a>
<p>Abbildung 4.5. Ein Fehlerbeispiel</p>
<p>Im Fehlerlisten-Formular sehen Sie folgende Einstellungen:</p>
<ul class="simple">
<li><strong>Anzahl der Fehler, die gespeichert werden</strong>: Dies ist die Anzahl der auf dem Bildschirm anzuzeigenden Fehler. Der Standardwert dafür beträgt 20.</li>
<li><strong>Fehler ins Event-Logfile kopieren</strong>: Hiermit werden alle Fehler in eine Fehlerprotokolldatei kopiert. Ohne diese Kopie wird nichts dauerhaft über diese Fehler festgehalten. Standardmäßig ist dies ausgewählt.</li>
<li><strong>Fehlertypen ignoriert</strong>: Dies ist eine Liste von Fehlertypen, die ignoriert werden sollen (einer pro Zeile). Voreingestellt sind <em>Unauthorized</em>, <em>NotFound</em> und <em>Redirect</em>.</li>
</ul>
<p>Jeden Fehler können Sie protokollieren und auf dem Schirm anzeigen. Das bedeutet: Wenn ein Benutzer Ihre Site besucht und es tritt ein Fehler auf, dann können Sie im Fehlerprotokoll nachsehen, was passiert ist. Die Fehler bestehen aus den drei Komponenten Fehlertyp, Fehlerwert (der String, der erklärt, wann ein Fehler auftritt) und Traceback. Die beiden ersten werden dem Benutzer auf der Standardfehlerseite angezeigt (siehe Abbildung 4.6).</p>
<a class="reference external image-reference" href="img/04-06.png/image_view_fullscreen"><img alt="img/04-06.png" class="original" src="img/04-06.png" /></a>
<p>Abbildung 4.6. Ein Beispiel für eine Fehlermeldung</p>
<p>Wenn ein Benutzer einen Fehler meldet, enthält der Bericht oft eine Nachricht mit dem Fehlernamen und einem Fehlerwert darin. Wenn der Benutzer nichts tun darf und ein Unauthorized-Fehler oder ein Page Not Found (404) auftritt, dann bekommen Sie eine speziell angepasste Fehlerseite statt einer Standardseite angezeigt (siehe Abbildung 4.6). Folgende Standardfehlertypen kommen vor:</p>
<ul class="simple">
<li><strong>Unauthorized</strong>: Dieser Fehler tritt ein, wenn ein Benutzer kein Recht hat, eine Funktion auszuführen.</li>
<li><strong>NotFound</strong>: Dieser Fehler tritt ein, wenn das Element, auf das ein Benutzer zugreifen möchte, nicht existiert.</li>
<li><strong>Redirect</strong>: Dies ist ein Fehler, der eine HTTP-Weiterleitung (Hypertext Transfer Protocol) auslösen kann.</li>
<li><strong>AttributeError</strong>: Dieser Fehler wird ausgelöst, wenn ein Objekt ein Attribut nicht besitzt.</li>
<li><strong>ValueError</strong>: Dieser Fehler tritt auf, wenn ein gegebener Wert nicht korrekt ist und vom Validierungs- oder einem anderen Framework nicht abgefangen wird.</li>
</ul>
</div>
</div>
<div class="section" id="das-look-and-feel-von-plone-anpassen">
<h3>Das Look-and-Feel von Plone anpassen</h3>
<p>Die folgenden Abschnitte beschreiben weitere mögliche Anpassungen. Fast alle davon erfordern den Zugriff auf das ZMI.</p>
<div class="section" id="mehr-uber-portlets">
<h4>Mehr über Portlets</h4>
<p>Auf einer Plone-Site sehen Sie standardmäßig drei Spalten, links, in der Mitte und rechts. Die mittlere Spalte enthält den Inhalt des gerade angezeigten Objekts. Hier befindet sich die meiste Benutzerfunktionalität für das Hinzufügen, Bearbeiten, Formularerstellen usw. Die beiden Spalten links und rechts enthalten eine Reihe von Kästen, die Informationen anzeigen. Jeder davon ist ein so genanntes <em>Portlet</em>. Eine Variable bestimmt, welche Portlets zu einem gewissen Zeitpunkt angezeigt werden. Am besten versteht man diese Portlets, wenn man sich die Standard-Portlets anschaut, die auf einer Plone-Site vorhanden sind. Die Parameter für die Portlets finden Sie im Portal-Objekt. Darauf können Sie zugreifen, wenn Sie ins ZMI gehen und sicherstellen, dass Sie in der Plone-Wurzel-Site sind, und auf den <em>Properties</em>-Reiter klicken. Dann wird eine Liste von Eigenschaften geöffnet, darunter auch <em>left_slots</em>, <em>right_slots</em> und <em>document_action_slots</em> (siehe Abbildung 4.7).</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">In früheren Versionen von Plone wurden Portlets auch <em>Slots</em> genannt. Das ist jedoch ein häufig gebrauchter Begriff, der auch bei Seiten-Templates verwendet wird. Deswegen wurde er in Version 2 in <em>Portlets</em> geändert. An manchen Stellen im Code oder im Text wird vielleicht noch <em>Slots</em> verwendet. An diesen Stellen bedeutet der Begriff <em>Slots</em> dasselbe wie <em>Portlets</em>.</p>
</div>
<a class="reference external image-reference" href="img/04-07.png/image_view_fullscreen"><img alt="img/04-07.png" class="original" src="img/04-07.png" /></a>
<p>Abbildung 4.7. Standardeigenschaften von Portlets</p>
<p>Die <em>left_slots</em>- bzw. <em>right_slots</em>-Eigenschaften gelten für Portlets jeweils im linken bzw. rechten Teil der Seite. Die Portlets werden von oben nach unten in der Reihenfolge angezeigt, in der sie in diesen Eigenschaften aufgelistet werden. Allerdings verfügen die meisten Portlets über Code, der sicherstellt, dass sie nur dann angezeigt werden, wenn es Sinn macht. Ein Login-Portlet macht z.B. keinen Sinn, wenn der Benutzer schon angemeldet ist. In dem Fall ist das Login-Portlet zwar in der Portlet-Liste enthalten, wird aber nur bei Bedarf angezeigt.</p>
<p>Alle Portlet-Werte sind tatsächlich spezielle Werte, nämlich TALES-Pfadausdrücke (Template Attribute Languages Expression Syntax), die in Kapitel 5 detailliert behandelt werden. Site-Entwickler können eigene Portlets zu einer Site hinzufügen, indem sie einfache Makros und Seiten-Templates erstellen. Folgende Standard-Portlets gibt es:</p>
<ul class="simple">
<li><strong>left_slots</strong>: Dazu gehören Portlets zur Navigation, zum Login und ähnliche.</li>
<li><strong>right_slots</strong>: Dazu gehören Portlets wie die Revisionsliste, Nachrichten, Termine, Aktuelle Artikel und der Kalender. Alle verfügbaren Portlets werden nicht standardmäßig in Plone konfiguriert. Die folgenden Abschnitte  beschreiben Portlet-Slots in Plone. Jeder Abschnitt beschreibt ein Portlet und zeigt, wie es aussieht. Dann gebe ich den Pfadausdruck an, den Sie brauchen, um es zur <em>slots</em>-Eigenschaft hinzuzufügen, so dass es in Ihrer Plone-Site erscheint.</li>
</ul>
<p>Um etwa links das Kalender-Portlet anzuzeigen, geben Sie in der <em>left_slots</em>-Eigenschaft <strong>here/portlet_calendar/macros/portlet</strong> ein und klicken auf <em>Save Changes</em>. Wenn Sie es aus der <em>right_slots</em>-Eigenschaft entfernen möchten, können Sie die gleiche Zeile aus der <em>right_slots</em>-Eigenschaft entfernen und wieder auf <em>Save Changes</em> klicken.</p>
<div class="section" id="kalender">
<h5>Kalender</h5>
<p>Das Kalender-Portlet ist eines der Standard-Portlets. Es zeigt rechts auf einer Plone-Seite einen Kalender an. Dieses Portlet zeigt veröffentlichte Termine für den jeweiligen Monat in einem kleinen Kalender an. Das Kalender-Portlet erscheint auch dann, wenn es keine Termine gibt. Mit dem Werkzeug <tt class="docutils literal">portal_calendar</tt> können Sie den Kalender im ZMI weiter konfigurieren (siehe Abbildung 4.8).</p>
<a class="reference external image-reference" href="img/04-08.png/image_view_fullscreen"><img alt="img/04-08.png" class="original" src="img/04-08.png" /></a>
<p>Abbildung 4.8. Kalender-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_calendar/macros/portlet</em>.</p>
</div>
<div class="section" id="termine">
<h5>Termine</h5>
<p>Das Termine-Portlet zeigt eine Liste veröffentlichter kommender Termine an. Wenn Sie diesen Eintrag in der Portlet-Liste haben, wird das Portlet nur dann angezeigt, wenn es solche Termine gibt (siehe Abbildung 4.9).</p>
<a class="reference external image-reference" href="img/04-09.png/image_view_fullscreen"><img alt="img/04-09.png" class="original" src="img/04-09.png" /></a>
<p>Abbildung 4.9. Termine-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_events/macros/portlet</em>.</p>
</div>
<div class="section" id="favoriten">
<h5>Favoriten</h5>
<p>In der oberen linken Ecke eines Plone-Dokuments sehen Sie ein Plone-Icon. Benutzer können darauf klicken, um einen Favoriten hinzuzufügen. Ein <em>Favorit</em> ähnelt einem Bookmark oder Link auf die Seite, die Sie aufrufen möchten. Dieser Favorit wird jedoch auf der Plone-Site gespeichert. Abbildung 4.10 zeigt das Icon, mit dem ein Favorit hinzugefügt wird.</p>
<a class="reference external image-reference" href="img/04-10.png/image_view_fullscreen"><img alt="img/04-10.png" class="original" src="img/04-10.png" /></a>
<p>Abbildung 4.10. Das Icon zum Erstellen eines Favoriten</p>
<p>Favoriten werden zum Benutzerordner eines Benutzers hinzugefügt und werden im Favoriten-Portlet angezeigt, zusammen mit einem Link, der dazu dient, sie zu organisieren (siehe Abbildung 4.11). Die gezeigten Favoriten sind solche, die der Benutzer gespeichert hat. Das heißt, selbst wenn Sie diesen Eintrag in Ihrer Portlet-Liste haben, wird das Portlet nur dann angezeigt, wenn Sie auch wirklich Favoriten haben.</p>
<a class="reference external image-reference" href="img/04-11.png/image_view_fullscreen"><img alt="img/04-11.png" class="original" src="img/04-11.png" /></a>
<p>Abbildung 4.11. Favoriten-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_favorites/macros/portlet</em>.</p>
</div>
<div class="section" id="anmeldung">
<h5>Anmeldung</h5>
<p>Das Einloggen-Portlet zeigt das Anmeldeformular an, über das sich ein Benutzer mit seinem Benutzernamen und Passwort anmelden kann. Wenn er sein Passwort vergessen hat, hat er die Möglichkeit, es sich per E-Mail schicken zu lassen. Auch wenn dieses Portlet in der Portlet-Liste ist, wird es nur dann angezeigt, falls der Benutzer noch nicht angemeldet ist (siehe Abbildung 4.12).</p>
<a class="reference external image-reference" href="img/04-12.png/image_view_fullscreen"><img alt="img/04-12.png" class="original" src="img/04-12.png" /></a>
<p>Abbildung 4.12. Einloggen-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_login/macros/portlet</em>.</p>
</div>
<div class="section" id="navigation">
<h5>Navigation</h5>
<p>Das Navigation-Portlet zeigt einen einfachen Baum aller Ordner an der aktuellen Position in Baumform an. Es bietet ein mächtiges und einfaches Navigationswerkzeug. Das Navigation-Portlet ist extrem anpassbar. Sie können es mit einem Klick auf <em>portal_properties</em> und <em>navtree_properties</em> im ZMI ändern, was im Abschnitt &quot;Navigation-Portlet ändern&quot; weiter unten behandelt wird (siehe Abbildung 4.13).</p>
<a class="reference external image-reference" href="img/04-13.png/image_view_fullscreen"><img alt="img/04-13.png" class="original" src="img/04-13.png" /></a>
<p>Abbildung 4.13. Navigation-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_navigation/macros/portlet</em>.</p>
</div>
<div class="section" id="nachrichten">
<h5>Nachrichten</h5>
<p>Das Nachrichten-Portlet zeigt die Liste der letzten Nachrichten mit Links darauf an (siehe Abbildung 4.14). Auch dann, wenn Sie dieses Portlet in der Portlet-Liste haben, wird es nur dann angezeigt, wenn irgendwelche Nachrichten veröffentlicht wurden. Die Nachrichten einer Site sind auch über einen Klick im <em>Nachrichten</em>-Reiter verfügbar.</p>
<a class="reference external image-reference" href="img/04-14.png/image_view_fullscreen"><img alt="img/04-14.png" class="original" src="img/04-14.png" /></a>
<p>Abbildung 4.14. Nachrichten-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_news/macros/portlet</em>.</p>
</div>
<div class="section" id="aktuelle-artikel">
<h5>Aktuelle Artikel</h5>
<p>Das Aktuelle Artikel-Portlet listet die zuletzt auf der Site veröffentlichten Artikel seit dem Zeitpunkt auf, an dem Sie zum letzten Mal angemeldet waren (siehe Abbildung 4.15). Auch wenn es keine solchen Artikel gibt, wird es angezeigt.</p>
<a class="reference external image-reference" href="img/04-15.png/image_view_fullscreen"><img alt="img/04-15.png" class="original" src="img/04-15.png" /></a>
<p>Abbildung 4.15. Aktuelle Artikel-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_recent/macros/portlet</em>.</p>
</div>
<div class="section" id="dazu-passend">
<h5>Dazu passend</h5>
<p>Das Dazu passend-Portlet zeigt eine Liste von Artikeln an, die zu dem Artikel passen, den Sie gerade sehen, was anhand der Stichwörter dazu bestimmt wird. Wenn ein passender Artikel ein Link auf eine andere Website ist, wird er in einer eigenen Liste externer Ressourcen angezeigt. Auch dann, wenn dieses Portlet in der Portlet-Liste ist, wird es nur dann angezeigt, wenn es passende Artikel gibt (siehe Abbildung 4.16).</p>
<a class="reference external image-reference" href="img/04-16.png/image_view_fullscreen"><img alt="img/04-16.png" class="original" src="img/04-16.png" /></a>
<p>Abbildung 4.16. Dazu passend-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_related/macros/portlet</em>.</p>
</div>
<div class="section" id="revisionsliste">
<h5>Revisionsliste</h5>
<p>Das Revisionsliste-Portlet zeigt eine Liste von Artikeln an, die im Zustand <em>Überprüfen</em> sind und darauf warten, geprüft zu werden. Es wird nur dann angezeigt, wenn der angemeldete Benutzer die Rolle des Redakteurs hat und es zu prüfende Artikel gibt.</p>
<a class="reference external image-reference" href="img/04-17.png/image_view_fullscreen"><img alt="img/04-17.png" class="original" src="img/04-17.png" /></a>
<p>Abbildung 4.17. Revisionsliste-Portlet</p>
<p>Der Ausdruck, der hinzugefügt werden muss, lautet <em>here/portlet_review/macros/portlet</em>.</p>
<div class="sidebar">
<p class="first sidebar-title">Buch-Website: Slots ändern</p>
<!-- For the Plone book site, most of the slots on the right side made no sense. This book has no events, so the calendar and event slots weren't needed. I expect new things to be added to the site, but really they'll be minimal once the site is complete. So I decided to remove all the right slots for my site. I did this by going to the portal root object in the ZMI and clicking the Properties object. Then I deleted all the content in the *right_slots* property. The navigation, login, and related portlets, which normally occur on the left side, are all useful to me, so I kept those. -->
<p>Für die Website zum Buch machen die meisten der Slots auf der rechten Seite keinen Sinn. Zu diesem Buch gibt es keine Termine, d.h., die Kalender- und Termine-Slots rechts sind nicht notwendig. Ich rechne zwar mit neuen Dingen, die noch zur Site hinzugefügt werden, aber es werden sehr wenige sein, sobald die Site einmal fertig ist. Also habe ich mich entschlossen, alle rechten Slots auf meiner Site zu entfernen. Das habe ich so gemacht, dass ich im ZMI zum Portal-Wurzelobjekt gegangen bin und auf das <em>Eigenschaften</em>-Objekt geklickt habe. Dann habe ich alle Inhalte in der Eigenschaft <em>right_slots</em> gelöscht. Die anderen Portlets, Navigation, Login und Dazu passend, die normalerweise links vorkommen, sind für mich alle von Nutzen, daher habe ich sie behalten.</p>
<!-- Here's how the portlet properties for the Plone book site look at this point: -->
<p>So sehen im Moment die Portlet-Eigenschaften der Plone-Buchsite aus:</p>
<a class="last reference external image-reference" href="img/04-17a.png/image_view_fullscreen"><img alt="img/04-17a.png" class="original" src="img/04-17a.png" /></a>
</div>
</div>
<div class="section" id="verschiedene-portlets-in-verschiedenen-teilen-ihrer-site">
<h5>Verschiedene Portlets in verschiedenen Teilen Ihrer Site</h5>
<p>Die Zope-Datenbank, auf der Plone basiert, verfügt über eine Eigenschaft namens <em>Akquisition</em>. In der einfachsten Form bedeutet das, dass bei der Suche nach einem Objekt wie <em>right_slots</em> Plone das nächstgelegene Objekt findet, das diese Eigenschaft enthält. Das heißt, dass Plone bei der Suche nach Portlets, die in der rechten Spalte angezeigt werden sollen, normalerweise das Wurzelobjekt findet und diese Portlets anzeigt.</p>
<p>Auf diese Weise können Sie die Eigenschaften im Portal-Wurzelobjekt ändern, um die ganze Site zu ändern. Sie werden vielleicht bemerken, dass es keinen Kalender gibt, wenn Sie auf den Link <em>Mein Ordner</em> klicken und zu Ihrem persönlichen Ordner gehen. Wenn Sie im ZMI auf <em>Members</em> und dann auf <em>Properties</em> klicken, werden Sie einen Eintrag für <em>right_slots</em> sehen. Für diesen Ordner besteht er aus einer leeren Liste. Wenn die Plone-Site nach einem Wert für die rechts anzuzeigenden Portlets sucht, bewegt sie sich in der Ordnerhierarchie so lange nach oben, bis sie den Ordner <tt class="docutils literal">Members</tt> erreicht. Dort findet sie den Wert <em>right_slots</em> und benutzt ihn. Da der Wert von <em>right_slot</em> im Ordner <tt class="docutils literal">Members</tt> leer ist, wenn Sie Inhalte aus <tt class="docutils literal">Members</tt> sehen, wird der rechte Slot leer sein.</p>
<p>Durch das Hinzufügen und Entfernen von Ordnereigenschaften mit Hilfe des ZMI können Site-Administratoren genau anpassen, welche Portlets auf ihren Sites erscheinen. Zum Glück ist der <em>Properties</em>-Reiter einigermaßen einfach. Sie wählen einfach den Artikel im ZMI und klicken dann auf <em>Properties</em>. Um eine Eigenschaft zu den linken oder rechten Slots hinzuzufügen, benutzen Sie das untere <em>Add</em>-Formular und vergewissern sich, dass der Eigenschaftstyp <em>lines</em> lautet.</p>
</div>
</div>
<div class="section" id="das-navigation-portlet-andern">
<h4>Das Navigation-Portlet ändern</h4>
<p>Von allen behandelten Portlets ist das Navigation-Portlet das wahrscheinlich nützlichste und am meisten nachgefragte. Wie können Sie also speziell dieses Navigation-Portlet und die Art, wie es angezeigt wird, ändern? Das Navigation-Portlet ist eine Liste von aktuellen Ordnern und Dokumenten im Navigation-Slot. Den Navigation-Slot können Sie dadurch ändern, dass Sie den Code ändern, allerdings können Sie viele Änderungen auch im ZMI vornehmen. Am Wichtigsten ist, sich daran zu erinneren, dass nur veröffentlichte Objekte im Navigationsbaum von Mitgliedern und anonymen Benutzern gesehen werden. Um die Eigenschaften des Navigationsbaums zu ändern, klicken Sie auf <em>portal_properties</em> und dann auf <em>navtree_properties</em> im ZMI.</p>
<p>Die folgende Liste ist ein Ausschnitt der verfügbaren Optionen:</p>
<ul class="simple">
<li><strong>showMyUserFolderOnly</strong>: Dies zeigt nur den Benutzerordner des angemeldeten Benutzers an. Wenn der <tt class="docutils literal">Mitglieder</tt>-Ordner in der Navigation angezeigt wird, werden nicht alle <tt class="docutils literal">Mitglieder</tt>-Ordner angezeigt. Dies ist standardmäßig ausgewählt.</li>
<li><strong>showFolderishSiblingsOnly</strong>: Wenn diese Option gewählt ist, werden nur Ordner in übergeordneten Ordnern angezeigt, sonst wird der ganze Inhalt angezeigt. Dies ist standardmäßig aus gewählt.</li>
<li><strong>showFolderishChildrenOnly</strong>: Wenn diese Option bei einem Ordner gewählt ist, werden nur Ordner im gleichen Ordner angezeigt statt alle anderen Inhaltstypen auch. Wird diese Option nicht gewählt, dann werden alle Inhalte des gerade angezeigten Ordners gezeigt. Diese Option ist standardmäßig aus gewählt.</li>
<li><strong>roleSeeUnpublishedContent</strong>: Wie bereits erwähnt wurde, wird ein Inhalt nur dann angezeigt, wenn er für Mitglieder und anonyme Benutzer veröffentlicht ist. Um nicht veröffentlichte Inhalte anzuzeigen, müssen Sie weitere Rollen zu dieser Liste hinzufügen, jeweils eine Rolle pro Zeile. Falls der Benutzer nicht sowieso schon Zugriff darauf hat, ist das nicht wünschenswert.</li>
<li><strong>croppingLength</strong>: Diese Option bestimmt, wie viele Zeichen der Namen im Navigationsbaum angezeigt werden. Der Standardwert hierfür ist 256.</li>
<li><strong>idsnotToList</strong>: Dies ist eine Liste der Artikel-IDs, die nicht angezeigt werden sollen. Setzen Sie jede ID in jeweils eine eigene Zeile. Standardmäßig ist diese Liste leer.</li>
</ul>
<p>Nehmen Sie Änderungen an dieser Liste vor, und klicken Sie dann auf <em>Save Changes</em>. Die Reihenfolge der Artikel im Navigationsbaum wird von ihrer Reihenfolge im Ordnerinhalt-Formular bestimmt. Wie Sie in Kapitel 3 gesehen haben, können die Benutzer diese Reihenfolge nach Belieben mit den Pfeilen nach oben bzw. unten ändern.</p>
<div class="sidebar">
<p class="first sidebar-title">Buch-Website: Navigationsbaum ändern</p>
<!-- For most Web sites I prefer a fuller navigation tree than the one provided by default. So, I went to the navigation tree options and deselected *showFolderishChildrenOnly* and *showFolderishSiblingsOnly*. This made the contents show up nicely; for example, here's the software folder with just a few items selected: -->
<p>Bei den meisten Websites bevorzuge ich einen umfangreicheren Navigationsbaum als den standardmäßig verfügbaren. Also bin ich zu den Optionen für den Navigationsbaum gegangen und habe <em>showFolderishChildrenOnly</em> und <em>showFolderishSiblingsOnly</em> ausgeschaltet. Dadurch wurden die Inhalte hübsch angezeigt. Hier ist z.B. der Software-Ordner mit nur einigen gewählen Artikeln:</p>
<a class="last reference external image-reference" href="img/04-17b.png/image_view_fullscreen"><img alt="img/04-17b.png" class="original" src="img/04-17b.png" /></a>
</div>
</div>
<div class="section" id="datumsformate-andern">
<h4>Datumsformate ändern</h4>
<p>In allen Portlets und auf der gesamten Site stellt Plone Datumsangaben konsistent in einem Format dar, das intern bearbeitet werden kann. Immer, wenn in Plone ein Datum angezeigt wird, wird eines von zwei Formaten aufgerufen. Diese Formate finden Sie im ZMI, indem Sie auf <em>portal_properties</em> und <em>site_properties</em> klicken. Diese Formate lauten:</p>
<ul class="simple">
<li><strong>localTimeFormat</strong>: Dies ist das Zeit-Format für Datumsangaben, die in Plone in einem Kurzformat erscheinen sollen.</li>
<li><strong>localLongTimeFormat</strong>: Dies ist das Zeit-Format für Datumsangaben, die in einem Langformat mit Sekundenangaben erscheinen sollen.</li>
</ul>
<p>Das Datumsformat basiert auf dem time-Modul von Python. Die Referenz zu den Formaten finden Sie unter <a class="reference external" href="http://www.python.org/doc/current/lib/module-time.html">http://www.python.org/doc/current/lib/module-time.html</a>. Beim Kurzformat lautet der Standardwert <tt class="docutils literal"><span class="pre">%Y-%m-%d</span></tt>, d.h. <tt class="docutils literal"><span class="pre">Jahr-Monat-Tag</span></tt> als Dezimalzahlen (z.B. <tt class="docutils literal"><span class="pre">2003-10-26</span></tt>). Beim Langformat lautet der Standardwert <tt class="docutils literal"><span class="pre">%Y-%m-%d</span> <span class="pre">%I:%M</span> %p</tt>, d.h. <tt class="docutils literal"><span class="pre">Jahr-Monat-Tag</span> Stunden:Minuten am/pm</tt> (z.B <tt class="docutils literal"><span class="pre">2003-10-26</span> 07:32 PM</tt>).</p>
<p>Hier ist eine kurze Zusammenfassung der verfügbaren Optionen:</p>
<ul class="simple">
<li><strong>%a</strong>: Abgekürzter Wochentagsname der aktuellen Lokalisierung (z.B. <tt class="docutils literal">Mon</tt>)</li>
<li><strong>%A</strong>: Voller Wochentagsname der aktuellen Lokalisierung (z.B. <tt class="docutils literal">Montag</tt>)</li>
<li><strong>%b</strong>: Abgekürzter Monatsname der aktuellen Lokalisierung (z.B. <tt class="docutils literal">Jan</tt>)</li>
<li><strong>%B</strong>: Voller Monatsname der aktuellen Lokalisierung (z.B. <tt class="docutils literal">Januar</tt>)</li>
<li><strong>%d</strong>: Tag des Monats als Dezimalzahl</li>
<li><strong>%H</strong>: Stunde (max. 24) als Dezimalzahl</li>
<li><strong>%I</strong>: Stunde (max. 12) als Dezimalzahl</li>
<li><strong>%m</strong>: Monat als Dezimalzahl</li>
<li><strong>%M</strong>: Minute als Dezimalzahl</li>
<li><strong>%S</strong>: Sekunde als Dezimalzahl</li>
<li><strong>%y</strong>: Jahr ohne Jahrhundert als Dezimalzahl</li>
<li><strong>%Y</strong>: Jahr mit Jahrhundert als Dezimalzahl</li>
</ul>
<p>Wenn der Wochentagsname im Kurzformat enthalten sein soll, können Sie das einfach dadurch erreichen, dass Sie das Kurzformat auf <tt class="docutils literal">%A, %b. %d, %y</tt> ändern. Dadurch entsteht z.B. <tt class="docutils literal">Donnerstag, Okt. 24, 02</tt>. Diese Datumsangaben werden in den Kästen links und rechts auf dem Bildschirm, in Suchergebnissen, in den Verfasserangaben usw. verwendet.</p>
</div>
<div class="section" id="stichworter-und-terminarten-hinzufugen">
<h4>Stichwörter und Terminarten hinzufügen</h4>
<p>Mit einem der Werkzeuge in Plone, <em>portal_metadata</em>, kann der Site-Administrator einige Metadaten-Elemente definieren. Plone verwendet die mit <em>portal_metadata</em> definierten Metadaten an verschiedenen Stellen.</p>
<p>Wenn Sie z.B. einen Termin hinzufügen, erhalten Sie eine Liste möglicher Terminarten. Diese Liste können Sie erweitern, wenn Sie im ZMI auf <em>portal_metadata</em> klicken und dann auf <em>elements</em> und <em>subject</em>. Dann sehen Sie  eine Wortliste (Vocabulary) für Termine mit den Themen für diesen Inhaltstyp. Diese Liste mit je einem Eintrag pro Zeile kann man ganz einfach erweitern oder bearbeiten, um die relevanten Terminarten zur Verfügung zu haben. Diese Terminarten erscheinen dann in den Formularen zum Hinzufügen und Bearbeiten von Terminen.</p>
<p>Mit <em>portal_metadata</em> kann man auch die auf einer Site verfügbaren Stichwörter angeben. In dem Formular unter <em>portal_metadata/elements/subject</em> sehen Sie auch ein Vocabulary-Formular für den Inhaltstyp <em>&lt;default&gt;</em>. Wenn Sie auf dieser Seite Einträge im <em>Vocabulary</em>-Feld hinzufügen und auf <em>Update</em> klicken, fügen Sie diese Einträge zur Liste der verfügbaren Stichwörter für <em>alle</em> Inhaltstypen hinzu.</p>
<p>Wenn Sie möchten, dass Stichwörter nur für, sagen wir, Dokumente erscheinen, dann benutzen Sie das Hinzufügen-Formular unten auf der Seite. Wählen Sie einen Inhaltstyp, und fügen Sie Wörter in die Vocabulary-Liste ein, und zwar eines pro Zeile. Daraus werden dann die einzigen Stichworte, die ein Benutzer für diesen Inhalt auswählen kann.</p>
<p>Wenn Sie als Benutzer mit Manager- oder Redakteursrechten angemeldet sind, erscheint, wenn Sie den <em>Eigenschaften</em>-Reiter eines Objekts in Plone anklicken, ein Kasten namens <em>Neues Stichwort</em>, in dem neue Stichwörter spontan hinzugefügt werden können. Diese erscheinen nicht in der Vocabulary-Liste von <em>portal_metadata</em>, aber in allen Inhaltstypen, die andere Benutzer eingeben können.</p>
</div>
<div class="section" id="standardseite-andern">
<h4>Standardseite ändern</h4>
<p>Wie in Kapitel 3 beschrieben wurde, erscheint die Standardseite eines Ordners, wenn ein Benutzer diesen Ordner anzeigt und diese Seite existiert. In älteren Versionen von Zope und Plone lautete der Name dieser Standardseite   <tt class="docutils literal">index_html</tt>. Diesen können Sie sehr oft auf Plone-Sites sehen, wo in Website-Adressen oftmals <tt class="docutils literal">index_html</tt> am Ende steht. Wenn Sie daraus eine Dateinamenserweiterung machen, die weiter verbreitet ist und leichter erkannt wird, z.B. <tt class="docutils literal">index.html</tt>, dann ist es auch einfacher, die Seite mit vorhandenen Editoren und Website-Werkzeugen zu bearbeiten.</p>
<p>In Plone können Sie eine Liste von Seiten definieren, nach denen gesucht wird, wenn eine Standardseite angezeigt werden soll (siehe Abbildung 4.18). Diese  Standardseiten heißen <tt class="docutils literal">index_html</tt>, <tt class="docutils literal">index.html</tt>, <tt class="docutils literal">index.htm</tt> und <tt class="docutils literal">FrontPage</tt>. Diese Liste setzen Sie in <tt class="docutils literal">site_properties/portal_properties/default_page property</tt>, mit je einem Namen pro Zeile. Bei der Suche nach der Standardseite sucht Plone nach jeder Seite aus dieser Liste, wobei es vorne anfängt und so lange weitersucht, bis es eine passende findet. Wenn Sie den Wert für nur einen Ordner ändern möchten, können Sie mit dem ZMI auf den Ordner zugreifen, den Reiter <em>Properties</em> anklicken und dann eine neue Listeneigenschaft namens <em>default_page</em> hinzufügen.</p>
<a class="reference external image-reference" href="img/04-18.png/image_view_fullscreen"><img alt="img/04-18.png" class="original" src="img/04-18.png" /></a>
<p>Abbildung 4.18. Einstellen von <tt class="docutils literal">index.asp</tt>  als erste Standardseite</p>
<div class="sidebar">
<p class="first sidebar-title">Wie bekommen Sie in der Nachrichtenliste einen Eintrag auf die Standardseite?</p>
<!-- Exactly how this works involves knowing the underlying machinery a little too much. For now, go to your portal root and click *Properties*. Then you need to go to the bottom of the page, complete the add new property form with the following information, and then click the Add button: -->
<!-- 1. For the Name field: **default_page** -->
<!-- 2. For the Value field: **news** -->
<!-- 3. For the Type field: **lines** -->
<p>Um zu begreifen, wie das genau funktioniert, muss man ziemlich viel von der darunter liegenden Maschinerie verstehen. Gehen Sie vorläufig einfach zu Ihrer Portal-Wurzel, und klicken Sie auf den Reiter <em>Properties</em>. Dann gehen Sie zum unteren Teil der Seite und füllen das Formular zum Hinzufügen neuer Eigenschaften mit folgenden Angaben aus. Klicken Sie anschließend auf den <em>Add</em>-Button:</p>
<ol class="arabic simple">
<li>Im Name-Feld: <strong>default_page</strong></li>
<li>Im Value-Feld: <strong>news</strong></li>
<li>Im Type-Feld: <strong>lines</strong></li>
</ol>
<!-- Now return to your Plone site. Instead of the standard home page, you'll see the news page. The news tab will still show you the news, as well, but in the following sections I'll show how to remove that. -->
<p class="last">Kehren Sie nun zu Ihrer Plone-Site zurück. Statt der Standard-Homepage sehen Sie nun die Nachrichtenseite. Der <em>Nachrichten</em>-Reiter zeigt ebenfalls weiterhin Nachrichten an, aber in den folgenden Abschnitten zeige ich Ihnen, wie Sie das abstellen.</p>
</div>
</div>
<div class="section" id="reiter-der-site-andern">
<h4>Reiter der Site ändern</h4>
<p>In einer Plone-Site beziehen sich verschiedene Reiter auf verschiedene Site-Abschnitte oder -Teile. Die Verwendung von Reitern ist ein sehr gebräuchliches Mittel beim Design von Websites und wird auch auf den Sites von Amazon, MSN und auf Plone-Sites praktiziert.</p>
<p>Es gibt zwei Arten von Reitern: <em>Portalreiter</em> und <em>Inhaltsreiter</em>. Portalreiter sind blau und erscheinen oben auf der Plone-Site. Die Standardreiter heißen  <em>Startseite</em>, <em>Nachrichten</em> und <em>Mitglieder</em>. In den folgenden Abschnitten sehen Sie, wie Sie diese Reiter anpassen können. Inhaltsreiter sind grün und erscheinen, wenn ein Artikel bearbeitet werden kann. Wie ihr Name schon sagt, beziehen sich Inhaltsreiter auf den Inhalt. Kapitel 11 zeigt, wie man diese Reiter ändern kann. Die Reiter auf einer Plone-Site entstehen aus einer Reihe von Aktionen. Das heißt, Sie müssen einen kleinen allgemeinen Abstecher zu Aktionen machen, um zu verstehen, wie Sie diese Reiter verändern können.</p>
<div class="section" id="einfuhrung-in-aktionen">
<h5>Einführung in Aktionen</h5>
<p>In Plone können gewisse Personen bestimmte Aufgaben zu verschiedenen Zeiten und an verschiedenen Orten der Site ausführen. Diese Aufgaben werden <em>Aktionen</em> genannt. Plone übersetzt sie in Reiter, Links und andere Elemente. Aktionen sind eine hochgradig konfigurierbare Art, um Navigationselemente für eine Site zu erstellen.</p>
<p>Jede Aktion verfügt über folgende Eigenschaften, die im ZMI konfiguriert werden können. Wo sie genau konfiguriert werden, hängt davon ab, wo die Aktion gespeichert ist. Hier ist eine Liste von Eigenschaften für eine Standardaktion:</p>
<ul class="simple">
<li><strong>Name</strong>: Dies ist ein benutzerfreundlicher Name für die Aktion. Er wird in der Benutzerschnittstelle oft verwendet. Wenn z.B. die Aktion als Reiter benutzt wird, entspricht dieser Wert dem Text im Reiter.</li>
<li><strong>Id</strong>: Dies ist eine eindeutige ID für die Aktion.</li>
<li><strong>Actions</strong>: Dies ist die auszuführende Aktion. Wenn die Aktion z.B. als Reiter definiert wird, wird diese Aktion beim späteren Klick auf diesen Reiter ausgeführt. Dieses Feld enthält einen TALES-Ausdruck (siehe weitere Informationen in Kapitel 5).</li>
<li><strong>Condition</strong>: Dies ist die Bedingung, die erfüllt sein muss, damit die Aktion ausgeführt wird. Wenn die Aktion z.B. als Reiter genutzt wird und diese Bedingung ist erfüllt, dann erscheint der Reiter. Dieses Feld enthält einen TALES-Ausdruck (siehe Kapitel 5).</li>
<li><strong>Permission</strong>: Dies sind die Rechte, die der Benutzer haben muss, damit er diese Aktion ausführen kann. Diese Rechte müssen gegeben sein, damit die Aktion genutzt werden kann (siehe Kapitel 9 für weitere Informationen zum Thema Sicherheit).</li>
<li><strong>Category</strong>: Hiermit werden die Aktionen kategorisiert. In Plone werden Aktionen damit voneinander unterschieden, d.h., sie werden in verschiedenen Abschnitten der Benutzerschnittstelle verwendet. Bei Portalreitern lautet der Kategoriewert <em>portal_tabs</em>.</li>
<li><strong>Visible</strong>: Das bestimmt, ob die Kategorie aktiv ist. Der Begriff <em>visible</em> wird verwendet, weil Aktionen normalerweise mit visuellen Elementen gekoppelt sind.</li>
</ul>
</div>
<div class="section" id="einfuhrung-in-die-obersten-reiter">
<h5>Einführung in die obersten Reiter</h5>
<p>In den folgenden Abschnitten werden Sie als Beispiel die Portalreiter auf zwei verschiedene Weisen ändern. Sie werden den Reiter der Startseite so ändern, dass er <em>Willkommen</em> lautet, und Sie werden den <em>Mitglieder</em>-Reiter nach links neben den <em>Nachrichten</em>-Reiter verlegen. Die Aktionen für die Portalreiter werden in dem Werkzeug <tt class="docutils literal">portal_actions</tt> gespeichert. Um sie also zu ändern, klicken Sie im ZMI auf <em>portal_actions</em>. Wie Sie in Abbildung 4.19 sehen, wird dabei eine lange Liste von standardmäßig vorhandenen Portalaktionen geöffnet. Manche dieser Aktionen werden Ihnen bekannt vorkommen, da sie für Teile der Plone-Site stehen.</p>
<a class="reference external image-reference" href="img/04-19.png/image_view_fullscreen"><img alt="img/04-19.png" class="original" src="img/04-19.png" /></a>
<p>Abbildung 4.19. Die Portal-Aktionen für Ihre Plone-Site</p>
<p>Scrollen Sie durch diese Aktionen, bis Sie den Eintrag <em>Home</em> finden, und ändern Sie das Namensfeld auf <strong>Willkommen</strong>. Scrollen Sie dann hinunter zum Seitenende, und klicken Sie auf <em>Save</em>. Zurück in der Plone-Schnittstelle werden Sie bemerken, dass der Reiter nun <em>Willkommen</em> heißt.</p>
<p>Die Reihenfolge der Reiter von links nach rechts auf der Seite wird durch die Reihenfolge von oben nach unten in der Liste der Aktionen bestimmt. Um einen Reiter zu verschieben, müssen Sie ihn in der Liste markieren und ans Seitenende zu den Buttons <em>Move Up</em> und <em>Move Down</em> scrollen. Es ist etwas umständlich, aber durch wiederholtes Markieren der Aktionen und Benutzen der Rauf- und Runter-Buttons können Sie die Reihenfolge ändern. Führen Sie das durch, und Sie werden bemerken, dass die Reiter nun in einer anderen Reihenfolge in Ihrer Plone-Site erscheinen.</p>
</div>
<div class="section" id="warum-erscheint-der-text-in-kleinbuchstaben">
<h5>Warum erscheint der Text in Kleinbuchstaben?</h5>
<p>Plone ändert mit einem Stylesheet die Schreibweise von vielen Dingen, z.B. Reitern, in Kleinbuchstaben. Um das abzustellen, können Sie das Stylesheet ändern, was später in diesem Kapitel im Abschnitt &quot;Bilder und CSS ändern&quot; beschrieben wird.</p>
<div class="sidebar">
<p class="first sidebar-title">Buch-Website: Einen neuen Reiter hinzufügen</p>
<!-- A nice navigation helper is to add a tab or remove one in the portal tabs. So, in this sidebar, you'll add a tab that points to the Software folder, and you'll remove the news and members tabs (which in my site is pointless). Return to the ZMI, and click *portal_actions*. Scroll to the bottom of the form to the add form. I filled out the form with the following values: -->
<!-- - **Software** -->
<!-- - **software_tab** -->
<!-- - **string:$portal_url/Software** -->
<!-- - **View** -->
<!-- - **portal_tabs** -->
<!-- - **selected** -->
<p>Durch das Hinzufügen oder Entfernen eines Reiters in den Portalreitern entsteht eine nette Navigationshilfe. Deswegen werden Sie an dieser Stelle einen Reiter hinzufügen, der auf den <tt class="docutils literal">Software</tt>-Ordner verweist, und außerdem werden Sie die Reiter <em>Nachrichten</em> und <em>Mitglieder</em> entfernen (die auf meiner Site unnütz sind). Gehen Sie zurück ins ZMI, und klicken Sie auf <em>portal_actions</em>. Scrollen Sie zum Seitenende bis zum <em>Hinzufügen</em>-Formular. Ich habe das Formular mit folgenden Werten ausgefüllt:</p>
<ul class="simple">
<li>Geben Sie im Name-Feld <strong>Software</strong> ein.</li>
<li>Geben Sie im ID-Feld <strong>software_tab</strong> ein.</li>
<li>Geben Sie im Action-Feld <strong>string:$portal_url/Software</strong> ein.</li>
<li>Geben Sie im Permission-Feld <strong>View</strong> ein.</li>
<li>Geben Sie im Category-Feld <strong>portal_tabs</strong> ein.</li>
<li>Geben Sie im Visible-Feld <strong>selected</strong> ein.</li>
</ul>
<!-- Further, I found the actions for news and members and deselected Visible (not forgetting to hit Save, of course). Returning to the Plone interface, you'll now see those new tabs. The key value here is *Action*, which is a TALES expression. These are discussed fully in Chapter 5. The *Action* value points to the URL of the folder you're pointing to; in my case, it's *Software* and is in the root of my Plone site. Hence, the expression is *string:$portal_url/Software*. -->
<p class="last">Weiterhin habe ich die Aktionen für Nachrichten und Mitglieder gefunden und dort <em>Visible</em> ausgeschaltet (und natürlich auf <em>Save</em> geklickt). Zurück in der Plone-Schnittstelle sehen Sie nun die neuen Reiter. Der Schlüsselwert hierbei ist <em>Action</em>, ein TALES-Ausdruck. Diese Ausdrücke werden in Kapitel 5 ausführlich beschrieben. Der <em>Action</em>-Wert enthält den URL des Ordners, auf den Sie zeigen, in meinem Fall <tt class="docutils literal">Software</tt>, und ist in der Wurzel meiner Plone-Site abgelegt. Daher lautet der Ausdruck <tt class="docutils literal"><span class="pre">string:$portal_url/Software</span></tt>.</p>
</div>
</div>
<div class="section" id="icons-fur-ein-dokument-andern">
<h5>Icons für ein Dokument ändern</h5>
<p>Wenn Sie eine Liste von Links oder Optionen in einer Plone-Site betrachten, ist es sehr wahrscheinlich, dass diese Liste von einer Serie von Aktionen produziert wird. Und wenn es keine Aktionen sind, dann ist es Code; aber viele der wesentlichen Eigenschaften der Plone-Schnittstelle werden aus den Einstellungen im ZMI dynamisch generiert. Zwei weitere Beispiele für Aktionen sind Dokument- und Site-Aktionen.</p>
<p>Die Site-Aktionen erscheinen in der oberen rechten Ecke als Links zum Ändern der Schriftgröße. Diese Links könnten alles Mögliche sein, aber zufällig verweisen sie auf einige clientseitige Scriptfunktionen. Auch diese Links werden in <em>portal_actions</em> konfiguriert und sind nichts weiter als Aktionen, die zu einer anderen Kategorie gehören. Wenn Sie sich die Aktionen in <em>portal_actions</em> anschauen, werden Sie am unteren Seitenende diese drei Aktionen sehen. Sie haben die Kategorie <em>site_actions</em>. Wenn Sie sie entfernen möchten, müssen Sie lediglich die <em>Visible</em>-Option ausschalten. Die Icons stammen aus dem Werkzeug <em>portal_actionicons</em>, das lediglich ein weiteres einfaches Werkzeug ist, das das Icon auf die Aktion abbildet. Unter <em>portal_actionicons</em> sehen Sie eine Übereinstimmung bei <em>normal_text</em> für <em>site_actions</em>, das auf ein Icon passt (siehe Abbildung 4.20).</p>
<a class="reference external image-reference" href="img/04-20.png/image_view_fullscreen"><img alt="img/04-20.png" class="original" src="img/04-20.png" /></a>
<p>Abbildung 4.20. Site-Aktionen</p>
<p>Analog dazu befinden sich Dokumentationen in <em>portal_actions</em>. Sie haben die Kategorie <em>document_actions</em>. Auch hier können Sie die Reihenfolge, Icons und den Text ändern und Icons in der Schnittstelle hinzufügen oder entfernen, indem Sie diese Aktionen bearbeiten.</p>
</div>
</div>
<div class="section" id="bilder-und-css-andern">
<h4>Bilder und CSS ändern</h4>
<p>Das Look-and-Feel einer Plone-Site ist ein umfangreiches Thema, das drei Kapitel für sich in Anspruch nimmt, Kapitel 5 bis 7. Die folgenden Abschnitte behandeln die Grundlagen und zeigen, wie man schnell ein paar Änderungen vornimmt. Sie erklären aber nicht, wie alles funktioniert.</p>
<p>Eine  <em>Skin</em> besteht aus einer Menge von CSS, Bildern, Templates und Scripts, die zusammen ein Look-and-Feel für den Benutzer bilden. Eine Skin dient dazu, das Aussehen und damit das Look-and-Feel einer Website ändern zu können, ohne den Inhalt ändern zu müssen.</p>
<div class="section" id="skins-andern">
<h5>Skins ändern</h5>
<p>Sie können die Standard-Skin einer Site ändern, indem Sie das Formular <em>Einstellungen des Aussehens</em> benutzen, zu dem Sie vom Control Panel aus gelangen. Eine Plone-Site können Sie auf einige verschiede Weisen darstellen, indem Sie verschiedene Farben, Stylesheets und Templates auf einer Site benutzen.</p>
<p>Dieses Formular enthält folgende Optionen:</p>
<ul class="simple">
<li><strong>Voreingestelltes Aussehen</strong>: Dies ist die Standard-Skin, wenn Benutzer auf die Site zugreifen. Es gibt nur eine standardmäßig vorgegebene Skin, nämlich Plone Default.</li>
<li><strong>Flexibilität des Aussehens</strong>: Damit bestimmen Sie, ob es Benutzern erlaubt ist, ihre eigene Skin auszuwählen. Wenn ja, dann kann ein Benutzer in seinen Voreinstellungen eine neue Skin wählen. Standardmäßig ist diese Option gewählt.</li>
<li><strong>Wirkungsdauer des Cookies für das Aussehen</strong>: Wenn ein Benutzer eine Skin wählen kann, dann sollten Sie diese Option wählen, damit das entsprechende Cookie beliebig lange gültig ist. Damit wird ein Benutzer immer diese Skin sehen, wenn er sich bei der Site anmeldet. Diese Option ist standardmäßig nicht gewählt.</li>
</ul>
<p>Nehmen Sie die gewünschten Änderungen vor, und klicken Sie auf <em>Speichern</em>, um diese zu bestätigen. Um die Performance auf der Site zu verbessern, können Sie ein Caching von Bildern und Stylesheets verwenden. Um sicherzustellen, dass Sie die neue Skin auch wirklich so sehen, wie sie sein soll, löschen Sie den Cache Ihres Browsers (beim Internet Explorer drücken Sie dazu <strong>Strg+F5</strong>).</p>
</div>
<div class="section" id="ein-anderes-logo-einstellen">
<h5>Ein anderes Logo einstellen</h5>
<p>Das Logo einer Plone-Site zu ändern und statt des Plone-Logos ein anderes zu verwenden, ist eine leichte Übung, aber die einzelnen Schritte können ein wenig verwirrend sein. Daher sollten Sie sie sorgfältig befolgen.</p>
<p>Zuerst greifen Sie auf das ZMI zu, klicken auf <em>portal_skins</em>, dann auf <em>plone_images</em> und schließlich auf <em>logo.jpg</em>. Danach wird die Seite zu diesem Objekt geöffnet. Sie sollte in etwa so aussehen wie in Abbildung 4.21.</p>
<a class="reference external image-reference" href="img/04-21.png/image_view_fullscreen"><img alt="img/04-21.png" class="original" src="img/04-21.png" /></a>
<p>Abbildung 4-21. Das Standard-Logo</p>
<p>Dieses Objekt stellt das Logo dar, wie es in Zope benutzt wird. In Abbildung 4.21 können Sie deutlich Informationen zum Bild sehen, seine Größe, seinen Typ und seinen Platz im Dateisystem. In der Seitenmitte befindet sich der <em>Customize</em>-Button, den Sie nun anklicken. Dabei wird eine Kopie des Objekts namens <tt class="docutils literal">logo.jpg</tt> im <tt class="docutils literal">custom</tt>-Ordner erzeugt (siehe Abbildung 4.22).</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Wenn Sie an dieser Stelle eine Fehlermeldung wegen einer fehlerhaften Anfrage erhalten, gehen Sie zurück zu <tt class="docutils literal">portal_skins/custom</tt>. Sie werden ein Objekt namens <tt class="docutils literal">logo.jpg</tt> sehen. Klicken Sie auf dieses Objekt. Es kann nur ein Objekt namens <tt class="docutils literal">logo.jpg</tt> im <tt class="docutils literal">custom</tt>-Ordner geben, und die Fehlermeldung warnt Sie  davor, dass diese Prozedur bereits ausgeführt worden ist. Falls Sie das ursprüngliche Objekt anpassen möchten (mit anderen Worten, wenn Sie diese Schritte wiederholen möchten), müssen Sie das Objekt innerhalb von <tt class="docutils literal">custom</tt> löschen.</p>
</div>
<a class="reference external image-reference" href="img/04-22.png/image_view_fullscreen"><img alt="img/04-22.png" class="original" src="img/04-22.png" /></a>
<p>Abbildung 4.22. Das angepasste Bild</p>
<p>Diese Seite sieht vielleicht ähnlich wie die vorherige Seite in Abbildung 4.21 aus, aber es gibt eine Reihe von Unterschieden. Erstens: Wenn Sie in die obere linke Ecke der Seite schauen, werden Sie bemerken, dass sich der <em>meta_type</em> und der Ort dieses Objekts verändert haben. Nun sind Sie sind nicht mehr in <tt class="docutils literal">portal_skins/plone_images/logo.jpg</tt>, sondern Sie sind in <tt class="docutils literal">portal_skins/custom/logo.jpg</tt>. Zweitens können Sie nun einen <em>Browse</em>-Button sehen, mit dem Sie ein Bild auswählen und hochladen können, d.h., Sie können dieses Bild ändern. Klicken Sie auf diesen Button, um Ihr neues Bild zu finden, und klicken Sie auf <em>Save</em>, um die Änderung zu bestätigen. In Abbildung 4.23 füge ich als Beispiel ein kanadisches Plone-Logo hinzu.</p>
<a class="reference external image-reference" href="img/04-23.png/image_view_fullscreen"><img alt="img/04-23.png" class="original" src="img/04-23.png" /></a>
<p>Abbildung 4.23. Das kanadische Plone-Logo</p>
<p>Wenn Sie nun zur Plone-Schnittstelle zurückgehen, sehen Sie, dass sich das Bild verändert hat. Um sicherzugehen, dass Sie das neue Bild sehen, sollten Sie den Cache Ihres Browsers löschen (beim Internet Explorer drücken Sie dazu <strong>Strg+F5</strong>).</p>
</div>
<div class="section" id="was-tun-sie-wenn-ihr-bild-nicht-im-jpeg-format-vorliegt">
<h5>Was tun Sie, wenn Ihr Bild nicht im JPEG-Format vorliegt?</h5>
<p>Zope bestimmt den MIME-Typ (Multipurpose Internet Mail Extensions) nicht aus der Dateierweiterung, sondern aus dem Inhalt. Daher können Sie ein GIF-Bild unter <tt class="docutils literal">logo.jpg</tt> hochladen, und es funktioniert trotzdem, weil der korrekte MIME-Typ <tt class="docutils literal">image/gif</tt> angewendet wird. Um die Verwirrung in Grenzen zu halten, möchten Sie das Bild aber vielleicht doch <tt class="docutils literal">logo.gif</tt> oder <tt class="docutils literal">logo.png</tt> nennen.</p>
</div>
<div class="section" id="css-code-andern">
<h5>CSS-Code ändern</h5>
<p>CSS bestimmt einen Großteil des Look-and-Feel Ihrer Website, darunter die Reiter, Bilder, Kästen und das allgemeine Layout. Die Tatsache, dass Plones CSS durchgehend angepasst werden kann, bedeutet, dass die Benutzer viele Aspekte einer Site mit ein paar Stylesheets vollkommen umgestalten können.</p>
<p>Noch einmal: Kapitel 7 beschreibt, was die einzelnen Elemente bewirken. In diesem Abschnitt zeige ich Ihnen nur schnell, wie Sie den CSS-Code einer Plone-Site ändern können. Zuerst gehen Sie ins ZMI. Dort klicken Sie auf <em>portal_skins</em>, dann auf <em>plone_styles</em> und schließlich auf <em>ploneCustom.css</em>. Danach erscheint eine Seite zu diesem Objekt. Dieses Stylesheet ist ziemlich einfach, ja, es ist sogar leer. Plone macht nämlich Gebrauch von der Kaskadierungseigenschaft von CSS. Da der HTML-Code für Plone zuerst <tt class="docutils literal">plone.css</tt> importiert und dann <tt class="docutils literal">ploneCustom.css</tt>, überschreiben alle Änderungen in letzterem das Standard-Stylesheet. Warum ist das eine gute Idee? Weil es bedeutet, dass Sie kleine inkrementelle Änderungen an <tt class="docutils literal">ploneCustom.css</tt> vornehmen können, ohne das Kern-Stylesheet kaputtzumachen oder zu verändern.</p>
<p>Um also das Objekt <tt class="docutils literal">ploneCustom.css</tt> anzupassen, klicken Sie auf <em>portal_skins</em>, dann auf <em>plone_styles</em> und auf <em>ploneCustom.css</em>. Danach klicken Sie auf den <em>Customize</em>-Button. Wieder wurde dieses Objekt angepasst, und Sie bemerken, dass Sie nicht mehr unter <tt class="docutils literal">portal_skins/plone_styles/ploneCustom.css</tt>, sondern nun unter <tt class="docutils literal">portal_skins/custom/ploneCustom.css</tt> sind. Da Dateiobjekte nun über das Web bearbeitet werden können, können Sie das Stylesheet direkt über das Web bearbeiten.</p>
<p>Ändern Sie als Beispiel den Hintergrund so, dass er ein Bild in der Mitte enthält (das ist nicht unbedingt die beste Benutzerschnittstelle, aber ein klares Beispiel dafür, wie Sie den CSS-Code anpassen können). Zuerst müssen Sie ein Bild in Plone hochladen. Dazu klicken Sie auf <em>portal_skins</em>, auf <em>custom</em> und dann auf den <em>Add</em>-Button, und schließlich wählen Sie ein Bild aus, wie in Abbildung 4.24 zu sehen ist.</p>
<a class="reference external image-reference" href="img/04-24.png/image_view_fullscreen"><img alt="img/04-24.png" class="original" src="img/04-24.png" /></a>
<p>Abbildung 4.24. Ein neues Bild hinzufügen</p>
<p>Als Datei habe ich ein Bild gewählt, das ich im Web gefunden habe (und das auch auf der Website zum Plone-Buch verfügbar ist), aber Sie können ein beliebiges Bild nehmen. Vergewissern Sie sich, dass die ID des Bildes <tt class="docutils literal">background.gif</tt> ist, wie in Abbildung 4.25 zu sehen ist.</p>
<a class="reference external image-reference" href="img/04-25.png/image_view_fullscreen"><img alt="img/04-25.png" class="original" src="img/04-25.png" /></a>
<p>Abbildung 4.25. Prüfen des neuen Bildes</p>
<p>Nun müssen Sie den CSS-Code ändern, damit er auf das neue Bild zeigt. Den CSS-Code haben Sie schon angepasst, gehen Sie also zu  <tt class="docutils literal">portal_skins/custom/ploneCustom.css</tt> zurück, und ändern Sie den Text von</p>
<pre class="literal-block">
/* DELETE THIS LINE AND PUT YOUR CUSTOM STUFF HERE */
</pre>
<p>in Folgendes:</p>
<pre class="literal-block">
body {
   background-image: url(background.jpg);
   background-repeat: no-repeat;
   background-position: center;
}
</pre>
<p>Klicken Sie auf <em>Save Changes</em>, um die Änderungen an dieser Datei nicht zu verlieren. Gehen Sie dann zur Plone-Schnittstelle zurück. Wenn alles gut ging, sollten Sie das neue Bild sehen (siehe Abbildung 4.26).</p>
<a class="reference external image-reference" href="img/04-26.png/image_view_fullscreen"><img alt="img/04-26.png" class="original" src="img/04-26.png" /></a>
<p>Abbildung 4.26. Das neue Hintergrundbild</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch5.rst">
    <title>5. Einführung in einfaches Plone-Templating</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch5.rst</link>
    <description>Plone kombiniert drei Technologien bei der Erstellung einer Webseite. Python und Seiten-Templates erzeugen HTML, das an den Browser gesendet wird. Dort wird mit einigen CSS-Stylesheets die hübsche Seite dargestellt, mit der Sie nunmehr vertraut sind. In diesem Kapitel und in Kapitel 6 stellen die beiden ersten Elemente, der Python-Code und die Seiten-Templates, den Hauptgegenstand der Betrachtung dar.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Einführung in einfaches Plone-Templating</h2>
<p>Plone kombiniert drei Technologien bei der Erstellung einer Webseite. Python und Seiten-Templates erzeugen HTML, das an den Browser gesendet wird. Dort wird mit einigen CSS-Stylesheets die hübsche Seite dargestellt, mit der Sie nunmehr vertraut sind. In diesem Kapitel und in Kapitel 6 stellen die beiden ersten Elemente, der Python-Code und die Seiten-Templates, den Hauptgegenstand der Betrachtung dar.</p>
<p>Um zu verstehen, wie man ein Plone-Template erzeugt und bearbeitet, muss man zunächst einiges über die wesentlichen zugrunde liegenden Konzepte lernen. Manche davon sind für Plone besonders einmalig, und obwohl sie große Vorteile haben, braucht man einige Zeit, um sich daran zu gewöhnen.</p>
<p>In diesem Kapitel beginne ich damit, dass ich das Objekt-Publishing behandle. Ich erkläre, wie man mit Objekten innerhalb von Plone interagiert. Danach erkläre ich, wie man Ausdrücke zusammensetzt. Sobald Sie mit diesen beiden Konzepten vertraut sind, beschreibe ich, wie Plone-Seiten wirklich zusammengestellt werden. Am Ende dieses Kapitels werden Sie eine neue Seite in Ihrer Plone-Site erstellen, die jene Techniken verwendet, die Sie bislang kennen gelernt haben.</p>
<p>Dieses Kapitel macht etwas mehr Sinn, wenn Sie mit Python schon etwas vertrauter sind. Ich werde aber durchgehend das Konzept hinter dem Code erklären, d.h., auch wenn Sie kein Python verstehen, sollten Sie damit zurechtkommen. Der Rest des Buchs verweist auf TALES- (Template Attribute Language Expression Syntax) und Script(Python)-Objekte, d.h., Sie sollten sich in diesem Kapitel die Zeit nehmen, sich mit ihnen vertraut zu machen. Aber Sie sollten bereits einen guten Start haben: TALES habe ich schon im vorigen Kapitel eingeführt, weil es bei der Erzeugung von Portlets und Aktionen benutzt wird.</p>
<div class="section" id="die-zugrunde-liegende-templating-maschinerie">
<h3>Die zugrunde liegende Templating-Maschinerie</h3>
<p>Ein direktes Eintauchen in die Arbeitsweise von Plone-Templates würde Sie vermutlich ein wenig verwirren, daher gehe ich erst einmal die Templating-Maschinerie durch, auf der sie aufbauen. Idealerweise wäre das etwas, worüber Sie sich keine Sorgen machen müssten, aber in der Praxis habe ich festgestellt, dass es die erste Hürde ist, über die man stolpert, wenn man anfängt, Plone zu benutzen.</p>
<p>Plone ist insofern einmalig, als alles in Plone ein Objekt ist. Seien Sie beruhigt, wenn Sie mit dem Konzept von Objekten nicht so recht vertraut sind. Es steckt nicht viel dahinter: Ein <em>Objekt</em> ist einfach ein Ding, das ein bestimmtes Verhalten hat. Jedes Objekt verfügt über Methoden, die Sie aufrufen können. Eine Computer-Maus könnte beispielsweise Methoden namens <tt class="docutils literal">bewegen</tt>, <tt class="docutils literal">klicken</tt> und <tt class="docutils literal">rechtsklicken</tt> haben.</p>
<p>Ein Dokument in Plone ist ein Objekt eines bestimmten Typs. Das heißt nichts weiter, als dass das Dokument nicht einfach nur aus statischem Text besteht, sondern ein bisschen komplizierter und weitaus nützlicher ist. In Plone hat ein Dokument z.B. eine Methode namens <tt class="docutils literal">description</tt>, die Ihnen eine Beschreibung liefert, die der Benutzer hinzugefügt hat. Bei der Benutzung des Templating-Systems werden Sie sehen, dass alles ein Objekt ist. Zuerst werden Sie aber ein paar Grundprinzipien beim Objekt-Publishing kennen lernen.</p>
<div class="section" id="einfuhrung-in-das-objekt-publishing">
<h4>Einführung in das Objekt-Publishing</h4>
<p>In Plone veröffentlichen Sie eigentlich Objekte, die sich in Zope befinden. Die meisten davon sind Objekte, die persistent in der Objektdatenbank gespeichert sind. Das Konzept ist komplizierter als bei den üblichen CGI-Umgebungen (Common Gateway Interface), in denen Sie ein Script ausführen und ihm eine Reihe von Anfragevariablen übergeben. In Plone ist alles ein Objekt, in Zope ist alles ein Objekt, und auch in Python ist alles ein Objekt. Bis jetzt habe ich es vermieden, das Wort <em>Objekt</em> zu verwenden, und habe stattdessen Wörter wie <em>Template</em>, <em>Script</em> und <em>Eintrag</em> benutzt, aber all das sind wirklich Objekte, die sich lediglich unterschiedlich verhalten.</p>
<p>Wenn Sie von Plone einen URL (Uniform Resource Locator) abfragen, wird ein Objekt aus der Umgebung aufgerufen. Das macht Plone so, dass es den URL in einen Pfad übersetzt. Das heißt: Falls der URL <tt class="docutils literal">/Plone/login_form</tt> lautet, versucht Plone, aus diesem URL einen Pfad zu bestimmen und all die Objekte darauf in der Datenbank zu finden. Dabei findet es das Objekt <tt class="docutils literal">Plone</tt> und darin dann ein Objekt  <tt class="docutils literal">login_form</tt>. Dieses Absuchen des Pfads wird auch  <em>Traversierung</em> genannt. Im Grunde genommen traversiert Zope diese Objekte und ruft das letzte Objekt im Pfad auf.</p>
<p>Wenn Zope das Objekt <tt class="docutils literal">login_form</tt> aufruft, wird dieses Objekt in seinem Kontext ausgeführt. Den Begriff  <em>Kontext</em> werden Sie im Zusammenhang mit Plone oft hören. Es ist lediglich der aktuelle Kontext des ausgeführten Objekts. In diesem Fall ist es <tt class="docutils literal">/Plone</tt>. Der Kontext verändert sich ständig, während Sie sich in einer Plone-Site bewegen. Falls Sie den URL <tt class="docutils literal">/Plone/Members/login_form</tt> in einem Browser aufgerufen haben, wäre der Kontext <tt class="docutils literal">/Plone/Members</tt>.</p>
<p>Wie oben erwähnt wurde, können Sie per <em>Traversierung</em> mit Hilfe eines Programms genauso auf Objekte in Plone zugreifen, wie Sie es mit einem URL tun. Das ähnelt dem Zugriff auf Dateien in einem Dateisystem. Das heißt: Wenn Sie unter Windows auf ein Bild in <tt class="docutils literal">Eigene Dateien</tt> zugreifen möchten, würden Sie ein Verzeichnis eingeben wie <strong>C:\Dokumente und Einstellungen\andym\Eigene Dateien\Mein Portrait.jpg</strong>. Auf ein Objekt in Plone könnten Sie zugreifen, indem Sie <strong>Members/andy/Mein Portrait.jpg</strong> eingeben. Das würde auch funktionieren, sofern Sie eine Reihe von Ordnern und Objekten wie folgt hätten:</p>
<pre class="literal-block">
Members
  |_ andy
     |_  Mein Portrait.jpg
</pre>
<p>In der Dateisystemversion gehen Sie auf der Festplatte des Rechners Verzeichnis für Verzeichnis durch. In Plone geschieht das Gleiche, nur ist es so, dass <tt class="docutils literal">Members</tt> und <tt class="docutils literal">andy</tt> Objekte sind.</p>
<p>Ein Unterschied besteht darin, dass Zope die Schreibweise beachtet. Unter Windows können Sie <strong>Mein Portrait.jpg</strong> oder <strong>mein portrait.jpg</strong> eingeben. In Plone wird das aber nicht funktionieren, denn Sie müssen die gleiche Schreibweise wie in der Objekt-ID verwenden. Aus diesem Grund empfiehlt es sich, in allen URLs Kleinbuchstaben zu verwenden, damit Ihre Benutzer weniger Fehler machen können.</p>
<p>Plone und Zope haben eine Besonderheit namens <em>Akquisition</em> zu diesem Publishing-System hinzugefügt. Das Konzept dahinter ist das des Enthaltenseins: Objekte befinden sich in anderen Objekten, die als  <em>Container</em> bezeichnet werden. Im vorigen Beispiel ist das Objekt <tt class="docutils literal">andy</tt> ein Container innerhalb des <tt class="docutils literal">Members</tt>-Containers innerhalb des Plone-Site-Containers (der sich seinerseits innerhalb des Zope-Application-Containers befindet).</p>
<p>In einer normalen objektorientierten Umgebung erbt ein Objekt Verhalten von seinem Elternobjekt. In Plone und Zope erbt ein Objekt auch Verhalten von seinem Container. Dabei wandert ein Objekt durch eine Container-Hierarchie, um herauszufinden, wo es dieses Verhalten findet.</p>
<p>Nehmen Sie z.B. den Zugriff auf <tt class="docutils literal">Members/andy/Mein Portrait.jpg</tt>. Was geschähe, wenn das Objekt <tt class="docutils literal">Ein Bild.jpg</tt> nicht im Ordner <tt class="docutils literal">andy</tt> vorhanden wäre, sondern weiter oben in der Hierarchie? Nun, dank Akquisition würde es für Sie gefunden. Betrachten Sie die folgende Hierarchie:</p>
<pre class="literal-block">
Members
   |_ andy
   |_ Mein Portrait.jpg
</pre>
<p>In diesem Fall würde Plone, wenn Sie die URL ausführen, diese bis <tt class="docutils literal">andy</tt> traversieren und dann versuchen, dort <tt class="docutils literal">Mein Portrait.jpg</tt> zu finden, was aber in dem Container nicht existiert. Daher würde es in der Container-Hierarchie suchen, also im Ordner <tt class="docutils literal">Members</tt>, wo es <tt class="docutils literal">Mein Portrait.jpg</tt> findet und zurückgibt. Als Ergebnis sieht der Benutzer das Bild wie sonst auch.</p>
<p>Wenn Sie das jedoch mit dem vorigen Beispiel vergleichen, in dem das Bild im Ordner <tt class="docutils literal">andy</tt> enthalten war, würden Sie folgende Unterschiede feststellen:</p>
<ul class="simple">
<li>Erstens ist der Kontext der gleiche, auch wenn sich das Objekt an einem anderen Ort befindet. Der Kontext basiert auf dem Ort, von dem aus das Objekt aufgerufen wird.</li>
<li>Zweitens ist der Container ein anderer, und der Container von <tt class="docutils literal">Mein Portrait.jpg</tt> ist nun ein anderer, nämlich <tt class="docutils literal">Members</tt> und nicht <tt class="docutils literal">andy</tt>.</li>
</ul>
<p>Worauf will ich also hinaus? Nun, jetzt können Sie ein Objekt in die Wurzel einer Plone-Site setzen, und ein beliebiges anderes Objekt kann darauf zugreifen, weil ersteres mit Hilfe der Akquisition gefunden wird.</p>
<p>Auch wenn das alles vermutlich Sinn macht, sollten Sie wissen, dass Akquisition ziemlich kompliziert werden kann, besonders bei der Suche in der Kontexthierarchie (die vorkommen kann). Wenn Sie gern mehr darüber lernen möchten, können Sie eine hervorragende Abhandlung zum Thema Akquisition von Jim Fulton, dem Systemarchitekten von Zope, unter <a class="reference external" href="http://www.zope.org/Members/jim/Info/IPC8/AcquisitionAlgebra/index.html">http://www.zope.org/Members/jim/Info/IPC8/AcquisitionAlgebra/index.html</a> lesen.</p>
</div>
<div class="section" id="einfuhrung-in-template-ausdrucke">
<h4>Einführung in Template-Ausdrücke</h4>
<p>Bevor Sie ins Zope Page Templates-System eintauchen, müssen Sie TALES verstehen. Oft müssen Sie in einer Anwendung Ausdrücke schreiben, die dynamisch ausgewertet werden können. Das sind keine Scripts, sondern  <em>einzeilige</em> einfache Ausdrücke, die etwas Einfaches mit einer Zeile Code machen können.</p>
<p>Ein Ausdruck wird mit einer Reihe von lokalen Variablen ausgewertet, die an ihn übergeben werden. Diese Variablen werden durch das bestimmt, was den Ausdruck aufruft. Vom Workflow wird eine Reihe Variablen übergeben, und das Zope Page Templates-System übergibt eine Reihe weiterer. Vorläufig werde ich Beispiele verwenden, die einen <tt class="docutils literal">context</tt> haben. Wie Sie sich hoffentlich erinnern, ist <tt class="docutils literal">context</tt> der Kontext, in dem ein Objekt aufgerufen wird.</p>
<p>Bislang haben Sie schon einige TALES-Ausdrücke wie <tt class="docutils literal"><span class="pre">string:${portal_url}/Software</span></tt> gesehen. Dies ist aber nur ein Beispiel aus einem weiten Spektrum möglicher Ausdrücke. Die Hauptanwendung von TALES liegt bei den Zope Page Templates, dem System, mit dem Plone HTML generiert. Auch wenn der Name vermuten lässt, dass es nur in Templates zu gebrauchen ist, verwenden viele Werkzeuge in Plone diese Syntax bei einfachen Ausdrücken, z.B. Aktionen, Workflow und Sicherheit. Es gibt verschiedene Arten von Ausdrücken, die ich einzeln durchgehen werde.</p>
<div class="section" id="pfadausdrucke-verwenden">
<h5>Pfadausdrücke verwenden</h5>
<p>Der Pfadausdruck ist der Standardausdruck, der auch am meisten verwendet wird. Anders als alle anderen Ausdrücke benötigt er kein Präfix, um den Typ des Ausdrucks anzugeben. Der Ausdruck umfasst einen oder mehrere Pfade. Die Pfade sind mit einem senkrechten Strich (<tt class="docutils literal">|</tt>) voneinander getrennt. Jeder Pfad besteht aus einer Reihe von Variablen, die mit Schrägstrichen (<tt class="docutils literal">/</tt>) voneinander getrennt sind. Hier sind einige einfache Beispiele:</p>
<pre class="literal-block">
context/message
context/ordnerA/title
context/Members/andy/Mein Portrait.jpg
</pre>
<p>Bei der Auswertung des Ausdrucks wird der Pfad an den Schrägstrichen aufgeteilt. Dann wird ausgehend vom linken Wert versucht, das Objekt, die Methode oder den Wert zu finden. Dieses Objekt wird dann auf den aktuellen Stack gelegt, und es geht mit dem nächsten Wert weiter. Das wird so lange wiederholt, bis das Ende des Ausdrucks erreicht ist oder kein passender Wert gefunden wird. Falls das gefundene Objekt ein Python-Dictionary oder ein Abbildungsobjekt ist, wird dieser Wert des Dictionarys aufgerufen. Eine nette Eigenschaft von Pfadausdrücken ist, dass <em>/</em> das einzige reservierte Zeichen ist. Das heißt, Namen dürfen Leerzeichen und Punkte enthalten und können dennoch ausgewertet werden.</p>
<p>Wenn das Ende erreicht ist, wird dieses Objekt aufgerufen, sofern das möglich ist. Handelt es sich um ein nicht aufrufbares Objekt, wird der String-Wert des Objekts geholt und zurückgegeben. Falls bei dieser Suche zu irgendeinem Zeitpunkt ein Fehler auftritt (am häufigsten passiert es, dass das verlangte Attribut nicht existiert), wird mit dem nächsten Alternativausdruck weitergemacht, sofern einer vorhanden ist. Einen Alternativausdruck können Sie nach einem senkrechten Strich angeben.</p>
<p>Beispiel:</p>
<pre class="literal-block">
context/ordnerA/title|context/ordnerB/title
</pre>
<p>Im vorigen Beispiel wird der Titel von <tt class="docutils literal">ordnerA</tt> ausgegeben, wenn er existiert, oder der Titel von <tt class="docutils literal">ordnerB</tt>, falls der erste nicht existiert. Dieser Vorgang wird für jeden Ausdruck so lange wiederholt, bis es keine weiteren Ausdrücke gibt oder einer davon erfolgreich ausgewertet werden kann.</p>
</div>
<div class="section" id="not-ausdrucke-verwenden">
<h5>Not-Ausdrücke verwenden</h5>
<p>Ein negierter Ausdruck hat das Präfix <tt class="docutils literal">not:</tt> und invertiert einfach die Auswertung des TALES-Ausdrucks, der dem Präfix folgt. Da das Zope Page Templates-System keine <tt class="docutils literal">if</tt>-Anweisung kennt, können Sie hiermit das Gegenteil einer vorherigen Bedingung testen.</p>
<p>Beispiel:</p>
<pre class="literal-block">
not: context/message|nothing
</pre>
</div>
<div class="section" id="nocall-ausdrucke-verwenden">
<h5>Nocall-Ausdrücke verwenden</h5>
<p>Wenn ein Pfadausdruck das letzte Element im Pfad erreicht, wird dieses, wenn möglich, standardmäßig aufgerufen. Das Präfix <tt class="docutils literal">nocall:</tt> verhindert genau das. Ein Nocall-Ausdruck wird in Plone selten verwendet, hat aber gelegentlich seinen Zweck. Sie können ihn z.B. dafür benutzen, auf ein anderes Objekt zu verweisen, es aber nicht auszugeben.</p>
<p>Beispiel:</p>
<pre class="literal-block">
nocall: context/einBild
</pre>
</div>
<div class="section" id="string-ausdrucke-verwenden">
<h5>String-Ausdrücke verwenden</h5>
<p>Mit String-Ausdrücken können Sie Text und Variablen in einem Ausdruck mischen. Alle String-Ausdrücke beginnen mit dem Präfix <tt class="docutils literal">string:</tt>. Dies ist eine nützliche und recht häufig benutzte Funktion. Der Text darf alles enthalten, was in einem Attribut erlaubt ist, d.h. im Wesentlichen alphanumerische Zeichen und Leerzeichen. In Text können Variablen enthalten sein, denen ein Dollar-Zeichen (<tt class="docutils literal">$</tt>) vorangestellt sein muss. Hier einige Beispiele:</p>
<pre class="literal-block">
string: Dies ist ein langer String
string: Dies ist der $title
</pre>
<p>Im letzten Beispiel wird die Variable <tt class="docutils literal">$title</tt> ausgewertet. Die Variable kann ein beliebiger Pfadausdruck sein. Wenn sie <tt class="docutils literal">/</tt> enthält, muss die Variable in  <tt class="docutils literal">{}</tt> verpackt werden, um den Anfang und das Ende des Ausdrucks deutlich zu machen.</p>
<p>Beispiel:</p>
<pre class="literal-block">
string: Dies ist der ${context/einBild/title}.
</pre>
<p>Falls ein Dollar-Zeichen im Text geschützt werden muss, setzen Sie ein weiteres Dollar-Zeichen vor das zu schützende Dollar-Zeichen.</p>
<p>Beispiel:</p>
<pre class="literal-block">
string: In $$US kostet es ${context/meinDing/kosten}.
</pre>
</div>
<div class="section" id="python-ausdrucke-verwenden">
<h5>Python-Ausdrücke verwenden</h5>
<p>Python-Ausdrücke werten eine Zeile Python-Code aus. Alle Python-Ausdrücke beginnen mit dem Präfix <tt class="docutils literal">python:</tt> und enthalten eine Zeile Python-Code.</p>
<p>Beispiel:</p>
<pre class="literal-block">
python: 1 + 2
</pre>
<p>Der Python-Code wird unter dem gleichen Sicherheitsmodell ausgewertet, das auch ein Script(Python)-Objekt benutzt, wie in Kapitel 6 dargestellt wird. Aus diesen Gründen sollte der Python-Code einfach und auf Präsentationsfunktionen beschränkt sein, z.B. auf die Formatierung von Strings und Zahlen oder auf das Aufstellen einfacher Bedingungen.</p>
<p>Weiterhin gilt, dass fast alle anderen erwähnten TALES-Ausdrücke in Python verpackt und aufgerufen werden können. Folgendes sind die entsprechenden Ausdrücke:</p>
<ul class="simple">
<li><strong>path(string)</strong>: Wertet einen Pfadausdruck aus.</li>
<li><strong>string(string)</strong>: Wertet einen String-Ausdruck aus.</li>
<li><strong>exists(string)</strong>: Wertet einen String-Ausdruck aus.</li>
<li><strong>nocall(string)</strong>: Wertet einen Nocall-Ausdruck aus.</li>
</ul>
<p>Beispielsweise ist folgender Code:</p>
<pre class="literal-block">
python: path('context/Members')
</pre>
<p>äquivalent zu folgendem:</p>
<pre class="literal-block">
context/Members
</pre>
<p>Einige Funktionen wurden auch hinzugefügt, um Entwicklern ihre Arbeit bequemer zu machen. Die Funktion <em>test</em> erwartet drei Parameter: eine auszuwertende Anweisung und die Bedingungen für <em>true</em> und <em>false</em>. Die Anweisung wird ausgewertet, und es wird der entsprechende Wert zurückgegeben.</p>
<p>Beispiel:</p>
<pre class="literal-block">
python: test(1 - 1, 0, 1)
</pre>
<p>Die Funktion <tt class="docutils literal">same_type</tt> erwartet zwei Parameter und stellt fest, ob sie gleich sind. Beispiel:</p>
<pre class="literal-block">
python: same_type(irgendwas, '')
</pre>
<p>Manche Entwickler raten davon ab, Python-Code im Zope Page Templates-System zu benutzen, weil das bedeutet, dass Logik zu den Präsentations-Templates hinzugefügt wird. Für Entwickler ist es oftmals hilfreich, sich bei jedem hinzugefügten Python-Happen zu fragen, ob dieser Code nicht besser ausgegliedert und in ein separates Script(Python)-Objekt verlagert werden sollte. Das heißt nicht, dass Sie jedes Stück Python-Code ausgliedern sollen, sondern nur, dass Sie darüber nachdenken sollten, bevor Sie etwas hinzufügen.</p>
<div class="sidebar">
<p class="first sidebar-title">Buch-Website: Noch einmal Aktionen</p>
<!-- In Chapter 4, you added an action for pointing to the software part of the site so it appeared as a portal tab. In that action, you added in the string expression *string: ${portal_url}/Software*. This may make a bit more sense now that I've explained the variable *portal_url*. This is the URL to your portal, which may vary depending upon if you're using virtual hosting. It does this by using acquisition to acquire the *portal_url* object and insert the resulting value into the string. The result is that you'll always get an absolute link to the *Software* folder. -->
<p class="last">In Kapitel 4 haben Sie eine Aktion zum Verweisen auf den Software-Teil der Site in Form eines Portalreiters hinzugefügt. In jener Aktion hatten Sie im String-Ausdruck <tt class="docutils literal">string: <span class="pre">${portal_url}/Software</span></tt> hinzugefügt. Das macht jetzt vielleicht etwas mehr Sinn, nachdem ich die Variable <tt class="docutils literal">portal_url</tt> erklärt habe. Dies ist der URL Ihres Portals, der variieren kann, falls Sie virtuelles Hosting verwenden. Das wird mit Hilfe von Akquisition gemacht, um das Objekt <tt class="docutils literal">portal_url</tt> zu erhalten und das Ergebnis in den String einzufügen. Als Ergebnis erhalten Sie immer einen absoluten Link auf den Ordner <tt class="docutils literal">Software</tt>.</p>
</div>
<div class="sidebar">
<p class="first sidebar-title">Aufgepasst: Python und Strings mischen</p>
<!-- I've seen newcomers mixing up Python and strings a few times. All the expressions are different. In other words, you can't place path-like expressions inside a Python expression. For example, the expression *python: here/Members + "/danae"* doesn't make sense. The entire expression is interpreted as Python, so Plone will try to divide *here* by *Members*, and you'll get errors. This is an ideal situation to use a string expression (which lets you do variable substitution), so the variable contain a path expression. So, you could use *string: ${here/Members}/danae*. -->
<p class="last">Ich habe öfter beobachtet, wie Neulinge Python und Strings mischen. Alle Ausdrücke sind verschieden, d.h., Sie können keine pfadähnlichen Ausdrücke in einen Python-Ausdruck setzen. So macht z.B. der Ausdruck <tt class="docutils literal">python: here/Members + &quot;/danae&quot;</tt> keinen Sinn. Der gesamte Ausdruck wird als Python-Code interpretiert, d.h., Plone versucht, <tt class="docutils literal">here</tt> von <tt class="docutils literal">Members</tt> zu trennen, und Sie erhalten einen Fehler. Das ist die ideale Gelegenheit, einen String-Ausdruck zu verwenden (in dem Sie Variablen ersetzen können). Das heißt, die Variable enthält einen Pfadausdruck. Sie könnten also <tt class="docutils literal">string: <span class="pre">${here/Members}/danae</span></tt> benutzen.</p>
</div>
</div>
</div>
</div>
<div class="section" id="das-zope-page-templates-system-verwenden">
<h3>Das Zope-Page Templates System verwenden</h3>
<p>Nun da Sie das Object-Publishing und Ausdrücke verstehen, können Sie sich aufs Eingemachte im System stürzen, auf Zope Page Templates. Das ist das Templating-System, mit dem Plone HTML generiert.</p>
<p>Es sind sehr viele Systeme zum Generieren von HTML verfügbar, und zu den bekannteren gehören JavaServer Pages, Active Server Pages und PHP. Den Benutzern dieser anderen Systeme sei gesagt: Das Zope Page Templates-System sieht zu Beginn sehr merkwürdig aus, aber Sie werden schnell sehen, dass es ein sehr mächtiges System ist.</p>
<p>Das einfachste Template sieht ungefähr wie folgt aus:</p>
<pre class="literal-block">
&lt;p tal:content=&quot;here/message&quot;&gt;Der Titel&lt;/p&gt;
</pre>
<p>Wenn der Wert von <tt class="docutils literal">message</tt> gleich <tt class="docutils literal">Hello, World!</tt> wäre, dann wäre Folgendes eine Ausgabe bei der Darstellung des Templates:</p>
<pre class="literal-block">
&lt;p&gt;Hello, World!&lt;/p&gt;
</pre>
<p>Im Moment überspringe ich einige der Feinheiten und zeige, was hier passiert ist. Ein normaler Absatz wurde in HTML geschrieben, aber der Inhalt dieses Absatzes ist nicht der Text, der in der Ausgabe gezeigt wird. Zu dem öffnenden Tag des Absatzes wurde das Attribut <tt class="docutils literal">tal:content</tt> hinzugefügt, und der Ausdruck <tt class="docutils literal">here/message</tt> wurde diesem Attribut zugewiesen. Als Absatzinhalt wurde aber der Wert der Variablen <tt class="docutils literal">message</tt> ausgegeben (in diesem Fall, <tt class="docutils literal">Hello, World!</tt>).</p>
<p>Das Template wird zur Laufzeit ausgewertet, und das Attribut <tt class="docutils literal">tal:content</tt> wird aufgerufen. Der <tt class="docutils literal">tal</tt>-Teil steht für Template Attribute Language, eine Sprache, die eine Reihe von Befehlen enthält, z.B. <tt class="docutils literal">content</tt>. All diese Befehle werden Sie später noch sehen. Mit ihnen können Sie fast alles tun, was Sie mit HTML-Tags tun möchten. Sie können Schleifen erzeugen, Tags ändern, Attribute ändern, Tags entfernen usw. Bevor das Template ausgeführt wird, wird es als gültiges XHTML (Extensible HTML) angezeigt und wird in einem Editor als Absatz mit diesem Text angezeigt.</p>
<p>All diese Page Templates sind gültiges XHTML. XHTML ist ein Standard für HTML und ist gültiger XML-Code (Extensible Markup Language). Das bedeutet, Sie müssen sich an folgende Regeln halten:</p>
<ul class="simple">
<li>Alle Tags müssen in Kleinbuchstaben geschrieben sein.</li>
<li>Attribute müssen immer in Anführungszeichen stehen (z.B. <tt class="docutils literal">&lt;input <span class="pre">type=&quot;checkbox&quot;</span> <span class="pre">checked=&quot;1&quot;</span> /&gt;</tt>).</li>
<li>Leere Elemente müssen terminiert werden (z.B. <tt class="docutils literal">&lt;br /&gt;</tt>, nicht <tt class="docutils literal">&lt;br&gt;</tt>).</li>
</ul>
<p>Um eine Seite als XHTML zu definieren, müssen Sie eine DOCTYPE-Deklaration angeben und den XML-Namespace benutzen, der im <tt class="docutils literal">html</tt>-Tag gesetzt ist. Plone verwendet folgende Deklaration oben auf jeder Seite:</p>
<pre class="literal-block">
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
    &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en&quot; lang=&quot;en&quot;&gt;
</pre>
<p>Weitere Informationen zur XHTML-Spezifikation finden Sie unter  <a class="reference external" href="http://www.w3.org/TR/xhtml1/#xhtml">http://www.w3.org/TR/xhtml1/#xhtml</a>.</p>
<div class="sidebar">
<p class="first sidebar-title">Noch ein System zur HTML-Generierung?</p>
<!-- In the first few years of the Web, programmers were the prime creators of HTML. Programmers rapidly threw together systems to generate HTML programmatically so they could get on with their real jobs. With tools such as Perl's CGI modules, programmers could write complicated server-side code for content. -->
<p>In den Anfangsjahren des Webs waren Programmierer die maßgeblichen Erzeuger von HTML. Diese Programmierer haben schnell Systeme zusammengewürfelt, die HTML mit Hilfe eines Programms generierten, damit sie wieder ihre eigentlichen Jobs machen konnten. Mit solchen Werkzeugen wie den CGI-Modulen in Perl konnten Programmierer komplizierten serverseitigen Code für ihre Inhalte schreiben.</p>
<!-- However, soon everybody was generating content, and the process had to be made easier. This brought about the wave of escape coding languages. These languages used a special kind of HTML markup that was processed to produce output. As mentioned, some of the most popular are Active Server Pages, JavaServer Pages, and even whole languages based on the concept, such as PHP. Zope followed this trend with Document Template Markup Language (DTML). -->
<p>Sehr bald wurden Inhalte aber von allen möglichen Leuten erstellt, und dieser Vorgang musste vereinfacht werden. Zu diesem Zweck entstand eine Welle von Sprachen, die Code schützen sollten. Diese Sprachen verwendeten eine spezielle Art von HTML-Auszeichnung, die verarbeitet wurde, um eine Ausgabe zu produzieren. Wie schon erwähnt wurde, sind Active Server Pages und JavaServer Pages einige der verbreitetsten, und es basieren ganze Sprachen wie PHP auf diesem Konzept. Zope folgte diesem Trend mit DTML (Document Template Markup Language).</p>
<!-- These systems take HTML and intersperse it with custom tags such as *<% .. %>* or *<dtml-... />*. This system was popular because it was easy to understand, and users who already knew basic HTML could grasp the idea of a few more tags. Designers could ignore the content of these tags and let the programmers deal with them. Programmers could alter the relevant code parts without upsetting the content. -->
<p>Diese Systeme nehmen HTML und vermischen es mit eigenen Tags wie <tt class="docutils literal">&lt;% .. %&gt;</tt> oder <tt class="docutils literal"><span class="pre">&lt;dtml-...</span> /&gt;</tt>. Dieses Konzept war beliebt, weil es leicht zu verstehen war, und Benutzer mit grundlegenden HTML-Kenntnissen begriffen schnell, was die paar neuen Tags leisten sollten. Designer konnten den Inhalt solcher Tags ignorieren und den Programmierern überlassen. Programmierer konnten die relevanten Codeteile ändern, ohne den Inhalt zu ruinieren.</p>
<!-- However, these systems have the following problems: -->
<!-- * The HTML templates can be hard to scale as more and more content gets added to the script. Pages quickly become huge and hard to manage. -->
<!-- * Logic and content aren't neatly separated. They can be separated with some of these systems; however, the ability to intersperse any HTML with a piece of programming code is too easy. Often, content, presentation, and logic become one large, entangled mess. -->
<!-- * Pages can't be easily edited. Often pages or templates come with the note "just leave these bits alone..."cause editing them would break the code. What You See Is What You Get (WYSIWYG) editors can be set to not alter some tags, but they can easily break others. In large organizations, users with different roles all have to edit the same page. -->
<!-- * It can be hard to see a default result. Take, for example, a database query that shows the result in a table. How can a designer see how that would look without actually running the code? -->
<p>Allerdings weisen solche Systeme folgende Probleme auf:</p>
<ul class="simple">
<li>Die HTML-Templates skalieren mitunter nur sehr schwer, wenn immer mehr Inhalte zum Script hinzukommen. Die Seiten werden schnell riesig lang und schwierig zu verwalten.</li>
<li>Logik und Inhalt sind nicht hübsch voneinander getrennt. Mit manchen dieser Systeme können sie zwar getrennt werden, aber die Möglichkeit, beliebiges HTML mit einem Stück Programmcode zu vermengen, ist zu verlockend. Oftmals wird aus Inhalt, Präsentation und Logik ein großer verwickelter Klumpen.</li>
<li>Seiten können nicht einfach bearbeitet werden. Oftmals enthalten Seiten oder Templates eine Bemerkung der Art &quot;diesen Teil nicht ändern...&quot;, weil bei einer Änderung der Code drumherum nicht mehr funktioniert. WYSIWYG-Editoren (What You See Is What You Get) kann man so einstellen, dass sie einige Tags nicht ändern, aber sie können leicht andere kaputtmachen. In großen Organisationen müssen Benutzer mit verschiedenen Rechten alle die gleiche Seite ändern dürfen.</li>
<li>Es kann sehr schwer sein, ein Standardergebnis zu sehen. Nehmen Sie z.B. eine Datenbankabfrage, deren Ergebnis in einer Tabelle dargestellt wird. Wie soll ein Designer sehen, wie das aussehen würde, ohne den Code auszuführen?</li>
</ul>
<!-- For these reasons, the Zope Page Templates system was created. Page templates present a novel approach; instead of providing another method of escape coding, code is added to existing tag attributes. Not only is the Zope Page Templates system free and open source, it doesn't require Zope. Currently, versions of the system exist in Python, Perl, and Java. -->
<p class="last">Aus diesen Gründen wurde das Zope Page Templates-System erfunden. Page Templates stellen einen neuen Ansatz dar: Anstatt eine weitere Methode zum Schutz von Code zu erfinden, wird der Code zu den vorhandenen Tag-Attributen hinzugefügt. Das Zope Page Templates-System ist nicht nur frei verfügbar und Open Source, es kommt auch ohne Zope aus! Im Moment existieren Versionen dieses Systems für Python, Perl und Java.</p>
</div>
<div class="section" id="einfuhrung-in-page-templates-und-inhalt">
<h4>Einführung in Page Templates und Inhalt</h4>
<p>Wie Sie nun wissen, ist Plone ein Content-Management-System, bei dem Benutzer über das Web Inhalte zur Plone-Site hinzufügen können. Diese Inhaltsobjekte werden innerhalb von Plone gespeichert und dann mit Hilfe von Page Templates für alle Welt dargestellt.</p>
<p>Kommen wir zum früheren Beispiel des Zugriffs auf <tt class="docutils literal">/Members/andy/Mein Portrait.jpg</tt> zurück. Ich werde nun beschreiben, was mit dem Inhalt in Plone wirklich passiert. Zuerst findet Plone das Objekt <tt class="docutils literal">Mein Portrait.jpg</tt> und ruft es auf. Es wird deswegen aufgerufen, weil keine spezielle Methode auf dem Objekt aufgerufen wird. Wenn ein Inhaltstyp aufgerufen wird, wird ein bestimmtes Template gefunden und dargestellt. Der Kontext für dieses Template wird das Bild sein, auf das Sie zugreifen möchten, und das Template ist jenes für dieses Bild.</p>
<p>Wenn eine andere Aktion auf dem Bild aufgerufen würde, z.B. <tt class="docutils literal">/Members/andy/Mein Portrait/image_edit</tt>, dann würde die Aktion <tt class="docutils literal">image_edit</tt> für dieses Objekt gesucht, und das entsprechende Template würde zurückgegeben werden. Wie das funktioniert, beschreibe ich in Kapitel 11 im Detail.</p>
<p>In allen Templates in Plone werden Sie also eine Referenz zu <tt class="docutils literal">here</tt> oder <tt class="docutils literal">context</tt> sehen. Das ist der Kontext des Inhalts, auf den zugegriffen wird. In einem Template können Sie nun <tt class="docutils literal">context/irgendwas oder wasanderes</tt> schreiben, und das wird das <tt class="docutils literal">irgendwas oder wasanderes</tt> sein, das relativ zu dem Stück Inhalt und nicht zum Template gesucht wird. Jetzt werden Sie Ihr erstes Template in Plone erstellen.</p>
</div>
<div class="section" id="erstellen-ihres-ersten-page-templates">
<h4>Erstellen Ihres ersten Page Templates</h4>
<p>Die Standardmethode, ein Page Template zu erstellen, ist das Zope Management Interface (ZMI). Weil das bedeutet, das Template im Textbereich eines Webbrowsers zu bearbeiten, ist das ZMI leider auch die unangenehmste Methode, die man als Entwickler benutzen kann. Verglichen mit den meisten Editoren, bietet dieser Textbereich sehr wenige Funktionen. Es fehlen Eigenschaften wie Zeilennummern, Syntax-Hervorhebung usw. In Kapitel 9 zeige ich Ihnen, wie Sie mit dem External Editor Inhalte bearbeiten können. Damit können Sie den Inhalt der Website in lokalen Editoren wie Macromedia Dreamweaver oder Emacs bearbeiten. In Kapitel 6 zeige ich Ihnen, wie Sie Plone dazu bringen, Page Templates als Dateien von der Festplatte zu lesen, und dann können Sie ein beliebiges Werkzeug dafür benutzen.</p>
<p>Um ein Template zu erstellen, gehen Sie ins ZMI, klicken erst auf <em>portal_skins</em>, dann auf <em>custom</em> und wählen dann <em>Page Template</em> im Dropdown-Menü (siehe Abbildung 5.1). Klicken Sie auf <em>Add</em>, und anschließend sehen Sie die Seite in Abbildung 5.2.</p>
<a class="reference external image-reference" href="img/05-01.png/image_view_fullscreen"><img alt="img/05-01.png" class="original" src="img/05-01.png" /></a>
<p>Abbildung 5.1. Wählen der Page Template-Option</p>
<a class="reference external image-reference" href="img/05-02.png/image_view_fullscreen"><img alt="img/05-02.png" class="original" src="img/05-02.png" /></a>
<p>Abbildung 5.2. Hinzufügen eines Page Templates</p>
<p>Geben Sie <strong>test</strong> als ID des Page Templates ein. Klicken Sie dann auf <em>Add</em> und auf den <em>Edit</em>-Button, mit dem Sie zum Management-Schirm gelangen (siehe Abbildung 5.3). Dann können Sie dieses Template über das Web bearbeiten, indem Sie den Textbereich benutzen und auf <em>Save Changes</em> klicken, um die Änderungen zu speichern.</p>
<a class="reference external image-reference" href="img/05-03.png/image_view_fullscreen"><img alt="img/05-03.png" class="original" src="img/05-03.png" /></a>
<p>Abbildung 5.3. Bearbeiten eines Page Templates</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Vor Zope 2.7 durchliefen alle Page Templates die Variable <tt class="docutils literal">here</tt>, die äquivalent zu <tt class="docutils literal">context</tt> ist. Wenn Sie <tt class="docutils literal">here</tt> in irgendeinem Stück Code in einem Page Template sehen, bedeutet das <tt class="docutils literal">context</tt>. Die neue Variable <tt class="docutils literal">context</tt> wurde hinzugefügt, um klarer zu sein und die Page Templates mit Script(Python)-Objekten zu harmonisieren.</p>
</div>
<p>Nach einem Klick auf <em>Save Changes</em> wird das Page Template kompiliert. Falls Sie Fehler im Template gemacht haben sollten, sehen Sie diese oben auf der Seite hervorgehoben. Abbildung 5.4 zeigt einen Fehler bei einem <tt class="docutils literal">h1</tt>-Tag an, das nicht geschlossen ist. (Wie zuvor erwähnt wurde, müssen Page Templates gültiges XHTML sein.)</p>
<a class="reference external image-reference" href="img/05-04.png/image_view_fullscreen"><img alt="img/05-04.png" class="original" src="img/05-04.png" /></a>
<p>Abbildung 5.4. Page Template-Fehler</p>
<p>Nachdem Sie das Page Template erfolgreich gespeichert haben, können Sie auf den <em>Test</em>-Reiter klicken, um den dargestellten Wert des Templates zu sehen. In Abbildung 5.5 sehen Sie, dass die Überschrift durch die ID des Templates ersetzt wurde und dass der Hauptabsatz nun die ID des Templates enthält.</p>
<a class="reference external image-reference" href="img/05-05.png/image_view_fullscreen"><img alt="img/05-05.png" class="original" src="img/05-05.png" /></a>
<p>Abbildung 5.5. Erzeugen der Seite</p>
<p>Der Management-Bildschirm eines Page Templates enthält auch die folgenden wichtigen Eigenschaften:</p>
<ul class="simple">
<li><strong>Title</strong>: Dies ist der Titel dieses Templates, der optional ist. Wenn Sie diesen im vorigen Beispiel ändern, z.B. nachdem Sie auf <em>Test</em> geklickt haben, werden Sie feststellen, dass sich das HTML im Ergebnis verändert hat.</li>
<li><strong>Content-Type</strong>: Dies ist der Inhaltstyp dieses Templates, der normalerweise <tt class="docutils literal">text/html</tt> ist.</li>
<li><strong>Browse HTML source</strong>: Hiermit wird das Template als unverarbeitetes HTML dargestellt. So würde das Template erscheinen, wenn es in einem HTML-Editor geladen würde.</li>
<li><strong>Test</strong>: Hiermit wird das Template verarbeitet und dargestellt.</li>
<li><strong>Expand macros when editing</strong>: Bei angekreuztem Kontrollkästchen wird versucht, Makros zu expandieren. Ich empfehle, es meistens nicht angekreuzt zu lassen. Makros sind eine Eigenschaft für Fortgeschrittene und werden in Kapitel 6 behandelt.</li>
</ul>
<p>Nun, da Sie ein Page Template erzeugt haben, werden Sie ein paar Änderungen daran vornehmen. Dabei werden die Themen demonstriert, die bisher in diesem Kapitel behandelt worden sind. Wenn Sie z.B. möchten, dass Ihr Page Template 1+2 berechnet, könnten Sie folgende Zeile zu Ihrem Page Template hinzufügen:</p>
<pre class="literal-block">
&lt;p&gt;1+2 = &lt;em tal:content=&quot;python: 1+2&quot; /&gt;&lt;/p&gt;
</pre>
<p>Klicken Sie dann auf den <em>Test</em>-Reiter, um zu sehen, ob es funktioniert. Sie sollten Folgendes sehen:</p>
<pre class="literal-block">
1+2 = *3*
</pre>
<p>Um ein Beispiel für eine Pfadtraversierung zu sehen, geben Sie das Logo Ihrer Plone-Site aus. Sie können einen Ausdruck im Logo Ihrer Plone-Site einfügen, indem Sie Folgendes zu Ihrem Page Template hinzufügen:</p>
<pre class="literal-block">
&lt;p tal:replace=&quot;structure context/logo.jpg&quot; /&gt;
</pre>
<p>Hierdurch wird das passende HTML für das Bild erzeugt und auf der Seite angezeigt.</p>
</div>
</div>
<div class="section" id="die-grundsyntax-von-page-templates">
<h3>Die Grundsyntax von Page Templates</h3>
<p>Nachdem Sie nun gesehen haben, wie man ein Page Template erstellt, werde ich dessen grundlegende Syntax erklären. Die Syntax von Page Templates lässt sich in einige verschiedene Bestandteile gliedern, die ich in den folgenden Abschnitten behandeln werde.</p>
<div class="section" id="einfuhrung-in-eingebaute-variablen">
<h4>Einführung in eingebaute Variablen</h4>
<p>Die Syntax von Ausdrücken kennen Sie bereits, also werden Sie nun Variablen kennen lernen, die Ausdrücken übergeben werden, wenn Sie ein Page Template darstellen. Alles nun Folgende spielt sich im Kontext eines Zugriffs auf das Bild <tt class="docutils literal">Ein Bild.jpg</tt> im Ordner <tt class="docutils literal">Members/andy</tt> mit dem URL <tt class="docutils literal">/Members/andy/Ein Bild.jpg</tt> ab:</p>
<ul class="simple">
<li><strong>container</strong>: Dies ist der Container, in dem sich das Template befindet. Bei Plone ist das normalerweise der Ordner <tt class="docutils literal">portal_skins</tt>. Sie sollten es vermeiden, einen Container zu verwenden, weil <tt class="docutils literal">portal_skins</tt> unerwartete Änderungen am Container bewirken kann (z.B. eine Referenz auf den Ordner <tt class="docutils literal">andy</tt>).</li>
<li><strong>context</strong>: Dies ist der Kontext, in dem das Template ausgeführt wird. In Plone ist das das angezeigte Objekt, falls Sie ein Portalobjekt anzeigen (z.B. eine Referenz auf das Objekt <tt class="docutils literal">Ein Bild.jpg</tt>).</li>
<li><strong>default</strong>: Manche Anweisungen haben ein spezielles Standardverhalten. Darauf wird bei allen Anweisungen hingewiesen, und diese Variable ist ein Zeiger auf dieses Verhalten.</li>
<li><strong>here</strong>: Dies ist äquivalent zu <tt class="docutils literal">context</tt>.</li>
<li><strong>loop</strong>: Dies ist äquivalent zu <tt class="docutils literal">repeat</tt>.</li>
<li><strong>modules</strong>: Dies ist ein Container für importierte Module. So ist z.B. <tt class="docutils literal">modules/string/atoi</tt> die Funktion <tt class="docutils literal">atoi</tt> aus Pythons string-Modul. Hierunter fallen alle Module, die man gefahrlos ins Zope Page Templates-System importieren kann. Weitere Informationen finden Sie im Abschnitt &quot;Plone mit Python scripten&quot; in Kapitel 6.</li>
<li><strong>nothing</strong>: Dies ist das Äquivalent zu <tt class="docutils literal">None</tt> in Python.</li>
<li><strong>options</strong>: Dies sind die Optionen, die ans Template übergeben werden, was dann passiert, wenn das Template von einem Script oder einer anderen Methode und nicht über das Web aufgerufen wird.</li>
<li><strong>repeat</strong>: Dies ist das wiederholte Element (siehe das Element <tt class="docutils literal">tal:repeat</tt> im Abschnitt &quot;Einführung in die Syntax von TAL-Anweisungen&quot; in diesem Kapitel).</li>
<li><strong>request</strong>: Dies ist die eingehende Anfrage des Clients (alle Werte der eingehenden Anfrage sind mit Hilfe des folgenden Kontext-Testscripts sichtbar). Alle <tt class="docutils literal">GET</tt>- und <tt class="docutils literal">POST</tt>-Parameter werden zwecks leichteren Zugriffs mit dem Python-Modul <tt class="docutils literal">marshall</tt> in ein Dictionary serialisiert. Hier sind einige Beispiele:</li>
</ul>
<pre class="literal-block">
request/HTTP_USER_AGENT # der Browser des Benutzers
request/REMOTE_ADDRR  # der Browser des Benutzers
request/someMessage   # der Wert einer Meldung im Abfrage-String
</pre>
<ul class="simple">
<li><strong>root</strong>: Dies ist das Zope-Wurzelobjekt. Mit <tt class="docutils literal">root/Control_Panel</tt> erhalten Sie beispielsweise das Control Panel für Zope.</li>
<li><strong>template</strong>: Dies ist das aufgerufene Template. Mit <tt class="docutils literal">template/id</tt> erhalten Sie z.B. die ID des dargestellten Templates.</li>
<li><strong>traverse_subpath</strong>: Dies enthält eine Liste von Elementen, die noch traversiert werden müssen. Es handelt sich um eine Variable für Fortgeschrittene, die Sie besser erst dann verwenden sollten, wenn Sie Traversierung und Akquisition verstanden haben.</li>
<li><strong>user</strong>: Dies ist das aktuelle Benutzerobjekt. <tt class="docutils literal">user/getUserName</tt> ist der Benutzername des aktuellen Benutzers.</li>
<li><strong>CONTEXTS</strong>: Dies ist eine Liste mit den meisten dieser Werte.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Mit Ausnahme von <tt class="docutils literal">CONTEXTS</tt> können all diese Variablen in einer <tt class="docutils literal">tal:define</tt>-Anweisung neu definiert werden, wenn der Benutzer das möchte. Das ist allerdings nicht ratsam, weil das für jemanden, der den Code verwendet, recht verwirrend sein kann.</p>
</div>
<p>Das Page Template <tt class="docutils literal">test_context</tt> zeigt die Werte all dieser Variablen sowie den Ort einiger Objekte (siehe Listing 5.1). Es kann hilfreich bei der Fehlersuche und beim Bestimmen von Variablenwerten sein. Fügen Sie es als Page Template namens <tt class="docutils literal">test_context</tt> hinzu, und klicken Sie dann auf <em>Test</em>, um die Ergebnisse zu sehen.</p>
<p>Listing 5.1. <tt class="docutils literal">test_context</tt></p>
<pre class="literal-block">
&lt;html&gt;
  &lt;head /&gt;
  &lt;body&gt;
    &lt;h1&gt;Debug information&lt;/h1&gt;
  &lt;h2&gt;CONTEXTS&lt;/h2&gt;
  &lt;ul&gt;
    &lt;tal:block
        tal:repeat=&quot;item CONTEXTS&quot;&gt;
    &lt;li
        tal:condition=&quot;python: item != 'request'&quot;
        tal:define=&quot;context CONTEXTS;&quot;&gt;
            &lt;b tal:content=&quot;item&quot; /&gt;
            &lt;span tal:replace=&quot;python: context[item]&quot; /&gt;
    &lt;/li&gt;
    &lt;/tal:block&gt;
  &lt;/ul&gt;
  &lt;h2&gt;REQUEST&lt;/h2&gt;
  &lt;p tal:replace=&quot;structure request&quot; /&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Das Page Template <tt class="docutils literal">test_context</tt> produziert die Ausgabe, die Sie in Abbildung 5.6 sehen.</p>
<a class="reference external image-reference" href="img/05-06.png/image_view_fullscreen"><img alt="img/05-06.png" class="original" src="img/05-06.png" /></a>
<p>Abbildung 5.6. Ein Beispiel für alle Standardvariablen in einem Script</p>
</div>
<div class="section" id="einfuhrung-in-die-syntax-von-tal-anweisungen">
<h4>Einführung in die Syntax von TAL-Anweisungen</h4>
<p>Die Sprache TAL (Template Attribute Language) bietet alle grundlegenden Bausteine für eine dynamische Präsentation. TAL definiert acht Anweisungen: <tt class="docutils literal">attributes</tt>, <tt class="docutils literal">condition</tt>, <tt class="docutils literal">content</tt>, <tt class="docutils literal">define</tt>, <tt class="docutils literal"><span class="pre">omit-tag</span></tt>, <tt class="docutils literal"><span class="pre">on-error</span></tt>, <tt class="docutils literal">repeat</tt> und <tt class="docutils literal">replace</tt>.</p>
<p>Da Page Templates gültiges XML sind, müssen alle TAL-Attribute in Kleinbuchstaben geschrieben werden. Außerdem darf jedes Element eine Anweisung nur einmal enthalten. In den folgenden Beispielen habe ich in den Elementen Zeilenumbrüche eingefügt, um die Lesbarkeit zu verbessern. Das ist absolut gültiger Code und kommt recht häufig im Plone-Quellcode vor. Dennoch ist es optional und wird nicht verlangt.</p>
<div class="section" id="tal-attributes-element-attribute-andern">
<h5>tal:attributes: Element-Attribute ändern</h5>
<p>Mit <tt class="docutils literal">tal:attributes</tt> können Sie ein oder mehrere Attribute eines Elements ersetzen. Eine Anweisung enthält die zu ändernden Attribute, die mit einem Leerzeichen von der Anweisung getrennt werden. Beispiel:</p>
<pre class="literal-block">
&lt;a href=&quot;#&quot;
   tal:attributes=&quot;href context/absolute_url&quot;&gt;
   Link hierher
&lt;/a&gt;
</pre>
<p>Hierbei wird das Attribut <tt class="docutils literal">href</tt> des Links auf das Ergebis von <tt class="docutils literal">here/absolute_url</tt> geändert. Das Attribut <tt class="docutils literal">href</tt> wurde für dieses Element bereits definiert, d.h., wenn ein Design-Programm diese Seite öffnet, sieht es ein gültiges Element (auch, wenn der Link vielleicht so lange keinen Sinn macht, bis die Seite verarbeitet worden ist). Es folgen einige Beispielausgaben:</p>
<pre class="literal-block">
&lt;a href=&quot;http://plone.org/Members/andy/book&quot;&gt;Link hierher&lt;/a&gt;
</pre>
<p>Da jedes Element mehrere Attribute haben kann, erlaubt <tt class="docutils literal">tal:attributes</tt> die Änderung eines oder mehrerer Attribute gleichzeitig, indem mehrere Anweisungen verwendet werden. Um mehrere Attribute gleichzeitig zu ändern, trennen Sie die Anweisungen mit einem Semikolon (<tt class="docutils literal">;</tt>). Falls das Attribut oder die Anweisung ein Semikolon enthält, können Sie dieses mit einem weiteren Semikolon direkt davor schützen (<tt class="docutils literal">;;</tt>). Tun Sie Folgendes, um beispielsweise <tt class="docutils literal">href</tt> und <tt class="docutils literal">title</tt> im Element zu ändern:</p>
<pre class="literal-block">
&lt;a href=&quot;#&quot;
   tal:attributes=&quot;href context/absolute_url;
      title context/title_or_id&quot;&gt;Link&lt;/a&gt;
</pre>
<p>Die Ausgabe dieses Beispiels ist folgende:</p>
<pre class="literal-block">
&lt;a href=&quot;http://plone.org/Members/andy/book&quot; title=&quot;Plone Book&quot;&gt;Link&lt;/a&gt;
</pre>
<p>Die Tags <tt class="docutils literal">tal:attributes</tt> und <tt class="docutils literal">tal:replace</tt> schließen einander aus, weil <tt class="docutils literal">replace</tt> das Element entfernt. Das Zope Page Templates-System stellt fest, wenn beide verwendet werden, gibt dann eine Warnung aus und ignoriert den <tt class="docutils literal">tal:attributes</tt>-Teil. Falls der Ausdruck zu <tt class="docutils literal">default</tt> ausgewertet wird, wird keine Änderung vorgenommen. Beispiel:</p>
<pre class="literal-block">
&lt;a href=&quot;#&quot;
    tal:attributes=&quot;href
        python:request.get('message', default)&quot;&gt;
    Link&lt;/a&gt;
</pre>
<p>In diesem Beispiel verwende ich die Funktion <tt class="docutils literal">get</tt> auf dem Objekt <tt class="docutils literal">request</tt>. Falls die eingehende Anfrage nach der Seite die Variable <tt class="docutils literal">message</tt> enthält, wird der erste Wert verwendet, d.h. <tt class="docutils literal">message</tt>. Wenn die Variable <tt class="docutils literal">message</tt> nicht vorhanden ist, wird der zweite Wert, <tt class="docutils literal">default</tt>, benutzt. Daher erfolgt allein durch die Übergabe des Parameters <tt class="docutils literal">message</tt> eine Änderung.</p>
</div>
<div class="section" id="tal-condition-bedingungen-auswerten">
<h5>tal:condition: Bedingungen auswerten</h5>
<p>Mit der Anweisung <tt class="docutils literal">tal:condition</tt> kann eine Bedingung getestet werden, bevor das Element dargestellt wird. Beispiel:</p>
<pre class="literal-block">
&lt;p tal:condition=&quot;request/message&quot;&gt;
    Es gibt eine Message
&lt;/p&gt;
&lt;p tal:condition=&quot;not: request/message&quot;&gt;
    Keine Message
&lt;/p&gt;
</pre>
<p>Hierbei wird der Absatz mit dem Text <tt class="docutils literal">message</tt> nur dann dargestellt, wenn die Variable <tt class="docutils literal">request</tt> ein Attribut hat und dieses zu <tt class="docutils literal">True</tt> ausgewertet wird. Der Test einer Bedingung ist jedoch sinnlos, falls das Gegenteil der Bedingung nicht getestet werden kann. Dies leistet der negierte Ausdruck. Das Präfix <tt class="docutils literal">not:</tt> invertiert die Anweisung, d.h., <tt class="docutils literal">not: request/message</tt> wird dann zu <tt class="docutils literal">True</tt> ausgewertet, falls die Anfragevariable <tt class="docutils literal">message</tt> zu <tt class="docutils literal">False</tt> ausgewertet wird.</p>
<p>In TAL wird Folgendes zu <tt class="docutils literal">False</tt> ausgewertet:</p>
<ul class="simple">
<li>Die Zahl Null</li>
<li>Alle Fließkomma- oder komplexen Zahlen, die als null ausgewertet werden (z.B. <tt class="docutils literal">0.0</tt>)</li>
<li>Strings der Länge null (z.B. <tt class="docutils literal">&quot;&quot;</tt>)</li>
<li>Eine leere Liste oder ein leeres Tupel</li>
<li>Ein leeres Dictionary</li>
<li>Der Wert <tt class="docutils literal">None</tt> in Python</li>
<li>Der Wert <tt class="docutils literal">nothing</tt> in TALES</li>
</ul>
<p>Folgendes wird zu <tt class="docutils literal">True</tt> ausgewertet:</p>
<ul class="simple">
<li>Der Wert <tt class="docutils literal">default</tt></li>
<li>Alle Zahlen, die von null verschieden sind</li>
<li>Strings, die nicht leer sind</li>
<li>Strings, die nicht nur aus Leerzeichen bestehen (z.B. <tt class="docutils literal">&quot;&nbsp;&nbsp; &quot;</tt>)</li>
<li>Alles andere</li>
</ul>
</div>
<div class="section" id="tal-content-text-hinzufugen">
<h5>tal:content: Text hinzufügen</h5>
<p>Die Anweisung <tt class="docutils literal">tal:content</tt> in einem Page Template ist die wahrscheinlich am häufigsten verwendete Anweisung. Sie ist auch eine der einfachsten, da sie den Inhalt eines Elements durch den angegebenen Wert ersetzt. Beispiel:</p>
<pre class="literal-block">
&lt;i tal:content=&quot;context/title_or_id&quot;&gt;Ein Titel&lt;/i&gt;
</pre>
<p>Die Ausgabe des Beispiels lautet:</p>
<pre class="literal-block">
&lt;i&gt;Ein Titel&lt;/i&gt;
</pre>
<p>Hierbei wird der Text <tt class="docutils literal">Ein Titel</tt> durch den Wert des Ausdrucks <tt class="docutils literal">context/title_or_id</tt> ersetzt. Falls der zu ersetzende Text HTML-Elemente enthält, werden diese geschützt. Der zu ersetzende Text wird standardmäßig HTML-geschützt. Das Präfix <tt class="docutils literal">structure</tt> erlaubt, dass HTML eingegeben wird, ohne dass die Elemente geschützt werden. Beispiel:</p>
<pre class="literal-block">
&lt;i tal:content=&quot;structure here/title_or_id&quot;&gt;HTML nicht schützen&lt;/i&gt;
</pre>
<p>Falls das Element mit den Attributen <tt class="docutils literal">tal:content</tt> weitere Elemente enthält, werden auch diese Elemente alle ersetzt. Die Tags <tt class="docutils literal">tal:content</tt> und <tt class="docutils literal">tal:replace</tt> schließen sich gegenseitig aus und können nicht beide im gleichen Element vorkommen, ohne dass ein Fehler dabei auftritt. Falls der Wert <tt class="docutils literal">default</tt> lautet, bleibt der Inhalt unverändert.</p>
</div>
<div class="section" id="tal-define-variablen-definieren">
<h5>tal:define: Variablen definieren</h5>
<p>Mit der Anweisung <tt class="docutils literal">tal:define</tt> können im Template Variablen erzeugt und wiederverwendet werden. Beispiel:</p>
<pre class="literal-block">
&lt;p tal:define=&quot;title here/title_or_id&quot;&gt;
    ... &lt;i tal:content=&quot;title&quot;&gt;Ein Titel&lt;/i&gt; ...
&lt;/p&gt;
</pre>
<p>In diesem Beispiel wird die Variable <tt class="docutils literal">title</tt> erzeugt, und es wird ihr das Ergebnis von <tt class="docutils literal">here/title_or_id</tt> zugewiesen. Später wird die Variable in der Anweisung <tt class="docutils literal">tal:content</tt> benutzt. Standardmäßig wird diese Variable nur im lokalen Geltungsbereich des aktuellen Elements erzeugt. Im vorigen Beispiel können daher nur Elemente im Absatz die Variable <tt class="docutils literal">title</tt> benutzen. Sie können die Variable an einer beliebigen Stelle in der Anweisung neu definieren oder in anderen Elementen so oft wiederverwenden, wie Sie möchten.</p>
<p>Um eine global benutzbare Variable zu erstellen, verwenden Sie das Präfix <tt class="docutils literal">global</tt>. Dadurch wird der Zugriff auf die Variable von überall im Template ermöglicht, nicht nur aus dem definierenden Element. Beispiel:</p>
<pre class="literal-block">
&lt;p tal:define=&quot;global title string:Foo bar&quot;&gt;
    ... &lt;i tal:content=&quot;title&quot;&gt;Ein Titel&lt;/i&gt; ...
&lt;/p&gt;
&lt;i tal:content=&quot;title&quot;&gt;Immer noch ein Titel&lt;/i&gt;
</pre>
<p>Plone bietet eine große Anzahl solcher globaler Definitionen, damit die Benutzer diese in eigenen Scripts einfach verwenden können. Wie bei allen solchen Definitionen gilt, dass sie sich in Zukunft verändern können, d.h., Sie sollten sie mit Bedacht einsetzen. Diese <tt class="docutils literal">define</tt>-Anweisungen bedeuten, dass eine große Menge an globalen Variablen verfügbar sind. Um beispielsweise an den Titel Ihrer Plone-Site zu gelangen, können Sie einfach Folgendes aufrufen:</p>
<pre class="literal-block">
&lt;p tal:content=&quot;portal_title&quot; /&gt;
</pre>
<p>Diese <tt class="docutils literal">define</tt>-Anweisungen finden Sie im ZMI, wenn Sie erst auf <em>portal_skins</em> klicken, dann auf <em>plone_templates</em> und schließlich auf <em>global_defines</em>. Eine vollständige Liste aller Definitionen samt Erklärung finden Sie in Anhang A.</p>
</div>
<div class="section" id="tal-omit-tag-elemente-entfernen">
<h5>tal:omit-tag: Elemente entfernen</h5>
<p>Die Anweisung <tt class="docutils literal"><span class="pre">tal:omit-tag</span></tt> ist etwas ungewöhnlich. Mit ihr kann ein Tag entfernt werden. Da das Zope Page Templates-System den Gebrauch von HTML-Tags vorschreibt, benötigen komplizierte Seiten oftmals eine Menge Elemente und können dazu führen, dass Extra-Tags hinzugefügt werden. Bei dieser Anweisung wird das Tag entfernt, so dass nur sein Inhalt stehen bleibt. Beispiel:</p>
<pre class="literal-block">
&lt;p tal:omit-tag=&quot;&quot;&gt;Irgendein Text&lt;/p&gt;
</pre>
<p>Die Ausgabe lautet:</p>
<pre class="literal-block">
Irgendein Text
</pre>
<p>In diesem Beispiel wird der Text <tt class="docutils literal">Irgendein Text</tt> ausgegeben, aber der Tag drumherum wird nicht dargestellt. Die Anweisung <tt class="docutils literal"><span class="pre">tal:omit-tag</span></tt> hat einen Ausdruck als optionales Argument. Wenn dieser zu <tt class="docutils literal">False</tt> ausgewertet wird, findet die Auslassung nicht statt. Folgende Anweisung z.B. tut nichts:</p>
<pre class="literal-block">
&lt;p tal:omit-tag=&quot;nothing&quot;&gt;Irgendein Text&lt;/p&gt;
</pre>
<p>Eine Alternative zum Einsatz von <tt class="docutils literal"><span class="pre">tal:omit-tag</span></tt> ist der eines <tt class="docutils literal">tal</tt>-Namespace, wie im Abschnitt &quot;Nützliche Hinweise&quot; in Kapitel 6 beschrieben wird.</p>
</div>
<div class="section" id="tal-on-error-fehler-behandeln">
<h5>tal:on-error: Fehler behandeln</h5>
<p>Die Anweisung <tt class="docutils literal"><span class="pre">tal:on-error</span></tt> bietet die Möglichkeit, Fehler zu behandeln. Sie verhält sich eher wie <tt class="docutils literal">tal:content</tt>, weil sie bewirkt, dass der Inhalt des Tags ersetzt wird. Aber sie wird nur dann ausgeführt, wenn ein Fehler auftritt.</p>
<p>Hier ist ein Beispiel:</p>
<pre class="literal-block">
&lt;p  tal:content=&quot;request/message&quot;
    tal:on-error=&quot;string: Keine Message&quot;&gt;Message&lt;/p&gt;
</pre>
<p>Wenn es hier einen Fehler bei der Auswertung des Ausdrucks <tt class="docutils literal">request/message</tt> gibt, dann wird das Attribut <tt class="docutils literal"><span class="pre">on-error</span></tt> aktiviert. Es bewirkt, dass der Inhalt des Tags durch den Text <tt class="docutils literal">Keine Message</tt> ersetzt wird.</p>
<p>Leider ist die Anweisung <tt class="docutils literal"><span class="pre">on-error</span></tt> sehr beschränkt. Das Tag kann nicht zwischen verschiedenen Fehlern unterscheiden und erlaubt nur einen einzigen Ausdruck, der ausgewertet und benutzt werden kann. Diese Einschränkung ist gewollt, damit das Tag nicht übermäßig verwendet wird. Eine Fehlerbehandlung ist wirklich sehr viel besser in der Logik Ihrer Anwendung aufgehoben.</p>
<p>Zum Glück können Sie bei allen Ausdrücken Alternativen in der Anweisung angeben, falls der erste Anweisungsteil weder zu <tt class="docutils literal">True</tt> noch zu <tt class="docutils literal">False</tt> ausgewertet wird, d.h., wenn ein Fehler auftritt. Alle Alternativen werden durch einen senkrechten Strich (<tt class="docutils literal">|</tt>) voneinander getrennt, und es können mehrere Alternativen in einer Anweisung vorkommen. Wenn Sie sich auf Variablen in eintreffenden Anfragen verlassen, sollten Sie am Ende immer <tt class="docutils literal">|nothing</tt> hinzufügen, um sicherzugehen, dass kein Attributfehler auftritt.</p>
<p>Beispiel:</p>
<pre class="literal-block">
&lt;p
  tal:content=&quot;request/message&quot;
  tal:condition=&quot;request/message|nothing&quot;&gt;
    Es gibt eine Message
&lt;/p&gt;
&lt;p tal:condition=&quot;not: request/message|nothing&quot;&gt;
    Keine Message
&lt;/p&gt;
</pre>
<p>Dieses zweite Beispiel ist etwas wortreicher, aber aus mehreren Gründen empfehlenswert:</p>
<ul class="simple">
<li>Der Designer sieht die positive <tt class="docutils literal">und</tt> die negative Bedingung.</li>
<li>Sie können eine kompliziertere Fehlerbedingung behandeln, anstatt nur einen String auszugeben.</li>
</ul>
</div>
<div class="section" id="tal-repeat-schleifen-ausfuhren">
<h5>tal:repeat: Schleifen ausführen</h5>
<p>Die Anweisung <tt class="docutils literal">tal:repeat</tt> erlaubt Schleifendurchgänge über Objekte und ist eine der komplizierteren Anweisungen. Eine Schleife enthält den Wert, der bei jeder Iteration über die Ergebnisse zugewiesen werden soll. Dieser Wert ist durch ein Leerzeichen von den Ergebnissen getrennt, über die iteriert wird.</p>
<p>Hier sehen Sie ein Beispiel für Schleifen:</p>
<pre class="literal-block">
&lt;table&gt;
  &lt;tr tal:repeat=&quot;row context/portal_catalog&quot;&gt;
    &lt;td tal:content=&quot;row/Title&quot;&gt;Title&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>In diesem Beispiel gibt der Ausdruck <tt class="docutils literal">here/portal_catalog</tt> eine Liste von Ergebnissen zurück. Da die Wiederholung beim <tt class="docutils literal">row</tt>-Tag der Tabelle beginnt, wird für jede Zeile in der Ergebnisliste eine neue Zeile in der Tabelle erzeugt. Wie bei <tt class="docutils literal">tal:define</tt> findet in jeder Iteration über die Ergebnisse eine Zuweisung an eine lokale Variable, in diesem Fall <tt class="docutils literal">row</tt>, statt. Dieses Beispiel gibt für jeden Eintrag in der Ergebnisliste eine Zeile aus.</p>
<p>In der <tt class="docutils literal">repeat</tt>-Anweisung können Sie auf einige nützliche Variablen wie die laufende Nummer der aktuellen Iteration zugreifen. Darauf können Sie über die Variable <tt class="docutils literal">repeat</tt> zugreifen, die zum Namespace hinzugefügt wird. Um beispielsweise auf die laufende Nummer zuzugreifen, benutzen Sie Folgendes:</p>
<pre class="literal-block">
&lt;table&gt;
  &lt;tr tal:repeat=&quot;row context/portal_catalog&quot;&gt;
    &lt;td tal:content=&quot;repeat/row/number&quot;&gt;1&lt;/td&gt;
    &lt;td tal:content=&quot;row/Title&quot;&gt;Title&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>Die vollständige Liste aller in <tt class="docutils literal">repeat</tt> verfügbaren Variablen lautet:</p>
<ul class="simple">
<li><strong>index</strong>: Dies ist die laufende Nummer, beginnend bei null.</li>
<li><strong>number</strong>: Dies ist die laufende Nummer, beginnend bei eins.</li>
<li><strong>even</strong>: Dies ist <tt class="docutils literal">True</tt> bei geradem Iterationsindex (<tt class="docutils literal">0</tt>, <tt class="docutils literal">2</tt>, <tt class="docutils literal">4</tt>, ...).</li>
<li><strong>odd</strong>: Dies ist <tt class="docutils literal">True</tt> bei ungeradem Iterationsindex (<tt class="docutils literal">1</tt>, <tt class="docutils literal">3</tt>, <tt class="docutils literal">5</tt>, ...).</li>
<li><strong>start</strong>: Dies ist <tt class="docutils literal">True</tt> bei der ersten Iteration.</li>
<li><strong>end</strong>: Dies ist <tt class="docutils literal">True</tt> bei der letzten Iteration.</li>
<li><strong>length</strong>: Dies ist die Gesamtzahl an Iterationen.</li>
<li><strong>letter</strong>: Dies ist die Iterationszahl als Kleinbuchstabe (<tt class="docutils literal">a</tt>-<tt class="docutils literal">z</tt>, <tt class="docutils literal">aa</tt>-<tt class="docutils literal">az</tt>, <tt class="docutils literal">ba</tt>-<tt class="docutils literal">bz</tt>, ..., <tt class="docutils literal">za</tt>-<tt class="docutils literal">zz</tt>, <tt class="docutils literal">aaa</tt>-<tt class="docutils literal">aaz</tt>, usw.).</li>
<li><strong>Letter</strong>: Dies ist eine Großbuchstabenversion von letter.</li>
<li><strong>roman</strong>: Dies ist die Zahl mit römischen Ziffern in Kleinbuchstaben (<tt class="docutils literal">i</tt>, <tt class="docutils literal">ii</tt>, <tt class="docutils literal">iii</tt>, <tt class="docutils literal">iv</tt>, <tt class="docutils literal">v</tt>, usw.), beginnend bei eins.</li>
</ul>
<p>Im <tt class="docutils literal">repeat</tt>-Namespace sind zwei weitere Werte verfügbar, die recht ungewöhnlich sind und selten verwendet werden, <tt class="docutils literal">first</tt> und <tt class="docutils literal">last</tt>. Mit diesen beiden Variablen können Sie Informationen über die Daten der Iteration speichern. Durch die Verwendung des Werts, den Sie in einem Ausdruck speichern möchten, wird ein boolescher Wert zurückgegeben. Bei der Variablen <tt class="docutils literal">first</tt> gibt <tt class="docutils literal">True</tt> an, dass dieser Wert zum ersten Mal in der Iteration aufgetreten ist. Entsprechend gilt bei der Variablen <tt class="docutils literal">last</tt>, dass <tt class="docutils literal">True</tt> anzeigt, dass der Wert zum letzten Mal in der Iteration aufgetreten ist.</p>
<p>Hier ist ein Beispiel dafür:</p>
<pre class="literal-block">
&lt;ul&gt;
  &lt;li tal:repeat=&quot;val context/objectValues&quot;&gt;
    First: &lt;i tal:content=&quot;repeat/val/first/meta_type&quot; /&gt;,
    Last: &lt;i tal:content=&quot;repeat/val/last/meta_type&quot; /&gt;:
    &lt;b tal:content=&quot;val/meta_type&quot; /&gt;,
    &lt;b tal:content=&quot;val/title_or_id&quot; /&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</pre>
</div>
<div class="section" id="tal-replace-text-hinzufugen">
<h5>tal:replace: Text hinzufügen</h5>
<p>Die Anweisung <tt class="docutils literal">tal:replace</tt> ähnelt <tt class="docutils literal">tal:content</tt>, mit dem Unterschied, dass das gesamte Tag entfernt wird.</p>
<p>Beispiel:</p>
<pre class="literal-block">
&lt;p tal:replace=&quot;context/title_or_id&quot;&gt;Ein Titel&lt;/p&gt;
</pre>
<p>Hiermit wird das Ergebnis des Ausdrucks <tt class="docutils literal">context/title_or_id</tt> angezeigt, aber die Absatz-Tags werden aus dem Ergebnis entfernt. Das ist gleichbedeutend mit dem Folgenden:</p>
<pre class="literal-block">
&lt;p
  tal:content=&quot;here/title_or_id&quot;
  tal:omit-tag=&quot;&quot;&gt;Ein Titel&lt;/p&gt;
</pre>
<p>Falls das Element mit der Anweisung <tt class="docutils literal">tal:replace</tt> weitere Elemente enthält, werden diese Elemente alle ersetzt. Die Anweisung <tt class="docutils literal">tal:replace</tt> können Sie nicht zusammen mit <tt class="docutils literal">tal:attributes</tt> oder <tt class="docutils literal">tal:content</tt> verwenden, da sie sich gegenseitig ausschließen und ein Fehler auftritt, wenn Sie beide im gleichen Element benutzen.</p>
</div>
</div>
<div class="section" id="einfuhrung-in-die-ausfuhrungsreihenfolge">
<h4>Einführung in die Ausführungsreihenfolge</h4>
<p>Die Reihenfolge, in der TAL-Attribute geschrieben werden, ist nicht die gleiche, in der sie ausgeführt werden, weil sie Teil von XML-Elementen sind (und XML kümmert sich nicht um die Reihenfolge von Attributen). Die Attribute werden in der folgenden Reihenfolge ausgeführt:</p>
<ol class="arabic simple">
<li><tt class="docutils literal">define</tt></li>
<li><tt class="docutils literal">condition</tt></li>
<li><tt class="docutils literal">repeat</tt></li>
<li><tt class="docutils literal">content</tt></li>
<li><tt class="docutils literal">replace</tt></li>
<li><tt class="docutils literal">attributes</tt></li>
<li><tt class="docutils literal"><span class="pre">omit-tag</span></tt></li>
</ol>
<p>Die Anweisungen <tt class="docutils literal">content</tt> und <tt class="docutils literal">replace</tt> können Sie nicht im gleichen Element verwenden, da sie sich gegenseitig ausschließen. Die Anweisung <tt class="docutils literal">attributes</tt> zusammen mit <tt class="docutils literal">replace</tt> oder <tt class="docutils literal"><span class="pre">omit-tag</span></tt> ist sinnlos in einem Element, weil die Attribute entfernt werden. Das <tt class="docutils literal"><span class="pre">on-error</span></tt>-Tag wird nicht erwähnt, weil es verwendet wird, wenn der erste Fehler in irgendeinem der vorigen Elemente auftritt.</p>
</div>
<div class="section" id="beispiel-benutzerinformationen-anzeigen">
<h4>Beispiel: Benutzerinformationen anzeigen</h4>
<p>Damit Sie sich die bisher gelernten Punkte veranschaulichen können, werden Sie nun ein Page Template erstellen, das eine einfache Aufgabe hat: Informationen über einen Benutzer im System anzuzeigen.</p>
<p>In diesem Beispiel verwendet eine Firma Plone intern in einem Intranet. Jeder Angestellte ist in Plone registriert und kann sich anmelden. Es gibt jedoch keine einfache Seite, die alle Angestellten anzeigt oder darüber informiert, wie man sie kontaktieren kann. Sie werden eine einfache Informationsseite für die Benutzer erstellen, die für alle Benutzer deren E-Mail-Adresse, Homepage und Bild anzeigt und die angibt, wann sie sich das letzte Mal angemeldet haben.</p>
<p>Ein erster Prototyp dieser Seite ist mit TAL, TALES und ein wenig Grundlagenwissen über CMF-Werkzeuge (Content-Management-Framework) schnell erstellt. Weil die APIs (Application Programming Interfaces) zu diesen Werkzeugen aber leider sehr verworren sind, ist ein Teil dieses Codes länger, als er sein sollte. Seien Sie erst einmal unbesorgt wegen der API dieser Werkzeuge, sie werden in Kapitel 9 behandelt. Wenn Sie das API erst einmal als gegeben hinnehmen, können Sie sich auf TAL konzentrieren.</p>
<p>Zuerst müssen Sie ein Page Template erstellen. Klicken Sie also auf <em>portal_skins</em>, dann auf <em>custom</em>, und fügen Sie ein Page Template mit der ID <strong>user_info</strong> hinzu. Als Zweites werden Sie es wie folgt bearbeiten. Ein vollständiges Listing diese Page Templates finden Sie in Anhang A. Wenn Sie dieses Listing untersuchen, sehen Sie, dass es mit <tt class="docutils literal">HTML</tt>- und <tt class="docutils literal">body</tt>-Tags beginnt.</p>
<p>Aus Gründen der Bequemlichkeit setzen Sie die Hauptdefinitionen in ein <tt class="docutils literal">div</tt>-Tag:</p>
<pre class="literal-block">
&lt;div
  tal:omit-tag=&quot;&quot;
  tal:define=&quot;
    userName request/userName|nothing;
    userObj python: here.portal_membership.getMemberById(userName);
    getPortrait nocall: here/portal_membership/getPersonalPortrait;
    getFolder nocall: here/portal_membership/getHomeFolder
    &quot;&gt;
</pre>
<p>In diesem <tt class="docutils literal">div</tt>-Tag gibt es vier <tt class="docutils literal">define</tt>-Anweisungen: eine, um an den Benutzernamen zu gelangen, der im <tt class="docutils literal">request</tt>-Objekt übergeben wird, und eine weitere, um diesen Benutzernamen in ein Benutzerobjekt zu übersetzen. Die letzten beiden <tt class="docutils literal">define</tt>-Anweisungen stellen sicher, dass Sie eine gültige Referenz auf die Methoden haben, mit denen Sie an die Bilder und Ordner der Benutzer gelangen. Auch diese sind bequem, weil sie den Code später vereinfachen. Ein solches <tt class="docutils literal">div</tt>-Tag oder auch anderes Tag einzurichten, das eine Reihe von Definitionen enthält, ist ein übliches Vorgehen beim Zope Page Templates-System. Der Code wird dadurch einfach sauberer.</p>
<p>Als Nächstes erstellen Sie zwei einfache Bedingungen, um zu prüfen, ob Sie einen Benutzer haben:</p>
<pre class="literal-block">
&lt;p tal:condition=&quot;not: userName&quot;&gt;
    Keinen Benutzernamen gewählt.
&lt;/p&gt;
&lt;p tal:condition=&quot;not: userObj&quot;&gt;
    Dieser Benutzername existiert nicht.
&lt;/p&gt;
</pre>
<p>Wenn die Anfrage keinen Benutzernamen enthält, resultiert der Ausdruck <tt class="docutils literal">request/username|nothing</tt> darin, dass <tt class="docutils literal">userName</tt> gleich <tt class="docutils literal">nothing</tt> ist, und der einfache Test schlägt fehl. Wenn der Benutzername ungültig ist, wird <tt class="docutils literal">userObj</tt> zu <tt class="docutils literal">None</tt>, und es werden Fehlermeldungen für diese beiden Bedingungen ausgegeben.</p>
<p>Nun sind Sie so weit, dass Sie den Benutzer bearbeiten können:</p>
<pre class="literal-block">
&lt;table tal:condition=&quot;userObj&quot;&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;img src=&quot;&quot;
      tal:replace=&quot;structure python: getPortrait(userName)&quot; /&gt;
    &lt;/td&gt;
</pre>
<p>Da Sie einen Benutzer nur dann anzeigen können, wenn Sie einen gefunden haben, werden Sie sicherstellen, dass es eine einfache Bedingung für diese Tabelle  <tt class="docutils literal"><span class="pre">tal:condition=&quot;userObj&quot;</span></tt> gibt. Um das Bild eines Benutzers anzuzeigen, verwenden Sie die zuvor definierte Methode <tt class="docutils literal">getPortrait</tt>. Diese Funktion gibt das gesamte Tag zurück, d.h., das <tt class="docutils literal">structure</tt>-Tag stellt sicher, dass das ganze Bild korrekt dargestellt wird. Als Nächstes möchten Sie einige Eigenschaften wie <tt class="docutils literal">name</tt> und <tt class="docutils literal">email</tt> anzeigen. Im Folgenden wird dies für eine dieser Optionen, den <tt class="docutils literal">home</tt>-Ordner, gezeigt:</p>
<pre class="literal-block">
&lt;li
    tal:define=&quot;home python: getFolder(userName)&quot;
    tal:condition=&quot;home&quot;&gt;
    &lt;a href=&quot;&quot;
        tal:attributes=&quot;href home/absolute_url&quot;
        &gt;Home-Ordner&lt;/a&gt;
&lt;/li&gt;
</pre>
<p>Zuerst verwenden Sie ein <tt class="docutils literal">define</tt>, um an den Ordner zu gelangen, und weisen ihn der Variablen <tt class="docutils literal">home</tt> zu. In einer Plone-Site ist das Erstellen eines <tt class="docutils literal">home</tt>-Ordners für einen Benutzer optional, d.h., beim Verweis auf einen Ordner müssen Sie sicher sein, dass dieser auch existiert. Wegen der Ausführungsreihenfolge von TAL kommt die Definition vor der Bedingung. Danach zeigen Sie einen Link auf den Ordner mit Hilfe des Ordnerattributs <tt class="docutils literal">absolute_url</tt> an.</p>
<p>Dieses Page Template enthält ein paar weitere Zeilen, um einige andere nützliche und aufregende Eigenschaften zu finden, die es dem Benutzer zeigen kann. Wie bei den meisten Dingen in Plone kommt es darauf an, die korrekten API-Aufrufe zu finden und die Ausgabe entsprechend weiterzuverarbeiten.</p>
<p>Die Seite endet schließlich mit dem Schließen aller relevanten Tags. Wenn alles gut geht, sollten Sie die Seite aufrufen können, indem Sie auf den URL <a class="reference external" href="http://ihresite/user_info?userName=[einbenutzer">http://ihresite/user_info?userName=[einbenutzer</a>] zugreifen, wobei <tt class="docutils literal">einbenutzer</tt> ein vorhandener Benutzername in Ihrer Plone-Site ist.</p>
<p>Momentan ist dieses Page Template ziemlich beschränkt. Nur ein Benutzer mit Managerrechten kann diese Seite sehen, sie kann nur jeweils ein Mitglied zu einem Zeitpunkt anzeigen, und die Information über diesen Benutzer ist recht mager. In Kapitel 6 werde ich zeigen, wie Sie dieses Beispiel ausbauen und einiges an Wiederverwendbarkeit von Komponenten hinzufügen - sowie die Möglichkeit, den Text in andere Sprachen zu übersetzen.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch6.rst">
    <title>6. Einführung in Plone-Templating und -Skripting für Fortgeschrittene</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch6.rst</link>
    <description>Das vorangegangene Kapitel behandelte die Funktionsweise des Zope Page Templates-Systems. Um Ihnen Page Templates nahe zu bringen, habe ich in Kapitel 5 auch die Objekthierarchie, Akquisition und TALES  (Template Attribute Language Expression Syntax) behandelt. Mit dem dortigen Code konnten Sie dynamische Webseiten erzeugen. In dem Kapitel haben Sie ein Beispiel-Page Template gesehen, das den Code zusammengesetzt hat. Außerdem wurden darin die Bestandteile des Templating-Systems in Plone behandelt, d.h., Sie haben die Schlüsselinformationen erhalten, die Sie brauchen, um Plone zu benutzen.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Einführung in Plone-Templating und -Skripting für Fortgeschrittene</h2>
<p>Das vorangegangene Kapitel behandelte die Funktionsweise des Zope Page Templates-Systems. Um Ihnen Page Templates nahe zu bringen, habe ich in Kapitel 5 auch die Objekthierarchie, Akquisition und TALES  (Template Attribute Language Expression Syntax) behandelt. Mit dem dortigen Code konnten Sie dynamische Webseiten erzeugen. In dem Kapitel haben Sie ein Beispiel-Page Template gesehen, das den Code zusammengesetzt hat. Außerdem wurden darin die Bestandteile des Templating-Systems in Plone behandelt, d.h., Sie haben die Schlüsselinformationen erhalten, die Sie brauchen, um Plone zu benutzen.</p>
<p>Nun ist es Zeit, zu einigen der weitergehenden Eigenschaften von Page Templates und allgemein zum Templating in Plone überzugehen. Zuerst werde ich dazu METAL  (Macro Expansion Template Attribute Language) und  Internationalisierungs-(I18N-)Namespaces einführen. Wie der TAL-Namespace bieten diese dem Site-Entwickler einiges an Funktionalität. Diejenigen, die ganz genau wissen möchten, wie eine Plone-Seite zusammengesetzt wird, finden im Abschnitt &quot;Sich mit METAL in Plone einklinken&quot; viele Antworten auf ihre Fragen.</p>
<p>Bisher habe ich gezeigt, wie Sie einfache Python-Ausdrücke in Page Templates verwenden können. Natürlich ist manchmal ein einzeiliger Python-Ausdruck nicht ausreichend. Im Abschnitt &quot;Plone mit Python skripten&quot; werde ich zeigen, dass Sie Python eine Ebene höher einsetzen können und Ihre Skripten damit wesentlich mächtiger machen können.</p>
<p>Abschließend werde ich ein häufig vorkommendes Beispiel behandeln, das zeigt, wie man ein Formular in Plone zusammenbaut. Dieses Beispiel demonstriert Konzepte, die in den vorherigen Kapiteln vermittelt wurden, und kombiniert alles miteinander, während Sie genau sehen können, wie Plone mit Formularen umgeht.</p>
<div class="section" id="hintergrund-zu-fortgeschrittenem-plone-templating">
<h3>Hintergrund zu fortgeschrittenem Plone Templating</h3>
<p>Eine der hübschen Eigenschaften von Page Templates ist die, dass verschiedene Funktionen klar in verschiedenen Namespaces voneinander getrennt sind. Im vorigen Kapitel haben Sie die TAL-Namespaces gesehen. Das ist nicht der einzige Namespace, den Page Templates zur Verfügung stellen. Zwei weitere Namespaces sind sehr wichtig für Plone.</p>
<p>Der erste ist METAL. Wie aus dem ziemlich langen Namen hervorgeht, ist er insofern ähnlich zu TAL, als er eine Attributsprache ist und sich selbst in Elementattribute einfügt. Sein Hauptzweck ist jedoch sicherzustellen, dass Sie Codeteile von anderen Page Templates wiederverwenden können, und zwar mit Hilfe von Slots und Makro-Funktionen.</p>
<p>Der zweite ist I18N, mit dem Sie den Inhalt von Page Templates übersetzen können. In Plone wird das dazu verwendet, die Schnittstelle von Plone in über 30 Sprachen zu lokalisieren, was für viele Benutzer eines der Schlüsselmerkmale von Plone ist. Wie Sie sehen werden, ist die Möglichkeit, Texte zu lokalisieren, für alle Benutzer von Interesse, auch für jene, die eine einsprachige Site bauen. Beginnen wir mit METAL.</p>
<div class="section" id="sich-mit-metal-in-plone-einklinken">
<h4>Sich mit METAL in Plone einklinken</h4>
<p>Bisher haben Sie gesehen, wie Sie Teile von Webseiten mit TAL dynamisch generieren können. Damit können Sie allerdings nicht wirklich komplexes Templating betreiben. Es gibt einfach keinen Mechanismus, um oben auf jede Seite einen Standardkopf zu setzen, außer eine TAL-Anweisung zu benutzen. METAL ist eine Methode zur Vorverarbeitung der Templates und bietet einige mächtigere Funktionen als TAL. Alle METAL-Funktionen beginnen mit dem Präfix <tt class="docutils literal">metal:</tt>.</p>
<div class="section" id="metal-define-macro">
<h5>metal:define-macro</h5>
<p>Mit dem Befehl <tt class="docutils literal"><span class="pre">metal:define-macro</span></tt> können Sie ein Element so definieren, dass es von einem anderen Template referenziert werden kann. Der Name des referenzierten Teils ist der Name des Makros. Es folgt ein Beispiel, das <tt class="docutils literal">boxA</tt> als etwas definiert, das Sie anderswo benutzen möchten:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;boxA&quot;&gt;
    ...
&lt;/div&gt;
</pre>
<p>Das <tt class="docutils literal">div</tt>-Element ist nun ein Makro, das aus anderen Templates referenziert werden kann. Das Makro bezieht sich nur auf den Teil der Seite, der von dem Element referenziert wird, was in diesem Fall der <tt class="docutils literal">div</tt>-Tag ist. Es ist üblich, auf einer Seite mehrfach <tt class="docutils literal">macro:defines</tt> zu verwenden, auch damit die Seite eine gültige HTML-Seite ist, z.B. so:</p>
<pre class="literal-block">
&lt;html xmlns:tal=&quot;http://xml.zope.org/namespaces/tal&quot;
    xmlns:metal=&quot;http://xml.zope.org/namespaces/metal&quot;
    i18n:domain=&quot;plone&quot;&gt;
    &lt;body&gt;
        &lt;div metal:define-macro=&quot;boxA&quot;&gt;
            ...
        &lt;/div&gt;
        &lt;div metal:define-macro=&quot;boxB&quot;&gt;
            ...
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Übereinstimmend mit den früheren Zielen von Page Templates ist diese Seite eine gültige HTML-Seite, die von einem Designer bearbeitet werden kann. Wenn das Makro aufgerufen wird, wird das HTML außerhalb der <tt class="docutils literal">div</tt>-Tags weggeworfen.</p>
</div>
<div class="section" id="metal-use-macro">
<h5>metal:use-macro</h5>
<p>Der Befehl <tt class="docutils literal"><span class="pre">metal:use-macro</span></tt> verwendet ein Makro, das mit <tt class="docutils literal"><span class="pre">define-macro</span></tt> definiert wird. Wenn ein Template ein Makro mit dem Befehl <tt class="docutils literal"><span class="pre">define-macro</span></tt> definiert, können andere Templates mit dem <tt class="docutils literal">macros</tt>-Property darauf zugreifen. Wenn Sie z.B. das Makro <tt class="docutils literal">portlet</tt> aus dem Template <tt class="docutils literal">portlet_login</tt> verwenden möchten, können Sie Folgendes machen:</p>
<pre class="literal-block">
&lt;div metal:use-macro=&quot;context/portlet_login/macros/portlet&quot;&gt;
    Der Einloggen-Slot kommt hierhin
&lt;/div&gt;
</pre>
<p>Damit wird das Makro geholt und sein Ergebnis an dessen Stelle eingefügt. Wie Sie sehen, erwartet der Befehl <tt class="docutils literal"><span class="pre">use-macro</span></tt> einen Pfadausdruck, der auf das Template und auf das spezielle Makro darin zeigt.</p>
</div>
<div class="section" id="beispiel-die-makros-use-macro-und-define-macro-verwenden">
<h5>Beispiel: Die Makros use-macro und define-macro verwenden</h5>
<p>Folgendes Template namens <tt class="docutils literal">time_template</tt> ist ein Beispiel hierfür. Dieses Template zeigt Datum und Zeit auf dem aktuellen Plone-Server an. Das ist eine recht nützliche Funktion, d.h., Sie können sie in ein Makro zur weiteren Wiederverwendung verpacken. Das Folgende ist ein Beispiel-Page-Template mit der Anweisung <tt class="docutils literal"><span class="pre">define-macro</span></tt>:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
        &lt;div metal:define-macro=&quot;time&quot;&gt;
            &lt;div tal:content=&quot;context/ZopeTime&quot;&gt;
                 Die Zeit
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Falls Ihr Template <tt class="docutils literal">time_template</tt> heißt, können Sie dieses Makro in einem anderen Template referenzieren. Sie können es auch aus mehreren Templates referenzieren. Hier ist ein solches Beispiel-Template:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
    &lt;div metal:use-macro=&quot;context/time_template/macros/time&quot;&gt;
      Falls es eine Message gibt, zeigt das Makro sie hier an.
    &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Wenn dieses Template dargestellt wird, sieht der von Plone generierte HTML-Code dafür wie folgt aus:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
    &lt;div&gt;
       &lt;div&gt;2004/04/15 17:18:18.312 GMT-7&lt;/div&gt;
    &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="metal-define-slot">
<h5>metal:define-slot</h5>
<p>Ein <em>Slot</em> ist ein Makro-Abschnitt, von dem der Autor des Templates erwartet, dass er von einem anderen Template überschrieben wird. Sie können sich einen Slot als Loch in Ihrem Page Template vorstellen, bei dem Sie davon ausgehen, dass es von einem anderen gefüllt wird. Alle <tt class="docutils literal"><span class="pre">define-slot</span></tt>-Befehle müssen in einem <tt class="docutils literal"><span class="pre">define-macro</span></tt> vorkommen. Beispiel:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;master&quot;&gt;
  &lt;div metal:define-slot=&quot;main&quot;&gt;
  ...
  &lt;/div&gt;
&lt;/div&gt;
</pre>
</div>
<div class="section" id="metal-fill-slot">
<h5>metal:fill-slot</h5>
<p>Hiermit wird ein Slot gefüllt, der mit dem Befehl <tt class="docutils literal"><span class="pre">define-slot</span></tt> definiert wurde. Ein <tt class="docutils literal"><span class="pre">fill-slot</span></tt> muss mit dem Befehl <tt class="docutils literal"><span class="pre">use-macro</span></tt> definiert werden. Wenn der <tt class="docutils literal"><span class="pre">define-macro</span></tt>-Teil aufgerufen wird, versucht das Makro, alle definierten Slots mit dem entsprechenden <tt class="docutils literal"><span class="pre">fill-slots</span></tt> zu füllen. Hier sehen Sie ein Beispiel für einen <tt class="docutils literal"><span class="pre">fill-slot</span></tt>:</p>
<pre class="literal-block">
&lt;div metal:use-macro=&quot;master&quot;&gt;
  &lt;div metal:fill-slot=&quot;main&quot;&gt;
    Der Haupt-Slot kommt hierhin
  &lt;/div&gt;
&lt;/div&gt;
</pre>
</div>
<div class="section" id="beispiel-makros-und-slots-verwenden">
<h5>Beispiel: Makros und Slots verwenden</h5>
<p>Kommen wir zum vorigen Beispiel zurück, das Sie nun ein wenig verbessern werden. Wenn Sie eine spezielle Meldung vor die Zeitangabe setzen möchten, würden Sie einen Slot am Anfang von <tt class="docutils literal">time_template</tt> innerhalb von <tt class="docutils literal"><span class="pre">define-macro</span></tt> hinzufügen. Der Slot heißt <tt class="docutils literal">time</tt> und sieht wie folgt aus:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
        &lt;div metal:define-macro=&quot;time&quot;&gt;
            &lt;div metal:define-slot=&quot;msg&quot;&gt;Time slot&lt;/div&gt;
            &lt;div tal:content=&quot;context/ZopeTime&quot;&gt;
                 Die Zeit
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Im aufrufenden Page Template können Sie nun <tt class="docutils literal"><span class="pre">fill-slot</span></tt> aufrufen:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
    &lt;div metal:use-macro=&quot;context/time_template/macros/time&quot;&gt;
      &lt;div metal:fill-slot=&quot;msg&quot;&gt;Die Zeit ist:&lt;/div&gt;
      Falls es eine Message gibt, zeigt das Makro sie hier an.
    &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Als Endergebnis sehen Sie den wie folgt gefüllten <tt class="docutils literal"><span class="pre">time-slot</span></tt>:</p>
<pre class="literal-block">
&lt;html&gt;
    &lt;body&gt;
    &lt;div&gt;
       &lt;div&gt;Die Zeit ist: &lt;/div&gt;
       &lt;div&gt;2004/04/15 17:18:18.312 GMT-7&lt;/div&gt;
    &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="wie-plone-makros-und-slots-benutzt">
<h5>Wie Plone Makros und Slots benutzt</h5>
<p>Makros und Slots sind sich insofern ähnlich, als beide Inhalte aus einem anderen Template extrahieren und Inhalte einfügen, aber sie tun das auf unterschiedliche Weise. Der Unterschied besteht darin, wie sie benutzt werden. Makros sind Elemente eines Templates, die explizit aufgerufen werden, während Slots wie Löcher in einem Template sind, die von anderen Templates für Sie gefüllt werden sollen. Bei Plone selbst sind Portlets wie Kalender, Navigation etc. Makros, die explizit aufgerufen werden.</p>
<p>Wenn Sie sich im ZMI (Zope Management Interface) eine Datei anschauen, indem Sie hintereinander auf <em>portal_skins</em>, <em>plone_templates</em> und <em>main_template</em> klicken, werden Sie sogar sehen, dass die gesamte Seite aus Makros und Slots besteht. In diesem Stadium ist sie vermutlich ein wenig verwirrend, aber wenn sie aufgerufen wird, läuft sie durch eine Reihe von Makros und trägt alles zusammen. Damit kann ein Benutzer ganz leicht alle Teile einer Plone-Site dadurch ändern, dass er das Makro überschreibt, was Sie im nächsten Kapitel sehen werden. Beispiel:</p>
<pre class="literal-block">
...
&lt;div metal:use-macro=&quot;here/global_siteactions/macros/site_actions&quot;&gt;
    Site-wide actions (Contact, Sitemap, Help, Style Switcher etc)
&lt;/div&gt;

&lt;div metal:use-macro=&quot;here/global_searchbox/macros/quick_search&quot;&gt;
    The quicksearch box, normally placed at the top right
&lt;/div&gt;
...
</pre>
<p>Beim weiteren Herunterscrollen in <tt class="docutils literal">main_template</tt> werden Sie einige define-Slots sehen. Ich wiederhole kurz, wie eine Seite in Plone dargestellt wird. Wenn ein Objekt angezeigt wird, wird ein Template für die Ansicht dieses Inhalts angezeigt. Wenn Sie ein Bild anzeigen, wird das Template <tt class="docutils literal">image_view</tt> angezeigt, und dieses Template bestimmt, wie das Bild angezeigt wird. Um diese Aufgabe zu erfüllen, füllt das Bild-Template den Slot <tt class="docutils literal">main</tt>. Wenn Sie ins Template <tt class="docutils literal">image_view</tt> schauen, sehen Sie darin den folgenden Code:</p>
<pre class="literal-block">
&lt;div metal:fill-slot=&quot;main&quot;&gt;
...
&lt;/div&gt;
</pre>
<p>Wenn Sie zurück zum <tt class="docutils literal">main_template</tt> springen, werden Sie sehen, dass es die Definition <tt class="docutils literal"><span class="pre">define-slot</span></tt> für den Slot <tt class="docutils literal">main</tt> enthält:</p>
<pre class="literal-block">
&lt;metal:bodytext metal:define-slot=&quot;main&quot; tal:content=&quot;nothing&quot;&gt;
    Page body text
&lt;/metal:bodytext&gt;
</pre>
<p>Zu jedem Inhaltstyp gibt es ein eigenes Template, und jedes Template definiert für sich, wie es den Slot <tt class="docutils literal">main</tt> benutzt. Das heißt, jeder Inhaltstyp erhält sein eigenes Look-and-feel aus dem Template. Nur ein Teil fehlt noch in dieser Gleichung. Als Sie <tt class="docutils literal">image_view</tt> aufgerufen haben, wusste das Template irgendwie, dass es <tt class="docutils literal">main_template</tt> verwenden sollte. Im Template <tt class="docutils literal">image_view</tt> benutzen Sie das Makro aus <tt class="docutils literal">main_template</tt>. Dieses wird mit folgendem HTML-Code definiert:</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
    lang=&quot;en-US&quot;
    metal:use-macro=&quot;here/main_template/macros/master&quot;
    i18n:domain=&quot;plone&quot;&gt;
</pre>
<p>In diesem Fall wird in <tt class="docutils literal">main_template</tt> der Slot <tt class="docutils literal">main</tt> von dem als <tt class="docutils literal">main</tt> definierten Slot in demjenigen Template gefüllt, das dargestellt wird. Folgendes ist die zeitliche Abfolge der Ereignisse bei der Darstellung eines Bildes:</p>
<ol class="arabic simple">
<li>Der Benutzer verlangt das Template <tt class="docutils literal">image_view</tt> eines Bildes.</li>
<li>Das Makro <tt class="docutils literal">main_template</tt> oben im Template <tt class="docutils literal">image_view</tt> wird gefunden und aufgerufen.</li>
<li>Das <tt class="docutils literal">main_template</tt> wird verarbeitet.</li>
<li>Das <tt class="docutils literal"><span class="pre">define-slot=&quot;main&quot;</span></tt> wird gefunden, und das <tt class="docutils literal"><span class="pre">fill-slot</span></tt> wird in <tt class="docutils literal">image_view</tt> gefunden.</li>
<li>Das <tt class="docutils literal"><span class="pre">fill-slot</span></tt> wird aufgerufen, und die Ergebnisse werden in <tt class="docutils literal">main_template</tt> eingefügt.</li>
<li>Das <tt class="docutils literal">main_template</tt> wird weiterverarbeitet.</li>
<li>Das Ergebnis wird zum Browser zurückgeschickt.</li>
</ol>
<p>Das ermöglicht Plone eine hohe Flexibilität bei der Definition einer Seite. So definiert z.B. <tt class="docutils literal">main_template</tt> kaum mehr als diesen einen Slot. Es gibt auch einen Slot zum Einfügen von CSS-Code (Cascading Style Sheets):</p>
<pre class="literal-block">
&lt;metal:cssslot fill-slot=&quot;css_slot&quot;&gt;
    &lt;metal:cssslot define-slot=&quot;css_slot&quot; /&gt;
&lt;/metal:cssslot&gt;
</pre>
<p>Falls eine Ansicht einen speziellen CSS-Teil bräuchte, könnten Sie diesen Slot in der Ansicht definieren, und er würde bei der Darstellung gefüllt werden. Einige der Makros in <tt class="docutils literal">main_template</tt> definieren darin auch Slots und füllen sie im <tt class="docutils literal">main_template</tt> wieder. Das heißt, wenn Sie wirklich wollten, könnten Sie auch diese Slots füllen. Allerdings ist das eine Technik für Fortgeschrittene, d.h., Sie sollten erst die Grundlagen beherrschen, bevor Sie sich auf diesen Weg begeben.</p>
</div>
</div>
<div class="section" id="einfuhrung-in-die-internationalisierung">
<h4>Einführung in die Internationalisierung</h4>
<p>Die Macher von Plone sind kontinuierlich bestrebt, eine große Zahl qualitativ hochwertiger Übersetzungen anzubieten. Die Tatsache, dass Plone eine zugängliche Benutzerschnittstelle in über 30 Sprachen bietet, ist ein großes Verkaufsargument für Plone. Das heißt auch, dass I18N ein Schlüsselmerkmal von Templates ist. Das wird dadurch unterstützt, dass der I18N-Namespace ein Extra-Namespace wie TAL oder METAL ist, der spezielle Anweisungen enthält.</p>
<p>Dieser Abschnitt enthält, was Benutzer in Bezug auf Templates wissen müssen. In einem Template können Sie einen <tt class="docutils literal">i18n</tt>-Tag zu einem Element hinzufügen, der die Übersetzung eines Attributs oder seines Inhalts erlaubt. Es sind sechs Anweisungen vorhanden: <tt class="docutils literal">attributes</tt>, <tt class="docutils literal">data</tt>, <tt class="docutils literal">domain</tt>, <tt class="docutils literal">source</tt>, <tt class="docutils literal">target</tt> und <tt class="docutils literal">translate</tt>. Das Grundmuster besteht darin, den zu übersetzenden Text einzuwickeln und die passenden <tt class="docutils literal">i18n</tt>-Attribute hinzuzufügen. Wenn Sie z.B. Folgendes übersetzen möchten:</p>
<pre class="literal-block">
&lt;i&gt;Irgendein Text&lt;/i&gt;
</pre>
<p>würde daraus Folgendes:</p>
<pre class="literal-block">
&lt;i i18n:translate=&quot;some_text_label&quot;&gt;Irgendein Text&lt;/i&gt;
</pre>
<p>Jede Lokalisierung enthält eine Übersetzung von <tt class="docutils literal">Irgendein Text</tt>, und das Translation-Werkzeug sucht nach einer Übersetzung für den Benutzer. Bei der Übersetzung muss jeder zu übersetzende String eine eindeutige Message-ID haben, die das zu übersetzende Element identifiziert. Ein String wie <tt class="docutils literal">Suchen</tt> kann z.B. die Message-ID <tt class="docutils literal">search_widget_label</tt> haben. Mit dieser Message-ID kann der String eindeutig identifiziert werden, und die Übersetzung kann wiederholt werden.</p>
<div class="section" id="i18n-translate">
<h5>i18n:translate</h5>
<p>Folgendes übersetzt den Inhalt eines Elements, wobei eine optionale Message-ID als Anweisung übergeben werden kann. Zum Beispiel erzeugt dieser Code eine Message-ID namens <tt class="docutils literal">title_string</tt>:</p>
<pre class="literal-block">
&lt;h1 i18n:translate=&quot;title_string&quot;&gt;Dies ist ein Titel&lt;/h1&gt;
</pre>
<p>Dieses Beispiel steht für einen statischen Text, der sich nicht verändert. In manchen Situationen könnte dieser Textteil dynamisch aus einer Datenbank oder einem Objekt kommen. Dadurch, dass die Anweisung <tt class="docutils literal">translate</tt> leer gelassen wird, setzt sich die Message-ID aus dem Wert im Feld zusammen. Wenn im folgenden Beispiel der Titel vom Pfadausdruck <tt class="docutils literal">here/title</tt> z.B. <tt class="docutils literal">Alice im Wunderland</tt> wäre, würde dieser Titel an das Translation-Werkzeug übergeben. Wenn keine Übersetzung vorhanden ist, wird der Originalwert eingesetzt:</p>
<pre class="literal-block">
&lt;h1
    tal:content=&quot;here/title&quot;
    i18n:translate=&quot;&quot;&gt;
      Dies ist ein Titel.
&lt;/h1&gt;
</pre>
<p>Der <tt class="docutils literal">translation</tt>-Befehl ist wahrscheinlich einer der häufigsten <tt class="docutils literal">i18n</tt>-Tags, den Sie benutzen werden, und Sie werden ihn in allen Plone-Templates sehen. Damit können Sie nicht nur statische Teile Ihrer Site wie Feldnamen in Formularen, Hilfen und Beschreibungen übersetzen, sondern auch die dynamischen Teile Ihrer Site, die sich öfter verändern können, beispielsweise Titel.</p>
</div>
<div class="section" id="i18n-domain">
<h5>i18n:domain</h5>
<p>Hiermit wird die Domain für die Übersetzung gesetzt. Um Konflikte zu vermeiden, kann jede Site mehrere Domains oder Gruppen von Übersetzungen haben. Es kann z.B. eine Domain für Plone und eine für Ihre eigene Anwendung geben. Plone verwendet die Domain <tt class="docutils literal">plone</tt>, was in Plone normalerweise die Standard-Domain ist:</p>
<pre class="literal-block">
&lt;body i18n:domain=&quot;plone&quot;&gt;
</pre>
<p>Diesen Tag sollten Sie nicht oft benutzen müssen. Wenn Sie jedoch eine eigene Anwendung schreiben, ist es für Sie vielleicht nützlich, eine Domain zu haben, die keine Konflikte mit anderen Domains hervorruft.</p>
</div>
<div class="section" id="ii8n-source">
<h5>Ii8n:source</h5>
<p>Hiermit wird die Ausgangssprache für den zu übersetzenden Text gesetzt. In Plone wird das nicht benutzt:</p>
<pre class="literal-block">
&lt;p i18n:source=&quot;en&quot; i18n:translate=&quot;&quot;&gt;Irgendein Text&lt;/p&gt;
</pre>
</div>
<div class="section" id="i18n-name">
<h5>i18n:name</h5>
<p>Dies bietet eine Möglichkeit, Elemente in einem längeren Textblock beizubehalten, damit der Textblock umgeordnet werden kann. In vielen Sprachen sind nicht nur die Wörter andere, sondern auch ihre Reihenfolge variiert. Wenn Sie einen ganzen Absatz oder einen Satz übersetzen müssen, der kleinere Teile enthält, die nicht übersetzt werden sollen, können diese durchgereicht werden:</p>
<pre class="literal-block">
&lt;p i18n:translate=&quot;book_message&quot;&gt;
    Das
    &lt;span
       tal:omit-tag=&quot;&quot;
       tal:content=&quot;book/color&quot;
       i18n:name=&quot;color&quot;&gt;blaue&lt;/span&gt;
    Buch
&lt;/p&gt;
</pre>
<p>Das produziert den folgenden String:</p>
<pre class="literal-block">
Das {color} Buch
</pre>
<p>Wenn in der Zielsprache verlangt wird, dass diese Wörter in einer anderen Reihenfolge stehen, könnten sie verschoben werden, und der dynamische Inhalt würde weiterhin am richtigen Platz eingesetzt. Auf Französisch müsste das wie folgt übersetzt werden:</p>
<pre class="literal-block">
Le Livre {color}
</pre>
</div>
<div class="section" id="i18n-target">
<h5>i18n:target</h5>
<p>Hiermit wird die Zielsprache für den zu übersetzenden Text gesetzt. In Plone wird das nicht benutzt.</p>
</div>
<div class="section" id="i18n-attributes">
<h5>i18n:attributes</h5>
<p>Dadurch ist es möglich, Attribute in einem Element statt deren Inhalt zu übersetzen. Ein <tt class="docutils literal">image</tt>-Tag hat z.B. das Attribut <tt class="docutils literal">alt</tt>, das eine alternative Textrepräsentation des Bildes enthält:</p>
<pre class="literal-block">
&lt;img
    href=&quot;/einBild.jpg&quot;
    alt=&quot;Irgendein Text&quot;
    i18n:attributes=&quot;alt alternate_image_label&quot; /&gt;
</pre>
<p>Mehrere Attribute sollten durch ein Semikolon getrennt werden, genau wie bei <tt class="docutils literal">tal:attributes</tt>.</p>
</div>
<div class="section" id="i18n-data">
<h5>i18n:data</h5>
<p>Damit können andere Dinge als Strings übersetzt werden. Ein Beispiel dafür ist das Objekt <tt class="docutils literal">DateTime</tt>. Eine <tt class="docutils literal">i18n:data</tt>-Anweisung benötigt eine passende <tt class="docutils literal">i18n:translate</tt>-Anweisung, damit eine gültige Message-ID verfügbar ist. Beispiel:</p>
<pre class="literal-block">
&lt;span i18n:data=&quot;here/currentTime&quot;
    i18n:translate=&quot;timefmt&quot;
    i18n:name=&quot;time&quot;&gt;2:32 pm&lt;/span&gt;... Piep!
</pre>
</div>
<div class="section" id="ubersetzungsdienst">
<h5>Übersetzungsdienst</h5>
<p>Nachdem ich die Tags nun behandelt habe, werde ich jetzt den Mechanismus für die Durchführung der Übersetzung beschreiben. Plone enthält standardmäßig einen I18N-Mechanismus. Mit diesem können Sie die Benutzerschnittstelle so lokalisieren, dass Sie Meldungen, Reiter und Formulare übersetzen können. Der von Benutzern hinzugefügte Inhalt wird im Moment davon nicht erfasst. Wenn Sie ein Dokument in englischer Sprache hinzufügen und die Seite anzeigen, die es auf Französisch abruft, erhalten Sie das englische Dokument mit französischem Text drumherum (siehe Abbildung 6.1).</p>
<a class="reference external image-reference" href="img/06-01.png/image_view_fullscreen"><img alt="img/06-01.png" class="original" src="img/06-01.png" /></a>
<p>Abildung 6.1. Plone.org auf Französisch</p>
<p>Plone liest die HTTP-Header, die ein Browser an den Client sendet, um eine Sprache abzufragen. Wenn in Ihrem Browser Englisch eingestellt ist, werden Sie nicht viel sehen.</p>
<p>Tun Sie Folgendes, um die Spracheinstellungen im Internet Explorer zu ändern:</p>
<ol class="arabic simple">
<li>Gehen Sie zu <em>Werkzeuge - Internet-Optionen</em>.</li>
<li>Klicken Sie auf den <em>Sprache</em>-Button unten im Dialogfeld.</li>
<li>Klicken Sie auf <em>Hinzufügen</em>, um eine neue Sprache hinzuzufügen, und wählen Sie eine Sprache, für die Plone eine Übersetzung hat, z.B. Französisch (Frankreich) [fr], und klicken Sie auf <em>Hinzufügen</em>.</li>
<li>Stellen Sie dann sicher, dass diese Sprache oben ist, indem Sie den <em>Nach oben</em>-Button verwenden.</li>
<li>Klicken Sie auf <em>OK</em> und noch einmal auf <em>OK</em>.</li>
</ol>
<p>Wenn Sie damit fertig sind, wählen Sie Ihre bevorzugte Plone-Site, und besuchen Sie diese mit Ihrem Browser.</p>
<p>Die Übersetzungen für Plone werden mit einem Werkzeug namens Placeless Translation Service (PTS) vorgenommen. Das PTS-Werkzeug finden Sie im Zope-Control Panel. Unten auf der Seite sehen Sie eine Option für <em>Placeless Translation Service</em>. Klicken Sie darauf, und es werden alle vorhandenen Übersetzungen geöffnet. Diese Übersetzungen werden aus dem Dateisystem gelesen. Klicken Sie auf eine Übersetzung, um Angaben über die Sprache wie den Übersetzer, die Codierung und den Pfad zur Datei zu sehen. All diese Dateien werden im <tt class="docutils literal">i18n</tt>-Unterverzeichnis vom <tt class="docutils literal">CMFPlone</tt>-Verzeichnis gespeichert.</p>
<p>Übersetzungen werden jeweils mit zwei Dateien mit den Endungen <tt class="docutils literal">.po</tt> und <tt class="docutils literal">.mo</tt> vorgenommen. So enthält z.B. <tt class="docutils literal"><span class="pre">plone-de.po</span></tt> die deutschen Übersetzungen (<tt class="docutils literal">de</tt> ist der Code für Deutsch). Die <tt class="docutils literal">.mo</tt>-Datei ist die &quot;kompilierte&quot; Version der <tt class="docutils literal">.po</tt>-Datei und wird aus Performance-Gründen von Plone benutzt. In die <tt class="docutils literal">.mo</tt>-Datei müssen Sie nie hineinschauen, und Sie können sie einfach ignorieren. Die <tt class="docutils literal">.po</tt>-Datei ist jene, die Sie bearbeiten können, um eine Übersetzung zu ändern. Wenn Sie diese Datei in einem Texteditor öffnen, sehen Sie eine Reihe von Zeilen, die mit <tt class="docutils literal">msgid</tt> oder <tt class="docutils literal">msgstr</tt> anfangen. Über dem <tt class="docutils literal">msgid</tt> befindet sich der Code, wo der <tt class="docutils literal">i18n</tt>-Befehl vorkommt, d.h., Sie sehen, welchen Teil einer Seite Sie übersetzen. Beispiel:</p>
<pre class="literal-block">
#: from plone_forms/content_status_history.pt
#.   &lt;input attributes=&quot;tabindex tabindex/next;&quot; value=&quot;Apply&quot;
class=&quot;context&quot; name=&quot;workflow_action_submit&quot; type=&quot;submit&quot; /&gt;
#.
#: from plone_forms/personalize_form.pt
#.   &lt;input attributes=&quot;tabindex tabindex/next;&quot; tabindex=&quot;&quot;
value=&quot;Apply&quot; class=&quot;context&quot; type=&quot;submit&quot; /&gt;
#.
msgid &quot;Apply&quot;
msgstr &quot;Anwenden&quot;
</pre>
<p>In den zwei Teilen des vorigen Page Templates wird das Wort <em>Apply</em> für deutsche Benutzer in das Wort <em>Anwenden</em> übersetzt. Was übersetzt wird, bestimmen die <tt class="docutils literal">i18n</tt>-Tags, die in die Page Templates eingefügt wurden, wie Sie zuvor schon gesehen haben. Wenn Sie diese Übersetzung ändern möchten oder wenn Sie eine eigene Variante davon hinzufügen möchten, müssen Sie lediglich die <tt class="docutils literal">.po</tt>-Datei ändern. Falls kein <tt class="docutils literal">msgstr</tt> gefunden wird, wird die englische Standardübersetzung gefunden. Nach einer solchen Änderung müssen Sie Plone neu starten. Dabei wird Plone diese Datei in die Version <tt class="docutils literal">.mo</tt> neu kompilieren und dann Ihre aktualisierte Übersetzung verwenden.</p>
<p>Was Plone angeht, so wird per Standardeinstellung immer die englische Übersetzungsdatei verwendet, falls keine Sprache angegeben oder keine Übersetzung verfügbar ist. Tatsächlich ist die Datei <tt class="docutils literal"><span class="pre">plone-en.po</span></tt> leer, d.h., es ist keine solche Übersetzung verfügbar. Deswegen greift Plone zum letzten Mittel, nimmt keine Übersetzung vor, und zeigt den Text im Page Template an. Der Text in allen Page Templates liegt in englischer Sprache vor, da die meisten Entwickler englisch sprechen. Zusammengefasst heißt das, dass es keine englische Übersetzung gibt.</p>
<p>Deswegen können Sie eine neue Übersetzung dadurch machen, dass Sie die Datei <tt class="docutils literal">plone.pot</tt> in eine neue Datei namens <tt class="docutils literal"><span class="pre">plone-xx.po</span></tt> kopieren. Dabei sollte der Wert von <tt class="docutils literal">xx</tt> dem Landescode Ihrer Übersetzung entsprechen. Eine Liste von Sprachcodes finden Sie unter <a class="reference external" href="http://www.unicode.org/onlinedat/languages.html">http://www.unicode.org/onlinedat/languages.html</a>. Sobald Sie mit der Übersetzung angefangen haben, setzen Sie die obigen Werte, inklusive Sprachcode, und übersetzen drauflos. Wenn Sie eine neue Sprachdatei vollendet haben, wird das Plone-I18N-Team diese glücklich entgegennehmen und Ihnen bei der Fertigstellung helfen. Die Mailing-Liste des Plone-Teams finden Sie unter <a class="reference external" href="http://sourceforge.net/mailarchive/forum.php?forum_id=11647">http://sourceforge.net/mailarchive/forum.php?forum_id=11647</a>.</p>
<p>Den von Benutzern hinzugefügten Inhalt zu übersetzen, ist eine wesentlich komplexere Angelegenheit, bei der die Tücke im Detail liegt. Bislang gab es hier den Ansatz mit <tt class="docutils literal">I18NLayer</tt>, der aber aufgrund von Seiteneffekten nicht einfach anzuwenden war. Inzwischen wurde von PloneSolutions das Nachfolgeprodukt <em>LinguaPlone</em> entwickelt, das wesentlich weniger Seiteneffekte hat und dem Ersteller von Inhalten wesentlich weniger Kenntnis der Materie abverlangt. Als dieser Abschnitt geschrieben wurde, war LinguaPlone in der Version 0.7.2 auf der Webseite von PloneSoutions (<a class="reference external" href="http://www.PloneSolution.com">http://www.PloneSolution.com</a>) verfügbar. Beim Einrichten von LinguaPlone muss man allerdings noch einige manuelle Schritte vornehmen und sicherstellen, dass Archetypes 1.3 und ATContentTypes installiert sind. Erst bei der Plone-Version 2.1 wird LinguaPlone standardmäßig mit ausgeliefert und installiert werden.</p>
</div>
</div>
<div class="section" id="beispiel-mehr-benutzerinformationen-anzeigen">
<h4>Beispiel: Mehr Benutzerinformationen anzeigen</h4>
<p>In Kapitel 5 haben Sie einfache TAL-Befehle benutzt, um detailliertere Angaben zu einem Benutzer anzuzeigen. Das dortige Template hat einige Nachteile. Einer davon ist, dass es nur jeweils einen Benutzer anzeigt. Wie Sie gesehen haben, können Sie mit einem einfachen <tt class="docutils literal">tal:repeat</tt> Inhalt wiederholen, aber jetzt werden Sie ein Makro verwenden, um diese Seite modularer zu machen.</p>
<p>Sie werden das Page Template <tt class="docutils literal">user_info</tt> so ändern, dass es alle Site-Mitglieder auflistet. Anstatt nach einem in der Anfrage übergebenen Benutzernamen zu suchen, werden Sie dabei die Funktion <tt class="docutils literal">listMembers</tt> benutzen, die eine Liste aller Mitglieder der Site zurückgibt:</p>
<pre class="literal-block">
&lt;div metal:fill-slot=&quot;main&quot;&gt;
  &lt;tal:block
   tal:define=&quot;
   getPortrait nocall: here/portal_membership/getPersonalPortrait;
   getFolder nocall: here/portal_membership/getHomeFolder
   &quot;&gt;
   &lt;table&gt;
     &lt;tr tal:repeat=&quot;userObj here/portal_membership/listMembers&quot;&gt;
         &lt;metal:block
          metal:use-macro=&quot;here/user_section/macros/userSection&quot; /&gt;
     &lt;/tr&gt;
   &lt;/table&gt;
 &lt;/tal:block&gt;
&lt;/div&gt;
</pre>
<p>Sie werden bemerken, dass nun der Code für <tt class="docutils literal">user_info</tt> wesentlich kürzer ist. Das von <tt class="docutils literal">listMembers</tt> zurückgegebene Mitglied wird an <tt class="docutils literal">tal:repeat</tt> weitergegeben. Für jedes Mitglied wird es eine Tabellenzeile geben und dann ein Makro, um dem Benutzer Informationen anzuzeigen. In dieser Tabellenzeile enthält die lokal definierte Variable <tt class="docutils literal">userObj</tt> nun die Benutzerangaben. Natürlich müssen Sie nun ein Makro namens <tt class="docutils literal">userSection</tt> in einem Page Template erstellen, d.h., Sie erstellen ein Page Template namens <tt class="docutils literal">user_section</tt>, wie es vom Makro referenziert wird. Dieses Template enthält den gesamten Code zwischen den <tt class="docutils literal">row</tt>-Tags der Tabelle. Auch hierfür finden Sie das vollständige Listing für dieses Page Template in Anhang B:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;userSection&quot;
     tal:define=&quot;userName userObj/getUserName&quot;&gt;
     ...
</pre>
<p>Die einzige wirkliche Änderung besteht darin, dass <tt class="docutils literal"><span class="pre">use-macro</span></tt> im Haupt-Template entfernt werden und ein neues Makro definiert werden muss, damit dieses Makro definiert werden kann. Weil der Benutzername nicht mehr explizit übergeben wird, müssen Sie ihn mit der Methode <tt class="docutils literal">getUserName</tt> aus dem Benutzerobjekt holen. Um die Ergebnisseite zu testen, gehen Sie zu <tt class="docutils literal"><span class="pre">http://ihresite/user_info</span></tt>, wo Sie eine Liste der Benutzer sehen sollten.</p>
<p>Nun ist die Seite benutzerfreundlich, da sie mehrere Benutzer auf einmal anzeigt. Der Code ist modularer, da er die Benutzerangaben in einem separaten Makro darstellt, das unabhängig vom Rest verändert werden kann. Die Seite ist noch nicht perfekt, wird aber in späteren Kapiteln noch verbessert.</p>
</div>
<div class="section" id="beispiel-ein-neues-portlet-mit-google-ads-erstellen">
<h4>Beispiel: Ein neues Portlet mit Google Ads erstellen</h4>
<p>In Kapitel 4 haben Sie gesehen, wie Sie die Portlets in einer Plone-Site einfach bearbeiten können. Ihr eigenes Portlet zu erstellen ist nicht viel schwerer. Um Ihren eignen Slot zu schreiben, müssen Sie ein neues Page Template mit einem Makro darin erstellen. Dann wird ein TALES-Ausdruck, der auf das Makro zeigt, zur Liste der Portlets hinzugefügt, wodurch das Portlet auf der Seite angezeigt wird.</p>
<p>Das Grund-Template für ein Portlet sieht wie folgt aus:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;portlet&quot;&gt;
    &lt;div class=&quot;portlet&quot;&gt;
      &lt;!-- Geben Sie hier Code ein --&gt;
    &lt;/div&gt;
&lt;/div&gt;
</pre>
<p>Alles, was Sie tun müssen, ist, den passenden Code ins Portlet einzufügen. Google hat 2003 ein textbasiertes Anzeigensystem erstellt, das Text auf Ihrer Site unterbringt. Die Anzeigen basieren auf Googles Annahmen darüber, worum es bei Ihrer Site geht, und diese Annahmen basieren auf den Suchergebnissen zu Ihrer Site. Das Google-System ist unter <a class="reference external" href="http://www.google.com/adsense">http://www.google.com/adsense</a> verfügbar. Um Anzeigen anzuzeigen (und Geld dafür zu bekommen), müssen Sie sich bei Google registrieren. Auf der Google-Website werden Sie gebeten, einige Farben und Stile auszuwählen. Da Sie das in einen Slot einsetzen werden, empfehle ich die Größe &quot;skyscraper&quot;, d.h. hoch und dünn. Machen Sie eine Kopie von dem JavaScript, das die Site produziert.</p>
<p>Als Nächstes müssen Sie ein Portlet erstellen:</p>
<ol class="arabic simple">
<li>Erstellen Sie im Ordner <tt class="docutils literal">portal_skins/custom</tt> ein Page Template namens <tt class="docutils literal">googleAds</tt>.</li>
<li>Nehmen Sie den vorigen Template-Grundcode, und ändern Sie den Portlet-Namen auf <tt class="docutils literal">googleBox</tt>.</li>
<li>Setzen Sie den Code von Google ein, wobei Sie den Abschnitt <tt class="docutils literal"><span class="pre">&lt;!--</span> Geben Sie hier Code ein <span class="pre">--&gt;</span></tt> ersetzen.</li>
</ol>
<p>Das Endergebnis sollte Listing 6.1 ähneln. Ihre Version wird allerdings einen gültigen Wert für <tt class="docutils literal">google_ad_client</tt> enthalten statt <tt class="docutils literal">yourUniqueValue</tt>. Dieser Wert sagt Google, welche Site diese Anzeige angefordert hat und wer dafür bezahlt werden soll. Merkwürdigerweise wird Google, wenn Sie keinen gültigen Wert dafür haben, die Anzeigen trotzdem anzeigen, ohne Sie dafür zu bezahlen!</p>
<p>Listing 6.1. Anzeigen von Google anzeigen</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;portlet&quot;&gt;
    &lt;div class=&quot;portlet&quot;&gt;
&lt;script type=&quot;text/javascript&quot;&gt;&lt;!--
google_ad_client = &quot;yourUniqueValue&quot;;
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = &quot;120x600_as&quot;;
//--&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;
  src=&quot;http://pagead2.googlesyndication.com/pagead/show_ads.js&quot;&gt;
&lt;/script&gt;

   &lt;/div&gt;
&lt;/div&gt;
</pre>
<p>Um das auf Ihrer Site einzufügen, fügen Sie folgendes Portlet zu Ihrer Portlet-Liste hinzu, wie es in Kapitel 4 beschrieben wurde:</p>
<pre class="literal-block">
here/googleAds/macros/portlet
</pre>
</div>
</div>
<div class="section" id="plone-mit-python-skripten">
<h3>Plone mit Python skripten</h3>
<p>In Plone existieren mindestens vier verschiedene Ebenen, auf denen eine Programmlogik erstellt werden kann. Die einfachste Ebene, auf der Python in Plone verwendet werden kann, ist der Python-TALES-Ausdruck, den ich im vorigen Kapitel besprochen habe. Ein Python-Ausdruck darf allerdings nur eine Zeile Code enthalten, und oftmals werden Sie etwas Komplexeres machen wollen.</p>
<p>Noch häufiger kommt es vor, dass Sie nur ungern die ganze Logik in das Template hineinzwängen wollen. Ganz allgemein ist es keine gute Idee, Anwendungslogik in Ihr Template zu setzen. Immer, wenn Sie etwas, das nicht explizit Präsentationslogik ist, aus Ihrem Template hinaus verlegen können, haben Sie eine Menge Kopfschmerzen gespart. Die Trennung von Anwendungslogik und Präsentation ermöglicht es, dass verschiedene Leute an unterschiedlichen Projektteilen arbeiten, und sie verbessert die Wiederverwendung von Code. Die anderen Ebenen, auf denen Skripten zu Plone hinzugefügt werden können, kommen ungefähr in folgender Reihenfolge vor:</p>
<ul class="simple">
<li><strong>Template-Attributausdrücke</strong>: Diese bieten Ausdrücke und eine Möglichkeit, kleine Schnipsel mit Logik oder einfachen Pfaden an vielen Orten einzufügen.</li>
<li><strong>Script (Python)-Objekte</strong>: Dies sind einfache Skripten, die in einer eingeschränkten Umgebung in Plone ausgeführt werden.</li>
<li><strong>Externe Methodenobjekte</strong>: Dies sind kompliziertere Module, die nicht in eingeschränkten Umgebungen ausgeführt werden.</li>
<li><strong>Python-Produkte</strong>: Daraus bestehen die wichtigsten Quellen, in denen das CMF und Plone geschrieben sind. Damit hat man Zugriff auf alles in Plone. Python-Produkte sind ein Thema für Fortgeschrittene und werden in Kapitel 14 behandelt.</li>
</ul>
<p>Nach einem Ausdruck ist ein <em>Script (Python)-Objekt</em> die nächstkompliziertere Stufe. Dieses Objekt ermöglicht mehrere Zeilen Python-Code, und Sie können es aus einem Ausdruck aufrufen. Wenn Sie ein Script (Python)-Objekt aufrufen, fällt ein kleiner zusätzlicher Aufwand an, weil Plone zu diesem Objekt umschaltet. Der Aufwand ist jedoch minimal, da es einen Kompromiss zwischen Klarheit, Trennung und Performance gibt. Mein Rat ist der, so viel Logik wie möglich nach Python zu verlagern und Page Templates so einfach und sauber wie möglich zu halten. Wenn es ein Performance-Problem gibt, kann man sie später leicht wieder zurückverlegen, aber zumindest werden Sie später verstehen, was passiert.</p>
<div class="section" id="script-python-objekte-verwenden">
<h4>Script (Python)-Objekte verwenden</h4>
<p>Ein Script (Python)-Objekt ist das, woran Sie in Plone normalerweise bei einem Skript denken. Es ist ein Python-Schnipsel, den Sie schreiben und dann aus anderen Templates oder direkt über das Web aufrufen können. Tatsächlich verfügt Plone über eine große Anzahl dieser Skripten, um verschiedene wichtige Funktionen auszuführen. Was seine Mächtigkeit angeht, liegt ein Python-Skript etwa auf halbem Weg zwischen einem Ausdruck und einer externen Methode.</p>
<p>Um ein Script (Python)-Objekt hinzuzufügen, gehen Sie ins ZMI, wählen <em>Script (Python)</em> im Dropdown-Menü und klicken auf <em>Add</em>, wie in Abbildung 6.2 zu sehen ist.</p>
<a class="reference external image-reference" href="img/06-02.png/image_view_fullscreen"><img alt="img/06-02.png" class="original" src="img/06-02.png" /></a>
<p>Abbildung 6.2. Ein Script (Python)-Objekt hinzufügen</p>
<p>Geben Sie dem Skript z.B. die ID <strong>test_py</strong>, und klicken Sie dann auf <em>Add and Edit</em>. Danach wird die <em>Bearbeiten</em>-Seite für das Script (Python)-Objekt geöffnet, die aussieht wie in Abbildung 6.3.</p>
<a class="reference external image-reference" href="img/06-03.png/image_view_fullscreen"><img alt="img/06-03.png" class="original" src="img/06-03.png" /></a>
<p>Abbildung 6.3. Ein Script (Python)-Objekt bearbeiten</p>
<p>Sie können das Skript direkt über das Web bearbeiten. Falls Sie einen Syntaxfehler machen, erfahren Sie das, gleich nachdem Sie auf <em>Save Changes</em> geklickt haben (siehe Abbildung 6.4).</p>
<a class="reference external image-reference" href="img/06-04.png/image_view_fullscreen"><img alt="img/06-04.png" class="original" src="img/06-04.png" /></a>
<p>Abbildung 6.4. Ein absichtlicher Einrückungsfehler im Script (Python)-Objekt</p>
<p>Falls es keinen Fehler in Ihrem Python-Skript gibt, können Sie den <em>Test</em>-Reiter anklicken, um zu sehen, wie die Ausgabe aussieht. In diesem Fall ist das Beispiel recht langweilig und gibt folgenden Text aus:</p>
<pre class="literal-block">
This is the Script (Python) &quot;test_py&quot; in
http://gloin:8080/Plone/portal_skins/custom
</pre>
<p>Ein Skript verfügt auch über die folgenden Optionen:</p>
<ul class="simple">
<li><strong>Title</strong>: Das <em>Bearbeiten</em>-Formular hat eine <em>Title</em>-Option, mit der Sie dem Skript einen Titel geben können. Dieser wird im ZMI angezeigt, d.h., man erinnert sich dann leichter daran, was es macht.</li>
<li><strong>Parameter List</strong>: Dies ist eine Liste von Parametern, die das Skript erwartet, z.B. <tt class="docutils literal">variableA</tt> oder <tt class="docutils literal">variableB=None</tt>. Dies ist sogar die Standardparameterliste, die Sie in einer normalen Python-Funktion erwarten würden. Manche Parameter sind in diesem Objekt allerdings schon für Sie definiert. Sie können Sie sehen, wenn Sie auf den Reiter <em>Bindings</em> klicken. Dort sehen Sie eine Liste der Variablen, die bereits an das Objeckt gebunden sind. Mit ihren Namen sollten Sie schon vertraut sein.</li>
</ul>
<p>Es gibt folgende an das Skript gebundene Variablen, auf die von einem Script (Python)-Objekt zugegriffen werden kann:</p>
<ul class="simple">
<li><strong>context</strong>: Dies ist das Objekt, auf dem das Skript aufgerufen wird.</li>
<li><strong>container</strong>: Dies ist das Objekt, das dieses Skript enthält.</li>
<li><strong>script</strong>: Dies ist das Script (Python)-Objekt selbst. Das Äquivalent in Zope Page Templates ist <tt class="docutils literal">template</tt>.</li>
<li><strong>namespace</strong>: Dies ist für den Fall da, dass das Skript aus DTML (Document Template Markup Language) heraus aufgerufen wird - etwas, was in Plone nicht passiert.</li>
<li><strong>traverse_subpath</strong>: Dies ist der URL-Pfad nach dem Skriptnamen, was eine Eigenschaft für Fortgeschrittene ist.</li>
</ul>
<p>Ich werde nun ein einfaches Beispiel zeigen, das diese Themen mit dem Zope Page Templates-System verbindet, wobei der Python-Ausdruck zur Addition zweier Zahlen verwendet wird, den ich im vorigen Kapitel angegeben habe. Wie Sie gesehen haben, konnten Sie ein Page Template dafür anlegen, das wie folgt aussieht:</p>
<pre class="literal-block">
&lt;p&gt;1 + 2 = &lt;em tal:content=&quot;python: 1 + 2&quot; /&gt;&lt;/p&gt;
</pre>
<p>Das Äquivalent mit einem Script (Python)-Objekt sieht wie folgt aus. Ändern Sie das Skript <tt class="docutils literal">test_py</tt> auf die folgende Zeile:</p>
<pre class="literal-block">
return 1+2
</pre>
<p>Wie Sie am Anfang des vorigen Kapitels gesehen haben, rufen Sie ein Objekt auf, indem Sie seinen Pfad als Ausdruck angeben. In einem Page Template können Sie also Folgendes machen:</p>
<pre class="literal-block">
&lt;p&gt;1 + 2 = &lt;em tal:content=&quot;here/test_py&quot; /&gt;&lt;/p&gt;
</pre>
<p>Durch den Pfadausdruck erhalten Sie das Objekt <tt class="docutils literal">test_py</tt> und rufen es auf. Dann gibt es den Python-Code ans Template zurück und gibt es aus. Soeben haben Sie ein Skript aus einem Template aufgerufen! Dies ist ganz offensichtlich ein einfaches Beispiel, aber der Punkt ist der, dass Sie in einem Script (Python)-Objekt eine ganze Menge dessen tun können, was Sie in einem Page Template nicht können.</p>
<p>In einem Script (Python)-Objekt können Sie den Titel, Parameter und Bindungseinstellungen angeben, indem Sie die <tt class="docutils literal">##</tt>-Notation am Anfang eines Skripts verwenden. Wenn Sie ein Skript mit diesem Stück Text oben speichern, wird Plone diese Zeile entfernen und stattdessen den entsprechenden Wert im Objekt ändern. Diese Syntax wird sehr oft im Script (Python)-Objekt in diesem Buch verwendet, um sicherzugehen, dass Sie über den richtigen Titel und die richtigen Parameter verfügen. Das heißt, Sie könnten das vorige Skript wie folgt umschreiben:</p>
<pre class="literal-block">
##title=Ergibt 1+2
##parameters=
return 1+2
</pre>
<div class="section" id="plone-skripten">
<h5>Plone skripten</h5>
<p>Plone mit Skripten zu steuern ist ein recht kompliziertes Unterfangen, weil Sie, sobald Sie Plone skripten können, die API (Application Programming Interface) aller Objekte und Werkzeuge beachten müssen, die Sie verwenden möchten. Eine Erklärung der APIs würde den Rahmen des Buches sprengen, daher werde ich demonstrieren, wie man einige einfache Aufgaben mit Script (Python)-Objekten erledigt. Wenn Sie einmal etwas vertrauter damit sind, werde ich weitere API-spezifische Funktionen beschreiben.</p>
<p>Page Templates können recht schön über Python-Dictionaries und -Listen iterieren. Aber oftmals liegen Ihre Daten nicht in einem dieser bequemen Formate vor, d.h., Sie müssen zu einem Script (Python)-Objekt greifen, um die Daten hübsch zu formatieren und sie an das Page Template zurück zu übergeben.</p>
<p>Am bequemsten ist das Datenformat einer Liste von Dictionaries, bei der Sie die Mächtigkeit eines <tt class="docutils literal">tal:repeat</tt> mit einem Pfadausdruck in einer Funktion kombinieren können. Als Beispiel werden Sie eine Funktion sehen, die eine Liste von Objekten erwartet. Jedes dieser Objekte ist ein Objekt in einem Ordner. Und jedes Objekt wird dann vorhanden sein, wenn es in den letzten fünf Tagen aktualisiert worden ist. Listing 6.2 zeigt ein nützliches kleines Portlet, das ich für eine Site erstellt habe, die genau diese Art von Informationen suchen und die entsprechenden Einträge hervorheben sollte.</p>
<p>Listing 6.2. Bis zu fünf Tage alte Objekte zurückgeben</p>
<pre class="literal-block">
##title=recentlyChanged
##parameters=objects
from DateTime import DateTime

now = DateTime()
difference = 5 # in Tagen
result = []

for object in objects:
  diff = now - object.bobobase_modification_time()
  if diff &lt; difference:
    dct = {&quot;object&quot;:object,&quot;diff&quot;:int(diff)}
    result.append(dct)

return result
</pre>
<p>In diesem Script (Python)-Objekt habe ich ein paar neue Konzepte benutzt. Zuerst einmal importieren Sie Zopes <tt class="docutils literal">DateTime</tt>-Modul mit der Anweisung <tt class="docutils literal">import</tt>. Das Modul <tt class="docutils literal">DateTime</tt>, das in Anhang C behandelt wird, bietet Zugriff auf Datumsangaben. Es ist ziemlich einfach, aber wenn Sie ein neues <tt class="docutils literal">DateTime</tt>-Objekt ohne Parameter anlegen, erhalten Sie das aktuelle Datum und die aktuelle Zeit, und zwar in der Variablen <tt class="docutils literal">now</tt>. Wenn Sie zwei <tt class="docutils literal">DateTime</tt>-Objekte subtrahieren, erhalten Sie die Anzahl der Tage dazwischen. Diese können Sie mit der Anzahl vergleichen, die ein Benutzer überwachen möchte, und wenn sie für ein Objekt größer ist, können Sie dieses zur Ergebnisliste hinzufügen. Das Ergebnis ist eine Liste von Dictionary-Objekten, die wie in Listing 6.3 aussieht.</p>
<p>Listing 6.3. Das Ergebnis von Listing 6.2</p>
<pre class="literal-block">
[
  {
      'diff': 1,
      'object': &lt;PloneFolder instance at 02C0C110&gt;
  },
  {
l      'diff': 4,
      'object': &lt;PloneFolder instance at 02FE3321&gt;
  },
  ...
</pre>
<p>Nun, da Sie die Ergebnisse in der richtigen Reihenfolge haben, benötigen Sie ein Page Template, das die Liste der Objekte übergibt und die Ergebnisse verarbeitet. Hier ist ein Beispiel dafür:</p>
<pre class="literal-block">
&lt;ul&gt;
  &lt;li tal:repeat=&quot;updated python: context.updateScript(context.contentValues())&quot;&gt;
</pre>
<p>Dieses Template enthält zu Beginn einen Aufruf <tt class="docutils literal">tal:repeat</tt>, der das Skript aufruft (in diesem Fall wird <tt class="docutils literal">updateScript</tt> aufgerufen). An diese Funktion übergibt es einen Wert, und zwar eine Liste von <tt class="docutils literal">contentValues</tt> aus dem aktuellen Kontext. Vorher haben Sie das Script (Python)-Objekt mit einem Pfadausdruck aufgerufen, was Sie hier mit <tt class="docutils literal">context/updateScript</tt> auch tun könnten. Allerdings können Sie mit dieser Syntax keine Parameter an das aufgerufene Skript übergeben, also nehmen Sie stattdessen einen Python-Ausdruck, <tt class="docutils literal">python: context.updateScript()</tt>. Die Funktion <tt class="docutils literal">contentValues</tt> gibt eine Liste aller Inhaltsobjekte in einem Ordner zurück. Betrachten Sie nun den Code für jede Iteration:</p>
<pre class="literal-block">
  &lt;a href=&quot;#&quot;
     tal:attributes=&quot;href updated/object/absolute_url&quot;
     tal:content=&quot;updated/object/title_or_id&quot;&gt;
     Der Titel des Artikels&lt;/a&gt;
  Vor &lt;em tal:content=&quot;updated/diff&quot; /&gt; Tagen
  &lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Wie Sie sehen, können Sie über diese Liste von Werten iterieren, und Sie können dann Pfadausdrücke benutzen, um zuerst auf den wiederholten Wert (<tt class="docutils literal">updated</tt>), dann auf das Objekt (<tt class="docutils literal">object</tt>) und dann auf eine Methode dieses Objekts (<tt class="docutils literal">title_or_id</tt>) zuzugreifen. Dies ist ein Beispiel für die Auslagerung einer komplizierten Logik in ein Script (Python)-Objekt.</p>
</div>
<div class="section" id="eingeschranktes-python">
<h5>Eingeschränktes Python</h5>
<p>Ich habe mehrfach erwähnt, dass Script (Python)-Objekte und Python-TAL-Ausdrücke alle in einem <em>eingeschränkten</em> Python-Modus ausgeführt werden. Eingeschränktes (Restricted) Python ist eine Umgebung, aus der einige Funktionen entfernt worden sind. Diese Funktionen sind in einer Web-Umgebung wie Plone potenziell gefährlich. Der ursprüngliche Grund ist der, dass Sie es zwar mit authentifizierten Benutzern zu tun haben, denen Sie aber nicht trauen können und die Python-Code auf Ihrer Site ausführen. Wenn Sie bei einem der vielen Zope-Webhosts einen Account eröffnen, werden Sie feststellen, dass Sie das können. Wenn Sie den Leuten jedoch das Recht geben, das zu tun, möchten Sie nicht, dass sie Zugriff auf bestimmte Dinge wie das Dateisystem haben.</p>
<p>In eingeschränktem Python wurden aus Sicherheitsgründen einige bekannte Python-Funktionen entfernt, insbesondere sind <tt class="docutils literal">dir</tt> und <tt class="docutils literal">open</tt> nicht verfügbar. Das heißt, dass wie bei Script (Python)-Objekten keine Introspektion darauf möglich ist und der Zugriff auf das Dateisystem beschränkt ist. Einige Python-Module stehen dem Benutzer aber zur Verfügung. Die meisten davon sind für erfahrene Entwickler gedacht. Weitere Informationen finden Sie in der Dokumentation oder im entsprechenden Modul-Code:</p>
<ul class="simple">
<li><strong>string</strong>: Dies ist das Python-Modul string (<a class="reference external" href="http://python.org/doc/current/lib/module-string.html">http://python.org/doc/current/lib/module-string.html</a>).</li>
<li><strong>random</strong>: Dies ist das Python-Modul random  (<a class="reference external" href="http://python.org/doc/current/lib/module-random.html">http://python.org/doc/current/lib/module-random.html</a>).</li>
<li><strong>whrandom</strong>: Dies ist das Python-Modul whrandom. Heute benutzen Sie meist besser das Modul random  (<a class="reference external" href="http://python.org/doc/current/lib/module-whrandom.html">http://python.org/doc/current/lib/module-whrandom.html</a>).</li>
<li><strong>math</strong>: Dies ist das Python-Modul math  (<a class="reference external" href="http://python.org/doc/current/lib/module-math.html">http://python.org/doc/current/lib/module-math.html</a>).</li>
<li><strong>DateTime</strong>: Dies ist Zopes eigenes <tt class="docutils literal">DateTime</tt>-Modul.</li>
<li><strong>sequence</strong>: Dies ist ein Zope-Modul zum einfachen Sortieren von Sequenzen.</li>
<li><strong>ZTUtils</strong>: Dies ist ein Zope-Modul mit verschiedenen Hilfsfunktionen.</li>
<li><strong>AccessControl</strong>: Hiermit haben Sie Zugriff auf Zopes <tt class="docutils literal">Access</tt>-Modul.</li>
<li><strong>Products.PythonScripts.standard</strong>: Das ermöglicht den Zugriff auf die normalen String-verarbeitenden Funktionen von DTML, z.B. <tt class="docutils literal">html_quote</tt>, <tt class="docutils literal">thousands_commas</tt> usw.</li>
</ul>
<p>Wenn Sie ein Modul importieren möchten, das in der obigen Liste nicht auftaucht, können Sie im Modul <tt class="docutils literal">PythonScript</tt> eine sehr gute Anleitung dafür finden. Diese finden Sie unter <tt class="docutils literal">Zope/lib/python/Products/PythonScripts/module_access_examples.py</tt>. Es steht Ihnen allerdings auch eine einfachere Methode zur Verfügung, nämlich die Verwendung einer externen Methode.</p>
</div>
</div>
<div class="section" id="externe-methodenobjekte-verwenden">
<h4>Externe Methodenobjekte verwenden</h4>
<p>Eine <em>externe Methode</em> ist ein Python-Modul im Dateisystem, das von Plone aufgerufen wird. Weil sie im Dateisystem gespeichert ist, wird sie nicht im eingeschränkten Python-Modus ausgeführt und gehorcht daher den normalen Plone-Sicherheitseinstellungen.</p>
<p>Sie können also ein Skript schreiben, das tut, was immer Sie wollen, und es dann aus einem Page Template heraus aufrufen. Häufige Aufgaben hierfür sind das Öffnen und Schließen von Dateien, das Zugreifen auf andere Prozesse oder ausführbare Programme und das Ausführen von Aufgaben in Plone oder Zope, die Sie mit anderen Mitteln einfach nicht erledigen können. Aus offensichtlichen Gründen müssen Sie, wenn Sie ein solches Skript schreiben, sicher sein, dass Sie nichts Gefährliches tun, z.B. die Passwortdatei auf Ihrem Server lesen oder ungewollt eine Datei löschen.</p>
<p>Um eine externe Methode hinzuzufügen, gehen Sie im Dateisystem zur Wurzel Ihrer Plone-Instanz und finden darin das Verzeichnis <tt class="docutils literal">Extensions</tt>. Darin fügen Sie eine neue Python-Skriptdatei hinzu. Abbildung 6.5 zeigt, dass ich z.B. <tt class="docutils literal">test.py</tt> in das Verzeichnis auf meinem Windows-Rechner eingefügt habe.</p>
<a class="reference external image-reference" href="img/06-05.png/image_view_fullscreen"><img alt="img/06-05.png" class="original" src="img/06-05.png" /></a>
<p>Abbildung 6.5. Eine neue externe Methode, <tt class="docutils literal">test.py</tt></p>
<p>Nun können Sie <tt class="docutils literal">test.py</tt> öffnen, nach Belieben bearbeiten und beliebigen Python-Code darin schreiben. Der einzige Haken ist der, dass Sie eine Eingangsfunktion haben müssen, die mindestens ein Argument erhält, nämlich <tt class="docutils literal">self</tt>. Dieses Argument ist das externe Methodenobjekt in Plone, das Sie gleich hinzufügen werden. Folgendes ist ein Beispiel für eine Eingangsfunktion, die die Datei <tt class="docutils literal">README.txt</tt> aus dem gleichen Verzeichnis <tt class="docutils literal">Extensions</tt> liest und an den Benutzer zurückgibt (Sie müssen den Pfad so ändern, dass er auf Ihre Datei zeigt):</p>
<pre class="literal-block">
def readFile(self):
   fh = open(r'c:\Program Files\Plone\Data\Extensions\README.txt', 'rb')
   data = fh.read()
   return data
</pre>
<p>Nachdem Sie das getan haben, müssen Sie eine externe Methode auf dieses Skript abbilden. Da das ein Zope-Objekt ist, gehen Sie wieder zum ZMI, klicken auf <em>portal_skins</em> und dann auf <em>custom</em>. Wählen Sie schließlich in der Dropdown-Liste <em>Add New Items</em> den Eintrag <em>External Method</em>. Wenn Sie eine externe Methode hinzufügen, müssen Sie den Namen des Moduls (ohne <tt class="docutils literal">.py</tt>) und die Eingangsfunktion angeben. In diesem Fall sieht das <em>Add</em>-Formular wie in Abbildung 6.6 aus.</p>
<a class="reference external image-reference" href="img/06-06.png/image_view_fullscreen"><img alt="img/06-06.png" class="original" src="img/06-06.png" /></a>
<p>Abbildung 6.6. Die neu hinzugefügte externe Methode</p>
<p>Nach einem Klick auf <em>Save Changes</em> können Sie den <em>Test</em>-Reiter anklicken, um zu sehen, was passiert, wenn die Methode ausgeführt wird. In diesem Fall sollten Sie ein oder zwei Zeilen Text erhalten. Da Ihr externes Methodenmodul in Plone ist, können Sie von einem Page Template genauso darauf zugreifen wie auf jedes andere Objekt. Ein Pfadausdruck auf <tt class="docutils literal">here/test_external</tt> ist in dem Fall ausreichend. Beispiel:</p>
<pre class="literal-block">
&lt;h1&gt;README.txt&lt;/h1&gt;
&lt;p tal:content=&quot;here/test_external&quot; /&gt;
</pre>
<p>Die wirkliche Mächtigkeit besteht darin, dass Sie Code in den uneingeschränkten Python-Modus und von dort an eine beliebige Funktion auslagern können, ohne sich um die Sicherheit sorgen zu müssen. Auch wenn das nach einer coolen Funktion aussieht, werden externe Methoden in Plone nicht übermäßig benutzt, weil komplexe Logik normalerweise in ein Produkt verlagert wird, während einfache Logik in einem Script (Python)-Objekt belassen wird. Sollten Sie feststellen, dass Sie sehr ausgiebigen Gebrauch von externen Methodenobjekten machen, möchten Sie vielleicht einmal einen Blick auf die in Kapitel 12 beschriebenen Werkzeuge werfen.</p>
</div>
</div>
<div class="section" id="nutzliche-hinweise">
<h3>Nützliche Hinweise</h3>
<p>Da Page Templates gültiger XML-Code sind und unabhängig von Zope oder Plone benutzt werden können, gibt es mehrere nützliche Skripten zum Aufräumen von Template-Code und zur Durchführung von Syntax-Prüfungen. Dies sind zusätzliche Werkzeuge und Prüfungen. Wenn Sie ein Page Template hochladen, führt Zope tatsächlich alle notwendigen Prüfungen durch. Bei einem Projekt wie Plone kann es hilfreich sein, automatische Prüfungen über Ihren Code laufen zu lassen oder ihn lokal zu prüfen, bevor Änderungen vorgenommen werden.</p>
<p>Um diese Prüfungen durchzuführen müssen Sie in der Lage sein, diese Werkzeuge lokal zu bearbeiten, und Sie müssen Python auf Ihrem Rechner installiert haben. Weitere Informationen zum External Editor, einer Methode für die lokale Bearbeitung von entferntem Code, finden Sie in Kapitel 10.</p>
<div class="section" id="einfuhrung-in-xml-namespaces">
<h4>Einführung in XML-Namespaces</h4>
<p>Page Templates verwenden XML-Namespaces, um Code zu generieren. Programmierer können sich mit den Regeln von XML-Namespaces das Leben leichter machen. Am Anfang eines Page Templates sehen Sie im ersten Tag eine Deklaration des Namespaces:</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;...
</pre>
<p>Damit wird der Standard-Namespace auf XHTML (Extensible HTML) gesetzt. Für alle darin enthaltenen Elemente gilt, dass sie diesen Namespace verwenden, wenn kein anderer angegeben ist. Somit wissen Sie z.B., dass das nächste Element XHTML ist, weil es kein Namespace-Präfix hat:</p>
<pre class="literal-block">
&lt;body&gt;
</pre>
<p>Bei TAL- und METAL-Elementen und -Attributen haben Sie normalerweise das Präfix <tt class="docutils literal">tal:</tt> und <tt class="docutils literal">metal:</tt> hinzugefügt, um den Namespace anzugeben. Der folgende Code sollte Ihnen nun vertraut vorkommen:</p>
<pre class="literal-block">
&lt;span tal:omit-tag=&quot;&quot; tal:content=&quot;python: 1+2&quot; /&gt;
</pre>
<p>Das gibt <tt class="docutils literal">3</tt> aus. Folgendes ist jedoch eine Alternative dazu:</p>
<pre class="literal-block">
&lt;tal:number content=&quot;python: 1+2&quot; /&gt;
</pre>
<p>Dadurch, dass Sie das Präfix <tt class="docutils literal">tal:</tt> im Element verwenden, haben Sie den Standard-Namespace für dieses ganze Element als <tt class="docutils literal">tal</tt> angegeben. Ohne weiteres Präfix wird der Namespace <tt class="docutils literal">tal</tt> benutzt. Im Beispiel mit den <tt class="docutils literal">span</tt>-Tags ist der Standard-Namespace XHTML, d.h. Sie müssen das <tt class="docutils literal">tal:</tt>-Präfix explizit angeben, wenn Sie den Content-Reiter benutzen.</p>
<p>Beachten Sie, dass der Elementname sprechend ist und alles sein darf, was noch nicht im <tt class="docutils literal">tal</tt>-Namespace definiert worden ist (z.B. <tt class="docutils literal">content</tt> oder <tt class="docutils literal">replace</tt>). Weil <tt class="docutils literal">tal:number</tt> kein gültiges XHTML-Element ist, wird dieser Tag nicht angezeigt, wohl aber sein Inhalt, was das <tt class="docutils literal"><span class="pre">omit-tag</span></tt> überflüssig macht. Diese Technik wird in Plone sehr oft eingesetzt, um kürzeren, prägnanteren Code zu erhalten, in dem man Fehler einfacher suchen kann.</p>
</div>
<div class="section" id="einfuhrung-in-code-sauberung">
<h4>Einführung in Code-Säuberung</h4>
<p>HTML Tidy ist ein hervorragendes Werkzeug zum Testen und Säubern von HTML-Code, das einige nützliche Aufgaben erledigen kann. Es existieren Versionen von HTML Tidy für alle Betriebssysteme. Sie können es unter <a class="reference external" href="http://tidy.sourceforge.net">http://tidy.sourceforge.net</a> herunterladen. Für Windows-Benutzer: Finden Sie das passende Paket für Ihre Windows-Version, packen Sie die Datei <tt class="docutils literal">tidy.zip</tt> aus, und fügen Sie <tt class="docutils literal">tidy.exe</tt> zu Ihrem <tt class="docutils literal">PATH</tt> hinzu (normalerweise Ihr Windows-Verzeichnis, z.B. <tt class="docutils literal"><span class="pre">C:\WINNT</span></tt>).</p>
<p>HTML Tidy sagt Ihnen, wenn es irgendwelche XHTML-Fehler in Ihrem Page Template gibt. Zu diesem Zweck kann ein Flag einen großen Unterschied ausmachen: <tt class="docutils literal"><span class="pre">-xml</span></tt>. Damit weiß HTML Tidy, dass es die Datei als XML behandeln und alle XML-Fehler berichten soll. Am Beispiel eines &quot;defekten&quot; Templates in Listing 6.4 können Sie einige Fehler sehen. Der Code ist nicht nur nicht eingerückt, sondern es fehlen auch schließende Elemente, und auch die Verschachtelung stimmt nicht.</p>
<p>Listing 6.4. Ein Beispiel für ein defektes Page Template: <tt class="docutils literal">bad_template.pt</tt></p>
<pre class="literal-block">
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
    &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;
&lt;div&gt;
Das ist schlechtes HTML,
XHTML oder XML...&lt;a tal:contents=&quot;string: someUrl&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;img&gt;
Außerdem ist er nicht eingerückt!
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Wenn Sie HTML Tidy über das Listing 6.4 laufen lassen, werden Sie die Fehler im Template sehen und schön eingerückten Code erhalten, wie in Listing 6.5 zu sehen ist.</p>
<p>Listing 6.5. Ausgabe von HTML Tidy</p>
<pre class="literal-block">
$ tidy -q -i bad_template.pt
line 11 column 1 - Warning: &lt;img&gt; element not empty or not closed
line 10 column 1 - Warning: missing &lt;/div&gt;
line 10 column 39 - Warning: &lt;a&gt; proprietary attribute &quot;tal:contents&quot;
line 11 column 1 - Warning: &lt;img&gt; lacks &quot;alt&quot; attribute
line 11 column 1 - Warning: &lt;img&gt; lacks &quot;src&quot; attribute
line 9 column 1 - Warning: trimming empty &lt;p&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;
    &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;

&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
  &lt;meta name=&quot;generator&quot; content=
  &quot;HTML Tidy for Linux/x86 (vers 1st August 2003), see http://www.w3.org&quot; /&gt;

  &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;div&gt;
    Das ist schlechtes HTML, XHTML oder XML...&lt;a tal:contents=
    &quot;string: someUrl&quot;&gt;&lt;/a&gt; &lt;img /&gt;Außerdem ist er nicht eingerückt!
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Die Beschwerden über proprietäre Attribute können etwas nervig sein. Um zu prüfen, ob Ihr Page Template gültiges XML ist, übergeben Sie das Flag <tt class="docutils literal"><span class="pre">-xml</span></tt>. Die Ausgabe ist weniger ausführlich und weist lediglich auf die fehlenden Tags hin:</p>
<pre class="literal-block">
$ tidy -q -xml bad_template.pt
line 15 column 1 - Error: unexpected &lt;/body&gt; in &lt;img&gt;
line 16 column 1 - Error: unexpected &lt;/html&gt; in &lt;img&gt;
</pre>
</div>
<div class="section" id="syntax-prufungen-durchfuhren">
<h4>Syntax-Prüfungen durchführen</h4>
<p>Wenn Sie ein Page Template im ZMI bearbeiten, führt Zope auf dem Dokument eine Syntax-Prüfung auf Dinge wie ungültige Tags durch. Falls ein Tag ungültig ist, wird ein Fehler bei dem Template angezeigt, während Sie es über das Web bearbeiten. Wenn Sie wie ich die meisten Ihrer Page Templates im Dateisystem schreiben (was ich in Kapitel 7 demonstrieren werde), so ist eine einfache Syntax-Prüfung des Page Templates wirklich sehr hilfreich. Listing 6.6 ist ein Python-Skript in Ihrem Dateisystem, das unabhängig von Zope läuft.</p>
<p>Um es auszuführen, müssen Sie einen Python-Interpreter haben, und das Python-Modul <tt class="docutils literal">PageTemplate</tt> muss importiert werden können. Damit <tt class="docutils literal">PageTemplate</tt> in Ihren Python-Interpreter importiert werden kann, müssen Sie das Verzeichnis <tt class="docutils literal">Products</tt> Ihrer Zope-Installation zu Ihrem Python-Pfad hinzufügen. Das können Sie auf mehrere Arten tun (siehe Anhang B).</p>
<p>Listing 6.6. Fehlersuche bei Page Templates</p>
<pre class="literal-block">
#!/usr/bin/python
from Products.PageTemplates.PageTemplate import PageTemplate
import sys

def test(file):
    raw_data = open(file, 'r').read()
    pt = PageTemplate()
    pt.write(raw_data)
    if pt._v_errors:
        print &quot;*** Error in:&quot;, file
        for error in pt._v_errors[1:]:
            print error

if __name__=='__main__':
    if len(sys.argv) &lt; 2:
        print &quot;python check.py file [files...]&quot;
        sys.exit(1)
    else:
        for arg in sys.argv[1:]:
            test(arg)
</pre>
<p>Für jede an das Skript übergebene Datei kompiliert das ZMI das Page Template und sucht nach irgendwelchen TAL-Fehlern. Für die Datei <tt class="docutils literal">bad_template.pt</tt> aus Listing 6.4 erhalten Sie einen Fehler:</p>
<pre class="literal-block">
$ python zpt.py /tmp/bad_template.pt
*** Error in: /tmp/bad_template.pt
TAL.TALDefs.TALError: bad TAL attribute: 'contents', at line 10, column 39
</pre>
<p>In diesem Fall beschwert es sich über die unrichtige Buchstabierung von <tt class="docutils literal">tal:content</tt> in Form von <tt class="docutils literal">tal:contents</tt>. Dies ist ein Fehler, den HTML Tidy nicht findet. Dummerweise wird die Verarbeitung beim ersten Syntax-Fehler abgebrochen. Wenn es mehrere Fehler gibt, wird nur der erste genannt, d.h., manchmal müssen Sie die Syntax mehrfach überprüfen.</p>
</div>
</div>
<div class="section" id="formulare-verwenden">
<h3>Formulare verwenden</h3>
<p>Formulare sind ein wichtiger Bestandteil einer Site, und fast jeder muss eine Methode zum Erstellen und Ändern von Formularen in seiner Plone-Site benutzen. Mit dem Formular-Framework von Plone können Sie die Validierung von Formularen ändern. Dieses Framework ist nicht ausschließlich für isolierte Formulare gedacht, die eine einfache Aufgabe erfüllen, z.B. ein Passwort abfragen, eine Anmeldung vornehmen etc. Das Framework funktioniert auch für alle Inhaltstypen bei Aufgaben wie der Bearbeitung eines Inhaltstyps, was ich später in den Kapiteln 11 bis 13 behandeln werde.</p>
<p>Alle einfachen Formulare verfügen über mindestens zwei Komponenten, die Sie bereits gesehen haben: ein Page Template-Objekt, um das Formular dem Benutzer anzuzeigen, und ein Script (Python)-Objekt, um die Ergebnisse zu parsen und irgendeine Aktion darauf durchzuführen.</p>
<p>Das Form Controller-Framework von Plone führt ein paar neue Objekttypen ein, die äquivalent zu den Typen sind, die Sie in diesem Kapitel gesehen haben. Dies sind das Controller Page Template-Objekt, das Controller Script (Python)-Objekt und das Controller Validator-Objekt. Zu diesen neuen Objekten gibt es äquivalente Zope-Objekte, wie Sie in Tabelle 6.1 sehen. Diese neuen Objekte haben mehr Eigenschaften und agieren auf leicht andere Weise als die dazu äquivalenten Objekte.</p>
<p>Tabelle 6.1. Neue Objekttypen, die der Controller bietet</p>
<table border="1" class="docutils">
<colgroup>
<col width="57%" />
<col width="43%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head"><strong>Objekttyp</strong></th>
<th class="head"><strong>Äquivalentes Zope-Objekt</strong></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Controller Filesystem Page Template</td>
<td>Page Template</td>
</tr>
<tr><td>Controller Python Script</td>
<td>Python Script</td>
</tr>
<tr><td>Controller Validator</td>
<td>Python Script</td>
</tr>
</tbody>
</table>
<p>Um eines dieser Objekte mit dem ZMI zu hinzuzufügen, gehen Sie ins Dropdown-Menü und wählen seinen Namen aus.</p>
<p>Das Form Controller-Framework erzeugt eine Reihe von Ereignissen für ein Formular, die ein Benutzer dann definieren kann. Folgendes ist die Reihe der Ereignisse, wenn ein Formular ausgeführt wird:</p>
<ol class="arabic simple">
<li>Dem Benutzer wird ein Controller Page Template-Objekt angezeigt.</li>
<li>Der Benutzer füllt das Formular aus und schickt es ab.</li>
<li>Eine eventuelle Validierung des abgeschickten Inhalts wird ausgeführt.</li>
<li>Eine den Daten entsprechende Aktion (oft Erfolg oder Fehlschlag) wird ausgeführt.</li>
</ol>
<p>Wenn diese Reihe von Ereignissen eintritt, wird ein Zustandsobjekt herumgereicht, das Informationen über den Objektstatus, den Erfolg irgendwelcher Validierungen und irgendwelche Meldungen enthält, die weitergegeben werden sollen.</p>
<p>Um zu zeigen, wie ein Formular validiert werden kann, gehen die folgenden Abschnitte diese Schritte durch. Anschließend werde ich ein vollständiges Beispiel im Abschnitt &quot;E-Mail-Beispiel: E-Mail an den Webmaster schicken&quot; zeigen.</p>
<div class="section" id="erstellen-eines-beispielformulars-und-zugehorige-skripten">
<h4>Erstellen eines Beispielformulars und zugehörige Skripten</h4>
<p>Am Anfang dieses Prozesses steht ein Formular. Obwohl es tatsächlich ein Controller Page Template-Objekt ist, ist es mit normalem TAL-Code geschrieben. Um ein Formular hinzuzufügen, wählen Sie im nun vertrauten Dropdown-Menü <em>Controller Page Template</em> und geben ihm die ID <strong>test_cpt</strong>.</p>
<p>Ein Formular in Plone besteht eigentlich aus ziemlich viel Code, falls Sie alle verfügbaren Optionen benutzen möchten. Dieser Codeteil ist in Anhang B vollständig enthalten und wird im späteren Beispiel benutzt:</p>
<pre class="literal-block">
&lt;form method=&quot;post&quot;
      tal:define=&quot;errors options/state/getErrors&quot;
      tal:attributes=&quot;action template/id;&quot;&gt;
    ...
    &lt;input type=&quot;hidden&quot; name=&quot;form.submitted&quot; value=&quot;1&quot; /&gt;
&lt;/form&gt;
</pre>
<p>Wenn Sie diesen Code betrachten, sollte Ihnen auffallen, dass einige kleine Unterschiede zu einer Art Standardformular existieren, damit der Code im Framework funktioniert. Zunächst ist das Formular so eingerichtet, dass es Daten an sich selbst schickt. Das ist <em>nicht</em> optional. Weiterhin existiert eine spezielle versteckte Variable namens <tt class="docutils literal">form.submitted</tt>.</p>
<p>Das Controller Page Template-Objekt prüft die Anfragevariable auf den Wert <tt class="docutils literal">form.submitted</tt>, um zu sehen, ob das Formular abgeschickt wurde. Wenn es stattdesen gerade erst aufgerufen worden ist, z.B. über einen Link, ist das <em>nicht</em> optional. Am Anfang des Formulars setzen Sie die Variable <tt class="docutils literal">error</tt>. Dieses Dictionary kommt aus dem <tt class="docutils literal">state</tt>-Objekt, das an die Templates übergeben wird. Das <tt class="docutils literal">state</tt>-Objekt ist in diesem System das gleiche Objekt für alle Templates und Skripten.</p>
<div class="section" id="validierer-erstellen">
<h5>Validierer erstellen</h5>
<p>Sobald der Benutzer den <em>Senden</em>-Button auf Ihrem Formular anklickt, gehen die Daten durch die Validierer und werden von ihnen geprüft. Validierer sind optional. Daten müssen nicht validiert werden, aber eine Anwendung sollte das natürlich bei Bedarf tun. Der <em>Validator</em>-Reiter für ein Controller Page Template-Objekt enthält einen Link zu den möglichen Validierern.</p>
<p>Ein Validierungsskript ist identisch mit einem normalen Script (Python)-Objekt, das eine Extravariable, <tt class="docutils literal">state</tt>, hat. Mit dieser Variablen können Sie Validierungsergebnisse übergeben. Listing 6.7 zeigt ein einfaches Validierungsskript, das überprüft, ob eine Zahl eingegeben wurde.</p>
<p>Listing 6.7. Validierung einer Zahleneingabe</p>
<pre class="literal-block">
##title=Ein Skript zum Überprüfen einer Zahleneingabe
##parameters=
num = context.REQUEST.get('num', None)
try:
    int(num)
except ValueError:
    state.setError(&quot;num&quot;, &quot;Keine Zahl&quot;, new_status=&quot;failure&quot;)
except TypeError:
    state.setError(&quot;num&quot;, &quot;Keine Zahl eingegeben.&quot;, new_status=&quot;failure&quot;)
if state.getErrors():
    state.set(portal_status_message=&quot;Bitte Fehler korrigieren.&quot;)
return state
</pre>
<p>Dieses <tt class="docutils literal">state</tt>-Objekt enthält einfache Angaben darüber, was in der Validierungskette passiert ist. Es speichert für jedes Feld die Fehler, den Status und alle anderen Werte. Wenn z.B. die eingegebene Zahl nicht in eine Ganzzahl umgewandelt werden kann, setzen Sie den Status auf Fehlschlag und geben eine Fehlermeldung für das Feld mit der Methode <tt class="docutils literal">setError</tt> aus. Später wird die Fehlermeldung für dieses Feld angezeigt. Am Ende des Skripts werden alle bisher zurückgegebenen Fehler mit der Methode <tt class="docutils literal">getErrors</tt> übergeben.</p>
<p>Um das obige Skript hinzuzufügen, klicken Sie auf <em>portal_skins</em>, dann auf <em>custom</em> und wählen im Dropdown-Menü <em>Controller Validator</em>. Geben Sie ihm die ID <strong>test_validator</strong>. Nun können Sie zum <em>Validation</em>-Reiter Ihres Controller Page Template-Objekts zurückgehen und einen Zeiger auf dieses Validierungsskript hinzufügen, wie in Abbildung 6.7 zu sehen ist.</p>
<a class="reference external image-reference" href="img/06-07.png/image_view_fullscreen"><img alt="img/06-07.png" class="original" src="img/06-07.png" /></a>
<p>Abbildung 6.7. Hinzufügen von <tt class="docutils literal">test_validator</tt> zum Controller Page Template-Objekt</p>
<p>Bei einer Validierung haben Sie einige Optionen zur Auswahl. Im Beispiel habe ich diese ignoriert, weil sie nicht relevant sind, aber hier ist die Liste dieser Optionen:</p>
<ul class="simple">
<li><strong>contextType</strong>: Dies ist der Typ des context-Objekts, falls vorhanden, in dem das Template ausgeführt wird. Dies ist eine Abkürzung zum Inhaltstyp des context-Objekts. Wenn Sie wollten, dass nur diese Validierung bei einem Link ausgeführt werden soll, könnten Sie diesen Wert auf <em>Link</em> setzen.</li>
<li><strong>button</strong>: Dies ist der Button, falls vorhanden, der angeklickt wird, um das Formular abzuschicken. Sie könnten verschiedene Buttons in einem Formular haben (z.B. einen <em>Senden</em>- und einen <em>Abbrechen</em>-Button). Jeder dieser Buttons könnte dann auf eine andere Aktion abgebildet werden. Ein Klick auf <em>Abbrechen</em> würde Sie an einen Ort bringen und ein Klick auf <em>Senden</em> an einen anderen.</li>
<li><strong>validators</strong>: Dies ist eine mit Kommata getrennte Liste von Validierern, d.h. Controller Validator-Objekten, die das Template verwenden wird. Im vorigen Beispiel haben Sie die Validierer-ID <tt class="docutils literal">test_validator</tt> benutzt.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Verwenden Sie Controller Validator-Objekte statt Script (Python)-Objekte, wenn Sie Validierungsskripten schreiben. Controller Validator-Objekte entsprechen gewöhnlichen Script (Python)-Objekten, haben aber einen zusätzlichen <em>Actions</em>-Reiter im ZMI.</p>
</div>
</div>
<div class="section" id="aktionen-angeben">
<h5>Aktionen angeben</h5>
<p>Aktionen werden ausgeführt, nachdem die Validierer beendet worden sind, und hängen vom Status ab, den die Validierer zurückgeben. Der <em>Actions</em>-Reiter für ein Controller Page Template-Objekt zeigt alle Aktionen des entsprechenden Page Templates an. Aktionen können Sie mit der gleichen Art von Spezialisierungsoptionen angeben, die zuvor schon mit einem Webformular beschrieben wurden (siehe Abbildung 6.8).</p>
<a class="reference external image-reference" href="img/06-08.png/image_view_fullscreen"><img alt="img/06-08.png" class="original" src="img/06-08.png" /></a>
<p>Abbildung 6.8. Eine Aktion hinzufügen</p>
<p>Für die eigentliche Ergebnisaktion haben Sie folgende vier Möglichkeiten:</p>
<ul class="simple">
<li><strong>redirect_to</strong>: Damit wird eine Umleitung zu der im Argument (einem TALES-Ausdruck) angegebenen URL erzielt. Die URL kann entweder absolut oder relativ sein.</li>
<li><strong>redirect_to_action</strong>: Hier erfolgt die Umleitung auf die im Argument (einem TALES-Ausdruck) angegebene Aktion zu dem aktuellen Inhaltsobjekt (z.B. <tt class="docutils literal">string:view</tt>). Zu diesem Zeitpunkt habe ich Aktionen noch nicht behandelt, aber jedes Inhaltsobjekt verfügt über Aktionen wie Anzeigen und Bearbeiten. Aktionen zu einem Objekt werden in Kapitel 11 behandelt.</li>
<li><strong>traverse_to</strong>: Führt eine Traversierung zu der im Argument (einem TALES-Ausdruck) angegebenen URL durch. Die URL kann entweder absolut oder relativ sein.</li>
<li><strong>traverse_to_action</strong>: Führt eine Traversierung zu der Aktion durch, die im Argument (einem TALES-Ausdruck) zu dem aktuellen Inhaltsobjekt angegeben ist (z.B. <tt class="docutils literal">string:view</tt>).</li>
</ul>
<p>Ein Beispiel hierfür ist, wenn Sie bei erfolgreichem Ausfüllen eines Formulars zu einem Controller Python Script-Objekt traversieren, das Sie geschrieben haben und das das Ergebnis des Formulars verarbeitet. Wenn die Seite fehlschlägt, traversieren Sie zum Template zurück und zeigen den Fehler an.</p>
<p>Der Unterschied zwischen einer Weiterleitung (Redirect) und einer Traversierung ist der, dass die Weiterleitung eine HTTP-Weiterleitung ist, die an den Browser des Benutzers gesendet wird. Der Browser verarbeitet sie und leitet den Benutzer zur nächsten Seite weiter. Daher verlieren die Weiterleitungsaktionen alle in der ursprünglichen Anfrage übergebenen Werte. Falls Sie den Inhalt des ursprünglichen Formulars untersuchen müssen, ist das nicht der beste Ansatz. Ich empfehle stattdessen die Traversierung zu den Optionen. Das Ergebnis ist das gleiche, aber die Traversierung erledigt das alles auf dem Server. Dabei werden die Anfragevariablen erhalten, die Sie dann in Skripten untersuchen können.</p>
</div>
</div>
<div class="section" id="e-mail-beispiel-e-mail-an-den-webmaster-schicken">
<h4>E-Mail-Beispiel: E-Mail an den Webmaster schicken</h4>
<p>Nun werden Sie ein echtes Beispiel sehen, das Sie im Rest dieses Kapitels erstellen werden. Eine häufige Anforderung ist ein spezielles Formular, das E-Mails an den Webmaster schickt. Ein solches Formular werden Sie in den folgenden Abschnitten erstellen. Die vollständigen Skripten, das Page Template und der zugehörige Code sind in Anhang B verfügbar. Wenn Sie all das nicht eintippen möchten, können Sie das Beispiel online auf der Buch-Website sehen. Sie können es auch als komprimierte Datei von der Plone-Buch-Website (<a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a>) und von der Apress-Website (<a class="reference external" href="http://www.apress.com">http://www.apress.com</a>) herunterladen. Das heißt, Sie können es einfach installieren und ausprobieren. Bei diesem Beispiel gibt es nur zwei Felder im Formular: die E-Mail-Adresse der Person, die das Formular abschickt, und ihren Kommentar. Bei diesem Formular ist die E-Mail-Adresse der Person notwendig, damit Sie auf ihre Kommentare antworten können.</p>
<div class="section" id="das-formular-erstellen">
<h5>Das Formular erstellen</h5>
<p>Das Formular ist der größte und komplizierteste Teil dieser Prozedur, vor allem, weil man viel Arbeit in die Fehlerbehandlung investieren muss. Dieses Formular ist ein Controller Page Template-Objekt namens <tt class="docutils literal">feedbackForm</tt>. Um sicher zu sein, dass es vom Haupt-Template umfasst wird, beginne ich das Formular mit der üblichen Methode:</p>
<pre class="literal-block">
&lt;html
    xmlns=&quot;http://www.w3.org/1999/xhtml&quot;
    xml:lang=&quot;en-US&quot;
    lang=&quot;en-US&quot;
    i18n:domain=&quot;plone&quot;
    metal:use-macro=&quot;here/main_template/macros/master&quot;&gt;
  &lt;body&gt;
    &lt;div metal:fill-slot=&quot;main&quot;
         tal:define=&quot;errors options/state/getErrors;&quot;&gt;
</pre>
<p>Ein Zusatz hierbei ist <tt class="docutils literal">errors options/state/getErrors</tt>, womit alle eventuellen Fehler für eine spätere Verwendung in die lokale Variable <tt class="docutils literal">errors</tt> abgelegt werden.</p>
<p>Wegen der Bedingung, dass das Formular sich selbst aufrufen soll, setzen Sie diese Aktion in TAL mit dem Ausdruck <tt class="docutils literal">template/id</tt>. Dieser Pfad extrahiert die ID des Templates und fügt sie in die Aktion ein. Das heißt, dieser Pfad funktioniert immer, sogar dann, wenn Sie das Template umbenennen. Beachten Sie, dass Sie auch die zuvor gesehenen <tt class="docutils literal">i18n</tt>-Tags hinzufügen, um zu gewährleisten, dass dieses Formular später lokalisiert werden kann:</p>
<pre class="literal-block">
&lt;form method=&quot;post&quot;
      tal:attributes=&quot;action template/id;&quot;&gt;

&lt;legend i18n:translate=&quot;legend_feedback_form&quot;&gt;
    Website Feedback
&lt;/legend&gt;
</pre>
<p>Der folgende Code ist der Anfang der Zeile für die E-Mail-Adresse. Hierbei definieren Sie eine Variable namens <tt class="docutils literal">error_email_address</tt>, die auf einen Fehler-String gesetzt ist, falls es einen passenden String im Dictionary <tt class="docutils literal">errors</tt> gibt. Dieser Fehlerwert wird vom Validierer erzeugt, falls ein Fehler vorkommen sollte:</p>
<pre class="literal-block">
&lt;div class=&quot;field&quot;
     tal:attributes=&quot;class python:test(error_email_address,
                                       'field error', 'field')&quot;&gt;
     tal:define=&quot;error_email_address errors/email_address|nothing;&quot;&gt;
</pre>
<p>Folgendes ist die Bezeichnung für das E-Mail-Adressenfeld. Darin fügen Sie ein <tt class="docutils literal">div</tt> für den Hilfetext ein. Das <tt class="docutils literal">span</tt>-Element wird zu dem nun vertrauten roten Punkt neben der Bezeichnung, damit der Benutzer weiß, dass das Feld ausgefüllt werden muss:</p>
<pre class="literal-block">
&lt;label i18n:translate=&quot;label_email_address&quot;&gt;Your email address&lt;/label&gt;
&lt;span class=&quot;fieldRequired&quot; title=&quot;Required&quot;&gt;(Required)&lt;/span&gt;
&lt;div class=&quot;formHelp&quot;
     i18n:translate=&quot;label_email_address_help&quot;&gt;
     Enter your email address.
&lt;/div&gt;
</pre>
<p>Als Nächstes fügen Sie das eigentliche Element hinzu:</p>
<pre class="literal-block">
    &lt;div tal:condition=&quot;error_email_address&quot;&gt;
        &lt;tal:block i18n:translate=&quot;&quot;
                   content=&quot;error_email_address&quot;&gt;Error
        &lt;/tal:block&gt;
    &lt;/div&gt;
    &lt;input type=&quot;text&quot; name=&quot;email_address&quot;
           tal:attributes=&quot;tabindex tabindex/next;
                           value request/email_address|nothing&quot; /&gt;
&lt;/div&gt;
</pre>
<p>Zu Beginn dieses Blocks testen Sie, ob es einen Fehler gibt. Wenn ja, hat die Klasse des Elements zu <tt class="docutils literal">field error</tt> gewechselt. Diese Klasse zeigt einen hübschen orangefarbenen Kasten um das Feld herum. Wenn für dieses Feld ein Fehler aufgetreten ist (was Sie bereits getestet haben), wird die entsprechende Meldung angezeigt. Schließlich zeigen Sie das Formularelement an, und wenn es schon einen Wert für <tt class="docutils literal">email_address</tt> in der Anfrage gibt, füllen Sie das Formularelement mit diesem Wert.</p>
<p>Das Werkzeug <tt class="docutils literal">tabindex</tt> ist recht nützlich in Plone. Es enthält eine fortlaufende Nummer, die für jedes Element inkrementiert wird und einen neuen Wert für den HTML-<tt class="docutils literal">tabindex</tt> setzt. Das ist eine nette Eigenschaft der Benutzerschnittstelle, denn dadurch kann jedes Formularelement gefahrlos herumgereicht werden, ohne dass man sich diese <tt class="docutils literal">tabindex</tt>-Zahlen merken muss, weil das schon automatisch geschieht.</p>
<p>Das ist eine Menge Arbeit für ein Element, aber es ist überwiegend Code nach dem immer gleichen Schema, den Sie einfach kopieren oder ändern können. Den Rest des Formulars finden Sie in Anhang B.</p>
</div>
<div class="section" id="einen-validierer-erstellen">
<h5>Einen Validierer erstellen</h5>
<p>In dem Beispiel gibt es nur ein notwendiges Element (die E-Mail-Adresse), d.h., es gibt ein einfaches Stück Python namens <tt class="docutils literal">validEmail.vpy</tt>, das die Arbeit macht. Der Inhalt dieses Skripts sieht wie folgt aus:</p>
<pre class="literal-block">
email = context.REQUEST.get('email_address', None)
if not email:
    state.setError('email_address', 'Email is required',
                    new_status='failure')
if state.getErrors():
    state.set(portal_status_message='Please correct the errors.')
return state
</pre>
<p>Wenn keine E-Mail-Adresse gefunden wird, fügt dieses Skript einen Fehler mit dem Schlüssel <tt class="docutils literal">email_address</tt> und einer Meldung zum Fehler-Dictionary hinzu. Dieser Schlüssel wird im Page Template benutzt, um zu prüfen, ob es in diesem speziellen Feld einen Fehler gab.</p>
</div>
<div class="section" id="das-skript-verarbeiten">
<h5>Das Skript verarbeiten</h5>
<p>Dieses Beispiel verwendet ein einfaches E-Mail-Skript, das die (bereits validierten) Werte bekommt und daraus eine E-Mail macht. Das ist ein Controller Python Script-Objekt. Es ähnelt dem normalen Script (Python)-Objekt, mit dem Unterschied, dass es eine zusätzliche Variable namens <tt class="docutils literal">state</tt> hat und Sie ihm, wie beim Controller Page Template, Aktionen angeben können, wenn es Erfolg hat:</p>
<pre class="literal-block">
mhost = context.MailHost
emailAddress = context.REQUEST.get('email_address')
administratorEmailAddress = context.email_from_address
comments = context.REQUEST.get('comments')

# the message format, %s will be filled in from data
message = &quot;&quot;&quot;
From: %s
To: %s
Subject: Website Feedback

%s
URL: %s &quot;&quot;&quot;

# format the message
message = message % (
    emailAddress,
    administratorEmailAddress,
    comments,
    context.absolute_url())

mhost.send(message)
</pre>
<p>Nun haben Sie ein einfaches Skript zum Verschicken von E-Mail gesehen. Das ist ein gängiges Skript, das Sie immer wieder sehen werden. Im Prinzip bekommt dabei das <tt class="docutils literal">MailHost</tt>-Objekt in Plone eine E-Mail-Adresse als String, sofern es die RFC-Spezifikation (Request for Comment) für E-Mails erfüllt, die <tt class="docutils literal">From</tt>- und <tt class="docutils literal">To</tt>-Adressen enthält.</p>
<p>In dieser E-Mail nehmen Sie die Administrator-Adresse, die Sie in den Portal-Einstellungen angegeben haben, und schicken die E-Mail an diese Person. Der einzige zusätzliche Teil in diesem Skript besteht darin, dass außerdem auch der Zustand gesetzt wird. Damit wird eine Meldung gesetzt, die dem Benutzer ein Feedback gibt:</p>
<pre class="literal-block">
screenMsg = &quot;Comments sent, thank you.&quot;
state.setKwargs( {'portal_status_message':screenMsg} )
return state
</pre>
</div>
<div class="section" id="verknupfen-aller-drei-teile">
<h5>Verknüpfen aller drei Teile</h5>
<p>Im Moment existieren jedoch drei separate Einheiten: ein Formular, ein Validierer und ein Aktionsskript. Diese müssen miteinander verbunden werden, um die ganze Kette zu bilden, damit Sie wieder zum Controller Template-Objekt zurückkommen. Klicken Sie auf den <em>Validator</em>-Reiter, und geben Sie einen neuen Validierer ein, der auf das Skript <tt class="docutils literal">validEmail</tt> zeigt. Sie werden auch eine Erfolgsaktion für den Fall hinzufügen, dass die Verarbeitung korrekt ist, um zu dem Skript <tt class="docutils literal">sendEmail</tt> zu traversieren. Bei dem <tt class="docutils literal">sendEmail</tt>-Skript können Sie nun eine weitere Traversierung zurück zu <tt class="docutils literal">feedbackForm</tt> hinzufügen, damit der Benutzer nach einem erfolgreichen <tt class="docutils literal">sendEmail</tt> wieder zur ursprünglichen Seite zurückgelangt.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">In Plone gibt es ein wesentlich vollständigeres E-Mail-Validierungsskript namens <tt class="docutils literal">validate_emailaddr</tt>, das prüft, ob die E-Mail das richtige Format hat. Wenn Sie lieber dieses Skript benutzen möchten, können Sie den Validierer auf dieses Skript zeigen lassen.</p>
</div>
<p>Das war's, Sie sind fertig! Nun sollten Sie in der Lage sein, das Formular auf der Buch-Website zu testen. Um es noch einfacher zu machen, habe ich einen Feedback-Reiter erstellt, der auf das Template <tt class="docutils literal">feedbackForm</tt> zeigt, und von dort können Sie mir nun Feedback zu diesem Buch geben!</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch7.rst">
    <title>7. Das Look-and-Feel von Plone anpassen</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch7.rst</link>
    <description>In den beiden vorangegangenen Kapiteln habe ich einige der Kernkomponenten von Plones Benutzerschnittstelle behandelt, inklusive Script(Python)-Objekten und Page Templates. Nun ist es Zeit, die Details bei der Gestaltung des Look-and-Feel einer Plone-Site zu behandeln. Dieses Kapitel enthält Objekte aus vorangegangenen Kapiteln und führt auch einige neue ein.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Das Look-and-Feel von Plone anpassen</h2>
<p>In den beiden vorangegangenen Kapiteln habe ich einige der Kernkomponenten von Plones Benutzerschnittstelle behandelt, inklusive Script(Python)-Objekten und Page Templates. Nun ist es Zeit, die Details bei der Gestaltung des Look-and-Feel einer Plone-Site zu behandeln. Dieses Kapitel enthält Objekte aus vorangegangenen Kapiteln und führt auch einige neue ein.</p>
<p>Zu Beginn werde ich die Schlüsseldefinitionen und jene Plone-Elemente behandeln, die eine Site ausmachen. Ich werde Begriffe definieren, die Sie vielleicht schon gehört haben, z.B. <em>Skins</em> und <em>Ebenen</em>. Dann werde ich die Anpassung der Benutzerschnittstelle von Plone behandeln - mit besonderem  Augenmerk auf den Möglichkeiten, über die ein Site-Entwickler mit Cascading Style Sheets (CSS)  verfügt. Ich werde die wichtigsten Variablen durchgehen und Ihnen zeigen, wie Sie sie ändern können. Dann betrachte ich nochmals die Anpassung von Schleifen  und Skin-Elementen, wobei alle Themen wieder auftreten werden, die ich in den drei vorangegangenen Kapiteln kurz behandelt habe.</p>
<p>Anschließend zeige ich Ihnen, wie man eine neue Skin erstellt, und erkläre die Techniken, mit denen man all das im Dateisystem entwickeln kann.</p>
<p>Und schließlich beende ich dieses Kapitel mit einer Beispiel-Site, nämlich der Maestro-Site, auf der die NASA Daten über die Mars-Rover bereitgestellt hat. Dies ist eine Plone-Website mit hohen Datentransferraten, und die Skin bietet eine hervorragende Fallstudie für die Anpassungen einer Site. Dieses sehr reale Beispiel dafür, wie man eine Plone-Site anpassen und ändern kann, wird Ihnen eine große Hilfe dabei sein, das Gleiche bei Ihrer eigenen Site durchzuführen.</p>
<div class="section" id="einfuhrung-in-plone-skins">
<h3>Einführung in Plone-Skins</h3>
<p>Wenn Plone ein Dokument anzeigt, wird der Inhalt dieses Dokuments mit der nunmehr vertrauten grün-blauen Plone-Schnittstelle angezeigt. Eine <em>Skin</em> (in der deutschen Plone-Lokalisierung auch <em>Aussehen</em> genannt) bestimmt ganz genau, wie dieses Dokument dem Benutzer angezeigt wird, inklusive Bilder und Styles um den Inhalt herum. Eine Skin gruppiert Elemente, hüllt dieses Stück Inhalt ein und stellt es auf eine bestimmte Weise dar.</p>
<p>Um die Darstellung zu generieren, die ein Benutzer sieht, hat eine Skin viele Elemente, inklusive eines statischen Teils, z.B. ein Bild, und dynamischer Teile wie Scripts. Das Feedback-Formular im vorigen Kapitel war ein Beispiel für das Hinzufügen einiger Elemente zu einer Skin, um neue Teile einer Skin zu erstellen. Dieses Beispiel enthielt eine Logik in Form eines Python(Script)-Objekts und neue Seiten in Form von Page Templates. Diese Teile haben Sie zu einer Skin hinzugefügt, damit ein Benutzer mit dem Formular interagieren konnte.</p>
<p>Von einer Skin können Sie so viel oder so wenig wiederverwenden, wie Sie möchten, um eine neue Skin mit Plone zusammenzustellen. Die Skin können Sie mit kleinen Korrekturen oder mit umfangreichen Änderungen erstellen, wie Sie z.B. auf Community-Sites wie <a class="reference external" href="http://www.zopezen.org">http://www.zopezen.org</a> und <a class="reference external" href="http://www.zopera.org">http://www.zopera.org</a> sehen können. Jede Plone-Site muss über mindestens eine Skin verfügen, die als Standard verwendet wird, aber sie kann so viele haben, wie der Site-Entwickler das möchte. Ein Benutzer kann gelegentlich zwischen verschiedenen Skins wechseln, falls der Site-Entwickler das erlauben möchte, auch wenn mir scheint, dass das selten der Fall ist.</p>
<p>Die Standard-Skin in Plone ist diejenige, die Sie auf einer Plone-Site wie <a class="reference external" href="http://www.plone.org">http://www.plone.org</a> sehen, mit der bekannten blau-grünen Schnittstelle. Aber Plone muss in keinster Weise so aussehen oder auch nur im Entferntesten als Plone-Site zu erkennen sein. Sein Aussehen liegt ganz in Ihrer Hand. Nehmen Sie z.B. die Site-Liste unter <a class="reference external" href="http://www.plone.org/about/sites">http://www.plone.org/about/sites</a>. Diese Sites haben alle ein eigenes Aussehen und eine eigene Benutzerführung. In den meisten Fällen können diese Sites leicht zwischen Skins umschalten, um den Benutzern ein anderes Aussehen anzubieten. Andere Sites machen internen Gebrauch von der mächtigen und flexiblen Schnittstelle von Plone, um Inhalte zu erstellen und zu bearbeiten, während externe Benutzer ein völlig anderes Aussehen der Site zu Gesicht bekommen.</p>
<p>Auf Mailinglisten habe ich viele Fragen dazu gelesen, ob Plone wie eine Plone-Site aussehen muss? Kann es für einen Benutzer so und für einen anderen anders aussehen? Kann es wie meine Firmen-Site aussehen? Die Antwort auf  diese Fragen lautet &quot;ja&quot;: Nur Ihre Vorstellungsgabe setzt Ihnen Grenzen (und die Menge an Zeit, die Sie in die Anpassung Ihrer Site investieren können).</p>
<div class="section" id="ebenen-in-einer-skin-benutzen">
<h4>Ebenen in einer Skin benutzen</h4>
<p>Ein Skin wird logisch in eine Ansammlung von Templates und Scripts namens <em>Ebenen</em> (<em>layers</em>) unterteilt. Das Ändern dieser individuellen Sammlungen erlaubt es dem Benutzer, Komponenten auf einfache Weise zu einer Skin hinzuzufügen oder daraus zu entfernen. Die Ebenen werden in einer Skin durch eine hierarchische Liste von Ordnern dargestellt. Jede Ebene entspricht einem Ordnernamen, und jeder Ordner enthält die Skin-Elemente.</p>
<p>Eine Skin kann z.B. folgende Ebenen haben:</p>
<pre class="literal-block">
custom, gruf, plone_ecmascript, plone_wysiwyg ...
</pre>
<p>Die Reihenfolge der Ebenen in dieser Liste ist der bestimmende Faktor dafür, wie Plone die Elemente findet. Wenn ein Element wie <tt class="docutils literal">logo.jpg</tt> von der Skin verlangt wird, geht die Skin die Ebenen durch, um das Element zu finden. Die Skin beginnt bei der ersten ihr zugeordneten Ebene (in diesem Beispiel <tt class="docutils literal">custom</tt>). Wenn die Skin das Element dort nicht finden kann, wird die Suche in der zweiten Ebene fortgesetzt (hier <tt class="docutils literal">gruf</tt>). Das setzt sich so lange fort, bis sie das gesuchte Element findet. Sonst wird ein Fehler 404 ausgelöst und an den Browser zurückgegeben.</p>
<p>Ein ähnliches Konzept dazu ist die Verwendung der Umgebungsvariablen PATH auf den meisten Betriebssystemen. Bei der Eingabe eines Befehls oder bei der Suche nach einem Programm durchsucht das Betriebssystem die Verzeichnisse, wie sie in der Umgebungsvariablen PATH angegeben sind. Etwas Ähnliches passiert bei Ebenen, wo diese auch nacheinander durchsucht werden, um das Element zu finden.</p>
<p>Dadurch, dass höhere Ebenen Vorrang vor niedrigeren haben, verfügen Entwickler und Administratoren nun über die Möglichkeit, ihre Site mit Hilfe von Ebenen anzupassen. Wenn Sie ein bestimmtes Element einer Plone-Skin nicht mögen, können Sie das Element anpassen, indem Sie es eine Ebene nach oben bewegen und das Ergebnis anpassen. Sie können Ihre Skins und Ebenen in Plone mit dem Werkzeug <tt class="docutils literal">portal_skins</tt> sortieren, das ich als Nächstes behandle.</p>
</div>
<div class="section" id="skins-mit-dem-werkzeug-portal-skins-verwalten">
<h4>Skins mit dem Werkzeug <tt class="docutils literal">portal_skins</tt> verwalten</h4>
<p>Das <tt class="docutils literal">portal_skins</tt>-Werkzeug in Plone können Sie dazu benutzen, das Verhalten von Skins und Ebenen zu definieren. Es verfügt auch über eine API (Application Programming Interface) zum Erstellen und Benutzen von Skins.</p>
<p>Um auf das <tt class="docutils literal">portal_skins</tt>-Werkzeug zuzugreifen, gehen Sie ins ZMI (Zope Management Interface) und klicken auf <em>portal_skins</em>. Im ZMI sehen Sie dann zwei wichtige Bildschirme. Der erste, der <em>Contents</em>-Reiter, zeigt alle Ordner und Dateisystem-Verzeichnisansichten (file system directory views, FSDVs) in diesem Werkzeug an (siehe Abbildung 7.1).</p>
<a class="reference external image-reference" href="img/07-01.png/image_view_fullscreen"><img alt="img/07-01.png" class="original" src="img/07-01.png" /></a>
<p>Abbildung 7.1. Inhalt des <tt class="docutils literal">portal_skins</tt>-Werkzeugs in einer Plone-Standardinstallation</p>
<p>Die Ordner und Dateisystem-Verzeichnisansichten unter dem Contents-Reiter sind nicht standardmäßig Ebenen, aber Sie können sie nun in Ebenen verwandeln. Der zweite wichtige Bildschirm, der Properties-Reiter, zeigt alle Skins und Ebenen an, die Sie in Ihrer Plone-Site definiert haben (siehe Abbildung 7.2).</p>
<a class="reference external image-reference" href="img/07-02.png/image_view_fullscreen"><img alt="img/07-02.png" class="original" src="img/07-02.png" /></a>
<p>Abbildung 7.2. Skins und Ebenen in einer Plone-Standardinstallation</p>
<p>Wie Abbildung 7.2 zeigt, ist die Liste dieser Ebenen ziemlich lang. Auch wenn sie etwas einschüchternd wirken mag, gibt diese große Anzahl von Ebenen dem Entwickler ein großes Maß an Flexibilität und Wiederverwendbarkeit. Jede Skin wird links angezeigt. Dazu gibt es rechts einen Textbereich, in dem alle zugehörigen Ebenen angezeigt werden. Wie ich schon sagte, durchsucht Plone bei der Suche nach Elementen die Ebenen von oben nach unten. Jede Ebene entspricht dem Namen eines Ordners oder FSDV unter dem <em>Contents</em>-Reiter. In Abbildung 7.2, können Sie ein Verzeichnis namens <tt class="docutils literal">plone_ecmascript</tt> sehen, und in Abbildung 7.1 sehen Sie das passende FSDV-Objekt.</p>
<p>Ein FSDV ist ein neues Objekt, das eine nützliche Fähigkeit in Plone bietet: §s bietet direkten Zugriff auf Skin-Elemente, die im Dateisystem, anstatt wie üblich in Zopes Objektdatenbank, definiert sind. Dadurch machen sie die Entwicklung und Anpassung leichter. Durch das direkte Lesen der Objekte aus dem Dateisystem ist es für Entwickler viel leichter, den Code zu schreiben und zu bearbeiten, aus dem die Site generiert wird. Wenn Sie Plone installieren, wird die Skin ins Dateisystem geschrieben. Wenn Sie ein Objekt anpassen, legen Sie eine lokale Kopie davon in Ihrer Plone-Datenbank an. Ein FSDV erlaubt eine klare Trennung zwischen Code, den Sie aus dem Web heruntergeladen haben, und Code, der in Ihrer lokalen Instanz angepasst wurde.</p>
<p>Plone 2 wird mit zwei Skins ausgeliefert:  <em>Plone Default</em> und  <em>Plone Tableless</em>. Plone Default verwendet Tabellen bei der Darstellung des Haupttextes, flankiert von zwei Tabellenzellen auf beiden Seiten, die die linken bzw. rechten Slots enthalten. Aus Gründen der Browser-Kompatibilität ist das die Standardeinstellung. Wenn Sie jedoch zu Plone Tableless umschalten, bekommen Sie eine Skin, die gleich aussieht, aber bei der Darstellung der Seiten völlig ohne Tabellen auskommt, was Ihnen als Site-Entwickler mehr Flexibilität gibt. Beim Schreiben dieses Buches war die Plone Tableless-Skin auf manchen Browsern wie dem Internet Explorer vielleicht etwas problematisch. In Zukunft, so hoffe ich, wird die Plone Tableless-Skin der Standard werden.</p>
<p>Um die Skin zu wechseln, scrollen Sie ans untere Formularende, wo Sie den Wert für Default Skin sehen, und wählen die Skin <em>Plone Default</em> aus der Liste aus. Falls Sie die Option <em>Skin Flexibility</em> wählen, können Benutzer ihre eigenen Skins im Abschnitt <em>Meine Einstellungen</em> wählen.</p>
<p>Zurück im <em>Contents</em>-Reiter des <tt class="docutils literal">portal_skins</tt>-Werkzeugs können Sie sehen, dass einige der Ordner, z.B. <tt class="docutils literal">custom</tt>, Standardordner aus Zope sind. Diese haben das normale Ordner-Icon. Andere, z.B. <tt class="docutils literal">plone_images</tt>, sind FSDVs, die in Bereiche des Dateisystems zeigen. Sie haben ein Ordner-Icon mit einem grünen Schloss darin. Dieses Schloss zeigt an, dass Sie keine Elemente über das Web, sondern nur über das Dateisystem in einem FSDV hinzufügen oder bearbeiten können.</p>
<p>Um zu sehen, wo die Dateien zu einem FSDV sich auf Ihrer Festplatte aufhalten, klicken Sie auf den <em>Properties</em>-Reiter des FSDV. Klicken Sie z.B. vom <em>Contents</em>-Reiter des <tt class="docutils literal">portal_skins</tt>-Werkzeugs auf <em>Properties</em>, und Sie sehen den im System verwendeten Dateipfad <tt class="docutils literal">CMFPlone/skins/plone_images</tt>. Dieser Pfad ist der Ort dieses Verzeichnisses im Dateisystem, relativ zum Instanzursprung, den Sie bei der Installation angegeben haben. Da Sie Dateien über das Web im FSDV oder im Dateisystem sehen können, können Sie auf beide Arten lesend darauf zugreifen. Im Allgemeinen ist es angenehmer und leichter, Dateien im Dateisystem anzuzeigen, weswegen ich als Datei einen Pfad im Dateisystem bezeichnen werde, auf den man mit den gewohnten Werkzeugen zugreifen kann.</p>
</div>
</div>
<div class="section" id="skins-anpassen">
<h3>Skins anpassen</h3>
<p>Sie haben gesehen, wie Skins und Ebenen miteinander interagieren. Nun werden Sie sehen, wie Sie eine Plone-Site anpassen können. Ich beginne mit dem Beispiel aus Kapitel 4, in dem Sie gelernt haben, wie man das Logo anpassen kann. Mit Ihrem neuen Wissen über die Funktionsweise von Skins können Sie hier anknüpfen, um auch die Skin anzupassen. Dann mache ich damit weiter, dass ich Ihnen die Mächtigkeit vom Plone-CSS zeige und demonstriere, wie Sie es anpassen können. Zum Abschluss behandle ich das Haupt-Template, das Sie in früheren Kapiteln gesehen haben, und werde dessen Elemente detailliert beschreiben.</p>
<div class="section" id="logo-anpassen-zweiter-teil">
<h4>Logo anpassen, zweiter Teil</h4>
<p>In Kapitel 4 haben Sie gelernt, wie Sie das Logo in der oberen linken Ecke einer Plone-Site anpassen können, aber ich habe nicht erklärt, was dabei wirklich passiert. Dieser Abschnitt holt das nach.</p>
<p>Das Bild <tt class="docutils literal">logo.jpg</tt> ist jenes Bild, das in der oberen linken Ecke jeder Seite erscheint. Sie werden nun sehen, was passiert, wenn ein Browser versucht, diese Seite darzustellen. Nachdem Plone die Anfrage zu diesem Bild erhält, durchsucht es die Ebenen nach <tt class="docutils literal">logo.jpg</tt>. In einer Plone Default-Site ist dies das Element in <tt class="docutils literal">plone_images</tt> namens <tt class="docutils literal">logo.jpg</tt>. Weil dies, wie ich zuvor erklärt habe, ein FSDV ist, können Sie das Bild nicht über das Web ändern. Um Ihre Site vor zukünftigen Änderungen zu schützen, wollen Sie es aber auch nicht im Dateisystem ändern. Betrachten Sie also einmal genauer, was der <em>Customize</em>-Button tut. Wenn Sie diesen Button noch einmal anschauen, sehen Sie links daneben ein Dropdown-Menü mit Ordnern unter dem <em>Contents</em>-Reiter des <tt class="docutils literal">portal_skins</tt>-Werkzeugs.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Die aufgelisteten Ordner existieren in der Zope-Datenbank. FSDVs werden im Dropdown-Menü nicht aufgeführt. Standardmäßig zeigt es nur Ordner an.</p>
</div>
<p>Mit einem Klick auf den <em>Customize</em>-Button wird eine lokale Kopie des Elements in dem Ordner angelegt, der im Dropdown-Menü gewählt wurde. Per Voreinstellung ist dies der Ordner <tt class="docutils literal">custom</tt>. Das heißt, nun haben Sie eine Kopie im <tt class="docutils literal">custom</tt>-Ordner. Wenn Plone das Element <tt class="docutils literal">logo.jpg</tt> sucht, greift es auf die Version im <tt class="docutils literal">custom</tt>-Ordner zu. Wenn Sie wieder die Ebenen der Plone Default-Skin betrachten, sehen Sie, dass der <tt class="docutils literal">custom</tt>-Ordner die oberste Ebene der Skin ist. Da beim Aufruf von <tt class="docutils literal">logo.jpg</tt> das Bild in der <tt class="docutils literal">custom</tt>-Ebene gefunden wird, wird dieses neue <tt class="docutils literal">logo.jpg</tt> dargestellt.</p>
<p>Die Ablage angepasster Elemente in den <tt class="docutils literal">custom</tt>-Ordner ist der schnellste Weg, Ihre Plone-Site abzuwandeln. Dieser Ordner ist ein Standardordner in Plone, d.h., Sie können darin so viele Elemente ablegen und frühere Versionen überschreiben, wie Sie möchten.</p>
</div>
<div class="section" id="einfuhrung-in-die-cascading-style-sheets-von-plone">
<h4>Einführung in die Cascading Style Sheets von Plone</h4>
<p>Die visuelle Darstellung einer Plone-Site in einem Browser wird fast vollständig mit Hilfe von CSS zusammengestellt. Der vielleicht einfachste Weg, um zu sehen, was das CSS für eine Plone-Site leistet, ist der, die Abbildungen 7.3 und 7.4 zu vergleichen. Die erste zeigt Plone mit Stylesheets, und die zweite zeigt es ohne Stylesheets.</p>
<a class="reference external image-reference" href="img/07-03.png/image_view_fullscreen"><img alt="img/07-03.png" class="original" src="img/07-03.png" /></a>
<p>Abbildung 7.3. Plone mit Stylesheets</p>
<a class="reference external image-reference" href="img/07-04.png/image_view_fullscreen"><img alt="img/07-04.png" class="original" src="img/07-04.png" /></a>
<p>Abbildung 7.4. Plone ohne Stylesheets</p>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Falls Sie diesen Effekt reproduzieren möchten, schalten Sie Stylesheets in Ihrem Browser ab. Der Internet Explorer lässt Sie das nicht ohne weiteres machen, aber in Firefox (<a class="reference external" href="http://www.mozilla.org/products/firefox/">http://www.mozilla.org/products/firefox/</a>), dem Mozilla-basierten Open-Source-Browser, können Sie das ganz leicht tun. Wählen Sie in Firefox <em>Tools - Web Developer - Disable - Disable Styles</em>. Wegen seiner großen Anzahl von CSS- und anderen Entwicklerwerkzeugen ist Firefox für viele Plone-Entwickler der Browser der Wahl.</p>
</div>
<p>Der Unterschied ist, gelinde gesagt, bestechend. CSS übernimmt nicht nur die visuelle Darstellung von Seiten, sondern auch deren Layout. Durch eine Änderung im CSS können Sie diese visuelle Darstellung und das Layout einer Plone-Site ändern (innerhalb des Rahmens von CSS).</p>
<p>Dass die Darstellung von Plone von CSS durchgeführt wird, ist eine beeindruckende Leistung von vielen talentierten Entwicklern von Benutzerschnittstellen. zu den Vorteilen eines CSS-Layouts zählen unter anderem:</p>
<ul class="simple">
<li>CSS bietet eine Trennung zwischen der Darstellung und den Templates, die die Darstellung generieren.</li>
<li>Sie können eine große Anzahl von Änderungen vornehmen, ohne die darunter liegenden Templates anfassen zu müssen. Man braucht nur einen erfahrenen CSS-Entwickler.</li>
<li>Mit CSS wird die Site schneller, weil kleinere Dateien übertragen werden. Jede HTML-Datei ist kleiner, da das Layout der Site nicht als HTML-Auszeichnungen, sondern als CSS vorliegt, das dann im Cache gespeichert werden kann.</li>
<li>CSS ermöglicht die Anpassung des Look-and-Feel, ohne die zugrunde liegende Arbeit an der Zugänglichkeit der Site zu ruinieren.</li>
</ul>
<div class="sidebar">
<p class="first sidebar-title">Code-Ebenen</p>
<!-- When a Plone page is rendered, at least three layers of code create a page. For the example of the tabs that appear across the top of a Plone site, this is how they're assembled: -->
<!-- 1. First , the action providers (which I discussed in Chapter 4) create a list of the tabs to display. Security and other checks happen there. -->
<!-- 2. The Zope Page Templates system assembles the tabs and loops through the values returned by the action provider, creating HTML that's sent to the browser. -->
<!-- 3. Finally, CSS creates the visual representation of the HTML in the browser on the user's client. -->
<p>Wenn eine Plone-Seite dargestellt wird, sind mindestens drei Ebenen von Code an der Erzeugung der Seite beteiligt. Zum Beispiel werden die Reiter, die oben auf einer Plone-Site erscheinen, wie folgt zusammengesetzt:</p>
<ol class="arabic simple">
<li>Zuerst erzeugen die Aktionsdefinitionen (engl. action providers), die ich in Kapitel 4 besprochen habe) eine Liste der anzuzeigenden Reiter. Sicherheits- und andere Überprüfungen werden dort vorgenommen.</li>
<li>Das Zope Page Templates-System setzt die Reiter zusammen und iteriert über die Werte, die von den Aktionsdefinitionen zurückgegeben werden, wobei HTML entsteht, das an den Browser geliefert wird.</li>
<li>Und schließlich erzeugt CSS die visuelle Darstellung des HTML im Browser auf dem Rechner des Benutzers.</li>
</ol>
<!-- So rather than asking yourself, how can I customize the tabs? you need to consider exactly what customization you want to perform. This could mean changing the CSS, the HTML, the data, or the underlying tabs. The general rules are as follows: -->
<!-- - Plone presents data in some manner, either from a data set or from content. -->
<!-- - CSS produces the visual representation. -->
<!-- - The HTML and templates provide teh glue between the two. -->
<p>Anstatt sich also zu fragen: &quot;Wie kann ich die Reiter anpassen?&quot;, müssen Sie sich genau überlegen, welche Anpassung Sie vornehmen möchten. Das könnte eine Änderung im CSS, im HTML, in den Daten oder den darunter liegenden Reitern bedeuten. Die allgemeinen Regeln sind folgende:</p>
<ul class="simple">
<li>Plone stellt Daten auf irgendeine Weise dar, entweder aus einem Datensatz oder aus Inhalten.</li>
<li>CSS produziert die visuelle Darstellung.</li>
<li>Das HTML und die Templates sind der Klebstoff zwischen den beiden.</li>
</ul>
<!-- In fact, Plone is so customizable, on so many levels, that it's easy to worry about which bit to customize. To make sure that future Plone template changes don't compromise your application's design, try not to customize the templates. Instead, I recommend you try the CSS or the actions first. This way, when the templates change in future versions of Plone, there will be less chance of a problem. -->
<p class="last">Tatsächlich ist Plone auf so vielen Ebenen derart anpassbar, dass man leicht Kopfschmerzen bei dem Gedanken bekommt, welcher Teil angepasst werden muss. Um sicherzustellen, dass zukünftige Änderungen in Plone-Templates Ihr Anwendungsdesign nicht beeinflussen, sollten Sie nicht versuchen, die Templates anzupassen. Stattdessen empfehle ich, dass Sie es zuerst mit CSS oder den Aktionen versuchen. Auf diese Art reduziert sich die Wahrscheinlichkeit für Probleme, wenn sich die Templates in zukünftigen Versionen von Plone ändern.</p>
</div>
</div>
<div class="section" id="schriftart-farben-und-abstande-anpassen">
<h4>Schriftart, Farben und Abstände anpassen</h4>
<p>Das eigentliche Stylesheet, das die meiste Arbeit erledigt, <tt class="docutils literal">plone.css</tt>, enthält eine Reihe von Variablen, die mit DTML (Document Template Markup Language) gesetzt werden. In diesem Buch behandle ich kein DTML, und wahrscheinlich ist das die einzige Stelle in Plone, wo es verwendet wird. Falls Sie DTML nicht schon kennen, rate ich Ihnen davon ab, es zu lernen, wenn es nicht sein muss! Das Zope Page Templates-System enthält alles, was Sie zum Erzeugen von Markup-Code (z.B. XML und [X]HTML) brauchen. DTML empfiehlt sich heutzutage lediglich, wenn Sie Text erzeugen wollen, der kein Markup-Code ist, wie zum Beispiel CSS. Es gibt sehr gute Online-Referenzen zu DTML für Zope, z.B. unter:  <a class="reference external" href="http://zope.org/Documentation/Books/ZopeBook/2_6Edition/DTML.stx">http://zope.org/Documentation/Books/ZopeBook/2_6Edition/DTML.stx</a>.</p>
<p>Die DTML-Syntax für dieses Stylesheet ist wirklich sehr einfach: Jede Variable bezieht sich auf ein entsprechendes Attribut in einer Eigenschaftsliste. Auf diese Eigenschaftsliste greifen Sie zu, indem Sie nacheinander auf <em>portal_skins</em>, <em>plone_styles</em> und <em>base_properties</em> klicken. In Abbildung 7.5 können Sie sehen, wie diese Datei im ZMI aussieht.</p>
<a class="reference external image-reference" href="img/07-05.png/image_view_fullscreen"><img alt="img/07-05.png" class="original" src="img/07-05.png" /></a>
<p>Abbildung 7.5. Die Grundeigenschaften des Stylesheets</p>
<p>So findet z.B. <tt class="docutils literal"><span class="pre">&amp;dtml-fontColor;</span></tt> die Variable <tt class="docutils literal">fontColor</tt> und setzt sie ins Stylesheet, d.h., <tt class="docutils literal">fontColor</tt> wird schwarz sein. Nun sehen Sie, wo diese Variable  in der Datei <tt class="docutils literal">plone.css</tt> referenziert wird. Um auf die CSS-Datei zuzugreifen, klicken Sie auf <tt class="docutils literal">portal_skins</tt>, <tt class="docutils literal">portal_skins</tt> und dann auf <tt class="docutils literal">plone.css</tt>. In dieser Datei sehen Sie, dass  <tt class="docutils literal">mainFontColor</tt> an mehreren Stellen referenziert wird, z.B. wie folgt im Haupttext einer Seite:</p>
<pre class="literal-block">
body {
    font: &amp;dtml-fontBaseSize; &lt;dtml-var fontFamily&gt;;
    background-color: &amp;dtml-backgroundColor;;

    color: &amp;dtml-fontColor;;

    margin: 0;
    padding: 0;
}
</pre>
<p>Sie können einfach im Stylesheet weiterlesen, wenn Sie wirklich wollen, aber die Variable zu ändern ist immer die schnellste Art herauszufinden, was davon betroffen ist.</p>
<p>Klicken Sie im ZMI auf <em>portal_skins</em>, <em>plone_styles</em>, <em>base_properties</em> und dann auf den <em>Customize</em>-Button. Wie Sie gesehen haben, wird dadurch ein Objekt im ZMI erzeugt, das Sie anpassen können. Dieses Mal ist das angepasste Objekt ein Ordner, der über die Eigenschaften verfügt, die in diesem Ordner vorhanden sind. Um auf die gerade angepassten Eigenschaften zuzugreifen, klicken Sie auf <em>portal_skins</em>, <em>custom</em> und dann auf <em>base_properties</em>. Als Nächstes wählen Sie den Properties-Reiter (siehe Abbildung 7.6).</p>
<a class="reference external image-reference" href="img/07-06.png/image_view_fullscreen"><img alt="img/07-06.png" class="original" src="img/07-06.png" /></a>
<p>Abbildung 7.6. Eigenschaften des Ordners</p>
<p>In dieser Eigenschaftsliste können Sie die Eigenschaft <tt class="docutils literal">mainColor</tt> ändern, z.B. auf <tt class="docutils literal">red</tt> oder <tt class="docutils literal">#cc9900</tt>. Ändern Sie den Wert dieser Eigenschaft, und klicken Sie dann auf <em>Save Changes</em>. Zurück in der Plone-Site sollten Sie nun die neue Farbe sehen.</p>
<p>In Kapitel 4 haben Sie ein Beispiel gesehen, in dem Benutzer Aktionen verändert haben, um einen Reiter oben auf der Seite zu ändern. Obwohl eine Aktion zwar mit einem Großbuchstaben anfangen kann, z.B. <em>Mitglieder</em>, wird sie dann auf der Webseite in Kleinbuchstaben angezeigt. Das liegt daran, dass CSS den Text wegen der Eigenschaft <tt class="docutils literal">textTransform</tt> in der Eigenschaftsliste in Kleinbuchstaben umwandelt. Um diese Umwandlung auszusetzen, ändern Sie die Eigenschaft <tt class="docutils literal">textTransform</tt> auf <tt class="docutils literal">none</tt>.</p>
<p>Im Stylesheet werden Eigenschaften für alle Farben, Abstände und Schriftarten definiert, die in einer Plone-Site benutzt werden. Tabelle 7.1 beschreibt all diese Parameter.</p>
<p>Tabelle 7.1. CSS-Eigenschaften</p>
<table border="1" class="docutils">
<colgroup>
<col width="25%" />
<col width="75%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Variablenname</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>logoName</td>
<td>Der Dateiname des Portallogos</td>
</tr>
<tr><td>fontFamily</td>
<td>Die Schriftfamilie für alle Texte, die keine Überschrift sind</td>
</tr>
<tr><td>fontBaseSize</td>
<td>Die Schriftgrundgröße, aus der alles berechnet wird</td>
</tr>
<tr><td>fontColor</td>
<td>Die Schrifthauptfarbe</td>
</tr>
<tr><td>backgroundColor</td>
<td>Die Hintergrundfarbe</td>
</tr>
<tr><td>linkColor</td>
<td>Die Farbe normaler Links</td>
</tr>
<tr><td>linkActiveColor</td>
<td>Die Farbe aktiver Links</td>
</tr>
<tr><td>linkVisitedColor</td>
<td>Die Farbe besuchter Links</td>
</tr>
<tr><td>borderWidth</td>
<td>Die Breite der meisten Rahmen in Plone</td>
</tr>
<tr><td>borderStyle</td>
<td>Der Stil der Rahmenlinien (normalerweise <tt class="docutils literal">solid</tt>)</td>
</tr>
<tr><td>borderStyleAnnotations</td>
<td>Der Stil der Rahmenlinien bei Kommentaren etc.</td>
</tr>
<tr><td>globalBorderColor</td>
<td>Die Rahmenfarbe der Hauptreiter, Portlets etc.</td>
</tr>
<tr><td>globalBackgroundColor</td>
<td>Die Hintergrundfarbe ausgewählter Reiter, Portlet-Überschriften etc.</td>
</tr>
<tr><td>globalFontColor</td>
<td>Die Schriftfarbe in Reitern und Portlet-Überschriften</td>
</tr>
<tr><td>headingFontFamily</td>
<td>Die Schriftfamilie der Überschriften <tt class="docutils literal">h1</tt>, <tt class="docutils literal">h2</tt>, ..., <tt class="docutils literal">h6</tt></td>
</tr>
<tr><td>headingFontBaseSize</td>
<td>Die Grundgröße bei der Berechnung der verschiedenen Überschriftengrößen</td>
</tr>
<tr><td>contentViewBorderColor</td>
<td>Die Rahmenfarbe der Reiter im <em>Inhalt</em>-Reiter</td>
</tr>
<tr><td>contentViewBackgroundColor</td>
<td>Die Hintergrundfarbe im <em>Anzeigen</em>-Reiter des <em>Inhalt</em>-Reiters</td>
</tr>
<tr><td>contentViewFontColor</td>
<td>Die Schriftfarbe in den Reitern des <em>Inhalt</em>-Reiters</td>
</tr>
<tr><td>textTransform</td>
<td>Gibt an, ob Text in Portlets, Reitern etc. in Kleinbuchstaben sein soll</td>
</tr>
<tr><td>evenRowBackgroundColor</td>
<td>Die Hintergrundfarbe gerader Zeilen in Listings</td>
</tr>
<tr><td>oddRowBackgroundColor</td>
<td>Die Hintergrundfarbe ungerader Zeilen in Listings</td>
</tr>
<tr><td>notifyBorderColor</td>
<td>Die Rahmenfarbe von Meldungen wie Statusmeldungen und vom Kalender</td>
</tr>
<tr><td>notifyBackgroundColor</td>
<td>Die Hintergrundfarbe von Meldungen</td>
</tr>
<tr><td>helpBackgroundColor</td>
<td>Die Hintergrundfarbe des Kalender-Pop-up-Widgets</td>
</tr>
<tr><td>discreetColor</td>
<td>Die Farbe von Credits, Dokumentverfasserangaben, Formularhilfen</td>
</tr>
<tr><td>portalMinWidth</td>
<td>Die minimale Breite des Portals</td>
</tr>
<tr><td>columnOneWidth</td>
<td>Die Breite der linken Spalte</td>
</tr>
<tr><td>columnTwoWidth</td>
<td>Die Breite der rechten Spalte</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="css-anpassen">
<h4>CSS anpassen</h4>
<p>Wenn Sie kleine Anpassungen vornehmen, können Sie das in <tt class="docutils literal">ploneCustom.css</tt> tun. Dies ist ein zweites Stylesheet, das nach <tt class="docutils literal">plone.css</tt> geladen wird. Dank der Kaskadierungsfunktion von Stylesheets können Sie in <tt class="docutils literal">ploneCustom.css</tt> beliebige Änderungen am gesamten Stylesheet vornehmen.</p>
<p>Um z.B. die Verfasserangaben zu ändern, die unten auf jeder Seite erscheinen, ändern Sie einfach <tt class="docutils literal">ploneCustom.css</tt>. Greifen Sie noch einmal mit dem ZMI auf die Datei zu, und klicken Sie dann auf <em>Customize</em>. Dadurch wird eine Kopie dieses Stylesheets im Ordner <tt class="docutils literal">custom</tt> angelegt. Ändern Sie die Verfasserangaben, indem Sie sie auf der Seite nach links verschieben und sie fett machen, wie in Abbildung 7.7 zu sehen ist.</p>
<a class="reference external image-reference" href="img/07-07.png/image_view_fullscreen"><img alt="img/07-07.png" class="original" src="img/07-07.png" /></a>
<p>Abbildung 7.7. Neue fette Verfasserangaben links</p>
<p>Das tun Sie, indem Sie Folgendes hinzufügen:</p>
<pre class="literal-block">
div.documentByLine {
    text-align: left;
    font-weight: bold;
}
</pre>
<p>Hierbei haben Sie zwei Attribute für das <tt class="docutils literal">byline</tt>-Element gesetzt: <tt class="docutils literal"><span class="pre">text-align</span></tt> und <tt class="docutils literal"><span class="pre">font-weight</span></tt>. Beachten Sie, dass Sie keine anderen Attribute im <tt class="docutils literal">byline</tt>-Element ändern. Die anderen Attribute werden vom Original-Stylesheet geerbt. Mit ein paar einfachen Zeilen CSS haben Sie die Site geändert und haben sichergestellt, dass andere Änderungen an Plone keinen Einfluss auf Ihre Site haben. Für kleine Änderungen ist <tt class="docutils literal">ploneCustom.css</tt> der am besten geeignete Ort.</p>
<p>Durch die Verwendung verschiedener Stylesheets können Sie Plone so benutzen, dass es für verschiedene Clients unterschiedlich aussieht. Websites verfügen oftmals über einen Button für eine Druckansicht, bei der die Seite in vereinfachter Form mit weniger Formatierungen angezeigt wird. Plone verringert dieses Problem dadurch, dass es ein separates Stylesheet anbietet. Wenn ein Browser die Seite druckt, wird sie von diesem Stylesheet formatiert. Alle weiteren Stylesheets sind oben auf der Seite enthalten. Sie finden sie, wenn Sie nacheinander auf <em>portal_skins</em>, <em>plone_templates</em> und <em>header.pt</em> klicken.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Ein etwas ungewöhnlicheres Stylesheet hat den Namen  <em>Präsentation</em>. Es wird nur von  Opera unterstützt. Wenn dort der Browser in vollem Bildschirmmodus benutzt wird, werden aus Überschriften separate Seiten, die in einer präsentationsähnlichen Schnittstelle angezeigt werden.</p>
</div>
</div>
<div class="section" id="das-haupt-template-anpassen">
<h4>Das Haupt-Template anpassen</h4>
<p>Wie Sie im vorigen Kapitel gesehen haben, müssen Sie das <tt class="docutils literal">master</tt>-Makro aus <tt class="docutils literal">main_template</tt> benutzen, um das Look-and-Feel von Plone auf einer Seite zu erhalten. Jede Plone-Seite benutzt dieses Makro und füllt darin die passenden Slots. Wenn Sie sich dieses Haupt-Template im Detail anschauen, können Sie genau sehen, wie eine Plone-Seite in einem Page-Template gebaut wird und wie Sie diese individuellen Seitenelemente anpassen können.</p>
<p>Wenn Sie sich die Plone-Hauptseite anschauen, sehen Sie darauf eine Reihe von Elementen. Abbildung 7.8 zeigt eine Plone-Seite, auf der alle wesentlichen Elemente der Benutzerschnittstelle markiert sind. Tabelle 7.2 beschreibt den Zweck jedes dieser Elemente. Zu jedem Element in Abbildung 7.8 finden Sie einen entsprechenden Eintrag in der Tabelle.</p>
<a class="reference external image-reference" href="img/07-08.png/image_view_fullscreen"><img alt="img/07-08.png" class="original" src="img/07-08.png" /></a>
<p>Abbildung 7.8. Alle Hauptelemente in der Plone-Benutzerschnittstelle</p>
<p>Tabelle 7.2. Elemente der Benutzerschnittstelle</p>
<table border="1" class="docutils">
<colgroup>
<col width="2%" />
<col width="25%" />
<col width="73%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Nr.</th>
<th class="head">Name</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>1</td>
<td>Site-Logo</td>
<td>Enthält das obere Logo.</td>
</tr>
<tr><td>2</td>
<td>Suchformular</td>
<td>Enthält das Suchformular.</td>
</tr>
<tr><td>3</td>
<td>Portal-Reiter</td>
<td>Enthält die Reiter oben auf der Site.</td>
</tr>
<tr><td>4</td>
<td>Persönliche Leiste</td>
<td>Enthält persönliche Informationen zu dem Benutzer, z.B. <em>Login</em> und <em>Mein Ordner</em>.</td>
</tr>
<tr><td>5</td>
<td>Pfadnavigation</td>
<td>Zeigt den Ort des aktuellen Inhalts an.</td>
</tr>
<tr><td>6</td>
<td>Linker Slot</td>
<td>Hier werden Portlets angezeigt, die in der Eigenschaft <tt class="docutils literal">left_slot</tt> vorkommen.</td>
</tr>
<tr><td>7</td>
<td>Inhalts-Reiter</td>
<td>Zeigt die Aktionen der Kategorie <tt class="docutils literal">content_tabs</tt> für dieses Stück Inhalt.</td>
</tr>
<tr><td>8</td>
<td>Inhalts Drop-Down--Listen</td>
<td>Zeigt einige Dropdown-Menüs für diesen Inhalt, Workflow oder neue Inhaltstypen an.</td>
</tr>
<tr><td>9</td>
<td>Dokument-Aktionen</td>
<td>Zeigt die Aktionen für dieses spezielle Stück Inhalt an: Drucken oder als E-Mail versenden.</td>
</tr>
<tr><td>10</td>
<td>Herkunftsangaben</td>
<td>Zeigt eine Beschreibung des Inhalts und seines Autors.</td>
</tr>
<tr><td>11</td>
<td>Rechter Slot</td>
<td>Hier werden Portlets angezeigt, die in der Eigenschaft <tt class="docutils literal">right_slot</tt> vorkommen.</td>
</tr>
<tr><td>12</td>
<td>Fußzeile</td>
<td>Zeigt Informationen am Seitenende an.</td>
</tr>
<tr><td>13</td>
<td>Kolophon</td>
<td>Zeigt weitere Angaben unter der Fußzeile an.</td>
</tr>
</tbody>
</table>
<p>Einen Abschnitt dieses Templates habe ich nicht behandelt: den Inhalt. Der ganze Text von <em>Welcome to Plone</em> bis hinunter zu <em>The Plone Team</em> ist Inhalt, der von Benutzern erstellt und bearbeitet wird. Dies ist der  <tt class="docutils literal">main</tt>-Slot im Page Template, der mit einem bestimmten Inhaltstyp oder Page Template gefüllt wird, wie Sie gesehen haben. In Kapitel 6 habe ich Slots behandelt und Ihnen gezeigt, wie Sie durch die Verwendung des <tt class="docutils literal">main</tt>-Slots sicherstellen können, dass der Inhalt  einer Plone-Seite erscheint.</p>
<p>Wie passen Sie also bei all diesen Komponenten einer Plone-Seite einen bestimmten Teil an? Die Antwort lautet, dass Sie den passenden Teil im <tt class="docutils literal">main_template</tt> erst finden und dann sehen müssen, welchen Teil er aufruft, um diesen anschließend anzupassen. Aus diesem Grund werde ich das Haupt-Template im Detail behandeln.</p>
<p>Auf den ersten Blick scheint das Haupt-Template ziemlich lang und kompliziert zu sein, aber es besteht fast nur aus Makros, und seine Hauptaufgabe ist lediglich die, Inhalte aus anderen Bereichen zu holen. Das Haupt-Template finden Sie, indem Sie nacheinander auf <em>portal_skins</em>, <em>plone_templates</em> und <em>main_template</em> klicken.</p>
<p>Die Philosophie hinter dem Haupt-Template ist, dass ein Benutzer die aktuelle Konfiguration des Templates nicht ändern muss, es sei denn, es sind größere Veränderungen geplant. Weil das Haupt-Template alle Inhalte aus anderen Stellen in Plone zusammenträgt, können Sie die zusammengestellte Seite ändern, indem Sie diese individuellen Elemente anpassen. Das heißt, Sie können genau die gewünschten Abschnitte modifizieren und müssen nicht das gesamte Template ändern.</p>
<p>Das Haupt-Template macht ausgiebigen Gebrauch von XML-Namespaces, um den <tt class="docutils literal">metal</tt>-Code so einfach mit möglich zu halten. Beispiel:</p>
<pre class="literal-block">
&lt;metal:headslot define-slot=&quot;head_slot&quot; /&gt;
   &lt;!-- A slot where you can insert elements in the header from a template --&gt;
</pre>
<p>Hierbei entspricht der Tag-Name keinem XHTML-Standardelement, sondern er verwendet das  <tt class="docutils literal">metal:</tt>-Präfix, um den Namespace <tt class="docutils literal">metal:headslot</tt> zu definieren. Das hat folgende Vorteile:</p>
<ul class="simple">
<li>Das Element <tt class="docutils literal">headslot</tt> hat eine Semantik, da es das Element beschreibt. Man kann leicht feststellen, dass es der Slot für alles Mögliche ist, was Sie zum Kopf Ihrer Seite hinzufügen möchten.</li>
<li>Attribute in diesem Element benutzen den Namespace im Element, falls nicht anders angegeben, d.h., statt <tt class="docutils literal"><span class="pre">metal:fill-slot</span></tt> können Sie einfach <tt class="docutils literal"><span class="pre">fill-slot</span></tt> benutzen.</li>
<li>Das eigentliche Tag ist kein gültiges XHTML-Tag, d.h., es wird nicht dargestellt. Wenn die Darstellung des Tags jedoch gültiges XHTML generiert, wird dieser XHTML-Code dargestellt.</li>
</ul>
<p>Wenn ein Makro verwendet wird, wird der Inhalt des aufrufenden Templates entfernt, d.h., man kann Kommentare in das aufrufende Template als Text ins Makro setzen. Beispiel:</p>
<pre class="literal-block">
&lt;div metal:use-macro=&quot;here/global_searchbox/macros/quick_search&quot;&gt;
    The quicksearch box, normally placed at the top right
&lt;/div&gt;
</pre>
<p>Wegen des Kommentars kann man leicht feststellen, dass sich dieses Makro auf den Kasten mit der Suchabfrage in der oberen rechten Ecke der Seite (Element 2 in Abbildung 7.8) bezieht. Um das Makro zu sehen, müssen Sie das Script namens <tt class="docutils literal">global_searchbox</tt> mit dem Makro <tt class="docutils literal">quick_search</tt> darin finden. Das Haupt-Template geht durch die <tt class="docutils literal">main</tt>-Makros und zieht dabei Informationen aus verschiedenen Templates und Scripts heraus.</p>
<p>Nach diesem Abschnitt erreicht das Haupt-Template den Hauptinhalt der Seite, d.h. das darzustellende Objekt. In Kapitel 6 habe ich den Unterschied zwischen einem Slot und einem Makro erklärt. Erinnern Sie sich daran, dass ein Template Slots definiert, die dann mit Inhalt gefüllt werden. Für diesen Inhalt gibt es wirklich nur einen wichtigen Slot, den ich schon oft genannt habe: den <tt class="docutils literal">main</tt>-Slot.</p>
<p>Ein häufiges, vielleicht verwirrendes Muster in Plone kommt bei der Definition eines Slots innerhalb eines <tt class="docutils literal">fill</tt>-Slots vor. Folgendes ist z.B. die Definition des Slots <tt class="docutils literal">css_slot</tt>:</p>
<pre class="literal-block">
&lt;metal:cssslot fill-slot=&quot;css_slot&quot;&gt;
    &lt;!-- A slot where you can insert CSS from a template --&gt;
    &lt;metal:cssslot define-slot=&quot;css_slot&quot; /&gt;
&lt;/metal:cssslot&gt;
</pre>
<p>Dieses Muster sieht etwas merkwürdig aus, aber es definiert den Slot und erzeugt dann den <tt class="docutils literal">fill</tt>-Slot erneut. Wenn Sie sich das Haupt-Template genau anschauen, befinden sich diese Slots tatsächlich innerhalb von <tt class="docutils literal"><span class="pre">use-macro</span></tt> im Kopf, d.h., das Kopf-Makro darf diesen Slot füllen. Aber Sie möchten auch, dass das End-Template den Slot füllen kann, deswegen wird der Slot neu definiert. Somit kann ein Slot nun an zwei Orten gefüllt werden, was ein nützliches Vorgehen beim Ändern des Templates ist.</p>
<p>Bei der Durchsicht des restlichen Haupt-Templates werden Sie die linke und rechte Spalte, die Fußzeilen und das Kolophon erreichen. Beachten Sie, dass die linke Spalte möglicherweise vor dem Hauptinhalt einer Seite erscheint (jedenfalls, wenn Ihre Sprache von links nach rechts gelesen wird), aber das Stylesheet bewegt sie dorthin. Das garantiert, dass beim Besuchen der Site mit einem rein textbasierten Browser der Hauptinhalt zuerst erscheint und nicht erst nach den ganzen Navigationsoptionen.</p>
<p>Tabelle 7.3 beschreibt die Makros und Slots im Haupt-Template.</p>
<p>Tabelle 7.3. Makros und Slots im Haupt-Template</p>
<table border="1" class="docutils">
<colgroup>
<col width="31%" />
<col width="31%" />
<col width="9%" />
<col width="28%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head" colspan="2">Beschreibung</th>
<th class="head">Slot oder Makro?</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Cache headers</td>
<td colspan="2">Setzt den Kopf des HTTP-Caches (Hypertext
Transfer Protocol) für den Inhalt.</td>
<td>Makro: <tt class="docutils literal">cacheheaders</tt> in
<tt class="docutils literal">global_cache_settings</tt></td>
</tr>
<tr><td>Head slot</td>
<td colspan="2">Hiermit können Inhalte etwas zum
<tt class="docutils literal">head</tt>-Element einer Seite hinzufügen.</td>
<td>Slot: <tt class="docutils literal">head_slot</tt></td>
</tr>
<tr><td>CSS slot</td>
<td colspan="2">Hiermit können Inhalte einen eigenen
CSS-Code zur Seite hinzufügen.</td>
<td>Slot: <tt class="docutils literal">css_slot</tt></td>
</tr>
<tr><td>JavaScript head slot</td>
<td colspan="2">Hiermit können Inhalte einen eigenen
JavaScript-Code zur Seite hinzufügen.</td>
<td>Slot:
<tt class="docutils literal">javascript_head_slot</tt></td>
</tr>
<tr><td>Site actions</td>
<td colspan="2">Die Site-Aktionen erlauben Ihnen eine Reihe
von Aktionen oberhalb des   <em>Suchen</em>-
Kastens. Standardmäßig können Sie damit
die Schriftgröße verändern.</td>
<td>Makro: <tt class="docutils literal">site_actions</tt> in
<tt class="docutils literal">global_siteactions</tt></td>
</tr>
<tr><td>Quick search</td>
<td colspan="2">Der Kasten für die schnelle Suche in der
oberen rechten Ecke.</td>
<td>Makro: <tt class="docutils literal">quick_search</tt> in
<tt class="docutils literal">global_searchbox</tt></td>
</tr>
<tr><td>Portal tabs</td>
<td colspan="2">Die (normalerweise blauen) Portal-Tabs, die
normalerweise oben links erscheinen. Welche
wirklich angezeigt werden, wird von den
Aktionen bestimmt. Sie bestimmen, wie die
Tabs in HTML dargestellt werden.</td>
<td>Makro: <tt class="docutils literal">portal_tabs</tt> in
<tt class="docutils literal">global_sections</tt></td>
</tr>
<tr><td>Personal bar</td>
<td colspan="2">Die persönliche Leiste oben rechts:
einloggen, ausloggen usw.</td>
<td>Makro: <tt class="docutils literal">personal_bar</tt> in
<tt class="docutils literal">global_personalbar</tt></td>
</tr>
<tr><td>Path bar</td>
<td colspan="2">Die Pfad-Navigationsleiste, die mit
&quot;Sie sind hier:&quot; beginnt.</td>
<td>Makro: <tt class="docutils literal">path_bar</tt> in
<tt class="docutils literal">global_pathbar</tt></td>
</tr>
<tr><td>Content views</td>
<td colspan="2">Die (normalerweise grünen) Tabs oberhalb
des Inhalts. Sie werden nur angezeigt,
wenn der aktuelle Benutzer den Inhalt
bearbeiten darf. Welche Tabs wirklich
angezeigt werden, wird von den Aktionen
bestimmt. Sie bestimmen, wie die Tabs in
in HTML dargestellt werden.</td>
<td>Makro: <tt class="docutils literal">content_views</tt> in
<tt class="docutils literal">global_contentviews</tt></td>
</tr>
<tr><td>Content actions</td>
<td colspan="2">Die kleinen Dropdown-Aktionen in der
oberen rechten Ecke der Kontextleiste.</td>
<td>Makro: <tt class="docutils literal">content_actions</tt> in
<tt class="docutils literal">global_contentviews</tt></td>
</tr>
<tr><td>Portal status message</td>
<td colspan="2">Ein Hinweis, der immer dann erscheint, wenn
sich etwas verändert.</td>
<td>Makro: <tt class="docutils literal">portal_message</tt> in
<tt class="docutils literal">global_statusmessage</tt></td>
</tr>
<tr><td>Header</td>
<td colspan="2">Der Kopfteil eines Inhalts.</td>
<td>Slot: <tt class="docutils literal">header</tt></td>
</tr>
<tr><td>Main</td>
<td colspan="2">Der Hauptteil eines Inhalts.</td>
<td>Slot: <tt class="docutils literal">main</tt></td>
</tr>
<tr><td>Sub</td>
<td colspan="2">Der untere Teil eines Inhalts, in dem die
Kommentare zum Objekt erscheinen.</td>
<td>Slot: <tt class="docutils literal">sub</tt></td>
</tr>
<tr><td>Linke Portlets</td>
<td colspan="2">Die Slots bzw. Portlets, die im linken
Teil der Seite erscheinen. Hier gibt es
einige Definitionen: <tt class="docutils literal"><span class="pre">column-one-slot</span></tt>
ist die ganze linke Spalte, und
<tt class="docutils literal"><span class="pre">portlets-one-slot</span></tt> ist dann der Slot.
Wenn keiner dieser Slots definiert ist,
wird das Makro aufgerufen.</td>
<td>Makro: <tt class="docutils literal">left_column</tt> in
<tt class="docutils literal">portlets_fetcher</tt></td>
</tr>
<tr><td>Rechte Portlets</td>
<td colspan="2">Die Slots bzw. Portlets, die im rechten
Teil der Seite erscheinen (siehe linke
Portlets).</td>
<td>Makro: <tt class="docutils literal">right_column</tt> in
<tt class="docutils literal">portlets_fetcher</tt></td>
</tr>
<tr><td>Fußzeile</td>
<td colspan="2">Copyright- und andere Hinweise.</td>
<td>Makro: <tt class="docutils literal">portal_footer</tt> in
<tt class="docutils literal">footer</tt></td>
</tr>
<tr><td>Kolophon</td>
<td colspan="2">Verschiedene Hinweise am unteren Ende.</td>
<td>Makro: <tt class="docutils literal">colophon</tt> in
<tt class="docutils literal">colophon</tt></td>
</tr>
</tbody>
</table>
<p>Mit dieser Information ausgestattet, ist es nun eine Frage der Anpassung eines Makros oder Slots, wenn das Look-and-Feel der Seite geändert werden soll. Um es noch einmal zu sagen: Sie sollten nicht das Haupt-Template selbst anpassen, sondern die Teile, die es aufruft. Der nächste Abschnitt zeigt ein paar Beispiele für Anpassungen, die Sie in Plone vornehmen können.</p>
</div>
<div class="section" id="beispiele-fur-anpassungs-code-schnipsel-untersuchen">
<h4>Beispiele für Anpassungs-Code-Schnipsel untersuchen</h4>
<p>Die folgenden Abschnitte enthalten einige Beispiele, die einfache Anpassungen an einer Plone-Site demonstrieren. Bei manchen Lösungen werden ein oder zwei Alternativen zur Lösung der Aufgabe gezeigt.</p>
</div>
<div class="section" id="einen-block-entfernen">
<h4>Einen Block entfernen</h4>
<p>Ein ziemlich netter Trick ist es, wenn man einen Block wie die Pfadleiste oder den Suchkasten aus der Benutzerschnittstelle leicht entfernen kann. Das können Sie auf zwei Arten erreichen. Der offensichtlichste Ansatz besteht in der Anpassung des Makros, mit dem das Element angezeigt wird. Um z.B. die Pfadleiste zu entfernen, könnten Sie nacheinander auf <em>portal_skins</em>, <em>plone_templates</em> und <em>global_pathbar</em> klicken und dann das Element auf der Ebene des Page Templates entfernen. Sie können z.B. Folgendes ändern:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;path_bar&quot;
    id=&quot;pathBar&quot;
    tal:define=&quot;breadcrumbs python:here.breadcrumbs(here);
        portal_url portal_url|here/portal_url&quot;&gt;
</pre>
<p>indem Sie wie folgt eine Code-Zeile hinzufügen:</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;path_bar&quot;
    id=&quot;portal-breadcrumbs&quot;
    tal:condition=&quot;nothing&quot;
    tal:define=&quot;breadcrumbs python:here.breadcrumbs(here);
        portal_url portal_url|here/portal_url&quot;&gt;
</pre>
<p>Das bedeutet, es wird ein Page Template angepasst, was überhaupt kein Problem ist, weil Sie mittlerweile schon damit vertraut sein sollten. Ein etwas anderer Ansatz besteht darin, Elemente auf CSS-Ebene zu verstecken. Das bedeutet, ein Element wird weiterhin dargestellt, und es wird HTML dafür generiert, wird dann aber für den Client ausgeschaltet, d.h., es wird unsichtbar. Da das HTML weiterhin generiert wird, ist diese Lösung suboptimal, aber es ist ein netter Trick.</p>
<p>Die meisten Elemente in Plone haben eine eindeutige DOM-Element-ID (Document Object Model). Im Fall der Pfadleiste lautet sie <tt class="docutils literal"><span class="pre">portal-breadcrumbs</span></tt>, wie Sie im Code oben sehen können. Um <tt class="docutils literal"><span class="pre">portal-breadcrumbs</span></tt> nicht mehr anzuzeigen, fügen Sie einfach Folgendes zu <tt class="docutils literal">ploneCustom.css</tt> hinzu:</p>
<pre class="literal-block">
#portal-breadcrumbs {
    display: none;
}
</pre>
</div>
<div class="section" id="portal-reiter-andern">
<h4>Portal-Reiter ändern</h4>
<p>Ich habe Ihnen bereits gezeigt, wie Sie den Text der Portal-Reiter ändern können, indem Sie die Aktionen ändern. Sie werden mit Hilfe des Stylesheets angezeigt, nicht etwa mit Tabellen, auch wenn man als Benutzer das vielleicht erst einmal denkt. In Tabelle 7.3 sehen Sie, dass der Code für die Portal-Reiter in <tt class="docutils literal">portalTabs</tt> steht. Um den Rahmen der nicht ausgewählten Reiter gepunktet darzustellen, können Sie einfach das Stylesheet <tt class="docutils literal">ploneCustom</tt> wie folgt ändern:</p>
<pre class="literal-block">
#portal-globalnav li a {
    border: 1px dotted;
}
</pre>
<p>Die Reiter bestehen aus einer Reihe von HTML-Listen- (<tt class="docutils literal">li</tt>) und Anker-Elementen (<tt class="docutils literal">a</tt>), d.h., durch eine Änderung des CSS für diese Elemente ändern Sie das Erscheinungsbild dieser Reiter. Im später folgenden Abschnitt &quot;Fallstudie: Die NASA-Skin&quot; werde ich zeigen, wie man diese Reiter zu Bildern abändert.</p>
<p>Dank CSS können Sie mit dem Attribut <tt class="docutils literal">position</tt> alle Elemente an einen anderen Platz verschieben. Verschieben Sie Ihre Reiter als Nächstes nach ganz oben auf den Bildschirm, oberhalb des Logos und des <em>Suchen</em>-Kastens. Zu diesem Zweck verwenden Sie den Wert <tt class="docutils literal">absolute</tt> bei der Position, wo Sie auch die Werte <tt class="docutils literal">left</tt>, <tt class="docutils literal">right</tt>, <tt class="docutils literal">top</tt> und <tt class="docutils literal">bottom</tt> verwenden dürfen. Fügen Sie Folgendes zum Stylesheet <tt class="docutils literal">ploneCustom</tt> hinzu, um die Portal-Reiter oben auf Ihre Plone-Site zu platzieren:</p>
<pre class="literal-block">
#portal-globalnav {
  position: absolute;
  top: 0em;
}
</pre>
<p>Das ist ein mächtiges Verfahren zum Verschieben von Elementen. Beim Positionieren der Elemente haben Sie mehrere Möglichkeiten, inklusive einer relativen Positionierung, aber das erfordert etwas Arbeit mit CSS, um die Positionierung genau hinzubekommen.</p>
</div>
<div class="section" id="linke-und-rechte-slots-verschieben">
<h4>Linke und rechte Slots verschieben</h4>
<p>Die linken und rechten Slots habe ich in Kapitel 4 beschrieben. Dort habe ich auch gezeigt, wie Sie einen neuen Slot zur Slot-Liste hinzufügen. Vielleicht haben Sie bemerkt, dass die Bezeichnungen <em>linke</em> und <em>rechte</em> Slots ein wenig missverständlich sein können. Standardmäßig werden die Slots in diesen Positionen angezeigt, aber sie lassen sich leicht verschieben.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Das funktioniert nur dann, wenn Sie die Plone Tableless-Skin verwenden. Da das aber nicht die Standardeinstellung ist, müssen Sie die Skin mit dem Werkzeug <tt class="docutils literal">portal_skins</tt> so ändern, wie es oben im Abschnitt &quot;Skins mit dem Werkzeug portal_skins verwalten&quot; beschrieben wurde.</p>
</div>
<p>Wenn Sie z.B. die linken Portlets rechts auf der Seite anzeigen möchten, könnten Sie das mit der folgenden Änderung in <tt class="docutils literal">ploneCustom.css</tt> bewirken:</p>
<pre class="literal-block">
#portal-column-one  {
    float: right;
}
#portal-column-content {
    float: left;
}
</pre>
<p>Dadurch wird die Spalte ganz links nach rechts verschoben, und der Hauptteil wandert nach links.</p>
</div>
<div class="section" id="hilfe-in-formularen-verstecken">
<h4>Hilfe in Formularen verstecken</h4>
<p>Wenn Sie in allen Formularen die Hilfe ausblenden wollten, wäre es unrealistisch, dafür alle Templates zu ändern. Aber eine ähnliche Taktik könnten Sie einsetzen, um die Pfadleiste zu verstecken, indem Sie bei den Forularelementen einfach nur <tt class="docutils literal">display: none</tt> setzen. Folgendes hat den gewünschten Effekt, nämlich das Eingabeelement nicht in eine neue Zeile zu setzen:</p>
<pre class="literal-block">
div.formHelp {
    display: none;
}
</pre>
<p>Abbildung 7.9 zeigt die Feedback-Seite ohne Pfadnavigation, mit versteckter Hilfe, gepunkteten Reitern und mit einem nach rechts verschobenen linken Slot, was alles mit nur ein paar Zeilen CSS geändert wurde.</p>
<a class="reference external image-reference" href="img/07-09.png/image_view_fullscreen"><img alt="img/07-09.png" class="original" src="img/07-09.png" /></a>
<p>Abbildung 7.9. Kombinationseffekt einiger Beispiele</p>
</div>
<div class="section" id="wie-finden-sie-element-x">
<h4>Wie finden Sie Element X?</h4>
<p>Wie ich gezeigt habe, sind die Templates, Scripts und Bilder, die eine Plone-Skin ausmachen, im Verzeichnis <tt class="docutils literal">skins</tt> einer Plone-Installation enthalten. In diesem Verzeichnis befinden sich viele Dateien, d.h., es wäre langwierig und unproduktiv, bei einer Dateiänderung durch die ganze Liste zu gehen. Stattdessen ist es sehr hilfreich, einige Grundtechniken zum Finden jener Elemente zu beherrschen, die Sie ändern möchten.</p>
<p>Überlegen Sie sich, auf welcher Ebene Sie das Element anpassen möchten. Wie schon erwähnt, stehen Ihnen bei der Darstellung eines Objekts drei Ebenen zur Verfügung. Wenn Sie die visuelle Darstellung oder Platzierung des Elements ändern möchten, können Sie die Änderungen wahrscheinlich allein mit CSS vornehmen und müssen sonst nichts tun.</p>
<p>Falls CSS nicht ausreicht, finden Sie den nächstbesseren Ort bei der Suche in den Templates. Nehmen Sie beispielsweise an, sie möchten den Text ändern, der auf der Seite erscheint, wenn sich ein Benutzer anmeldet, oder Sie möchten gleich die ganze Seite ändern. In diesem Beispiel werden Sie die Seite in Abbildung 7.10 ändern, damit daraus ein Script wird, das etwas Ungewöhnliches macht.</p>
<a class="reference external image-reference" href="img/07-10.png/image_view_fullscreen"><img alt="img/07-10.png" class="original" src="img/07-10.png" /></a>
<p>Abbildung 7.10. Die Seite &quot;Sie sind jetzt eingeloggt&quot;</p>
<p>Es gibt einige Hinweise darauf, wie man dieses Template findet, das Sie ändern können. Im Folgenden gehe ich sie alle durch.</p>
</div>
<div class="section" id="suchen-mit-hilfe-der-url">
<h4>Suchen mit Hilfe der URL</h4>
<p>Der URL (Uniform Resource Locator) auf eine Seite wird in eine Folge von Objekten in Plone übersetzt, die traversiert werden. In Abbildung 7.11 habe ich eine Traversierung zur Seite <tt class="docutils literal">login_success</tt> durchgeführt. In diesem Fall ist der letzte Teil des URL <tt class="docutils literal">login_success</tt>, wie Sie in der Adresszeile in Abbildung 7.11 sehen können. Wenn ein Objekt in ein FSDV geladen wird, wird die Dateierweiterung weggelassen, d.h., Sie suchen nach einem Template oder Script, das mit <tt class="docutils literal">login_success</tt> anfängt.</p>
<a class="reference external image-reference" href="img/07-11.png/image_view_fullscreen"><img alt="img/07-11.png" class="original" src="img/07-11.png" /></a>
<p>Abbildung 7.11. Suche nach einer ID</p>
<p>In Zope können Sie diese Suche durchführen, indem Sie zum <tt class="docutils literal">portal_skins</tt>-Werkzeug gehen und auf den <em>Find</em>-Reiter klicken. Dort geben Sie <strong>login_success</strong> im Feld <tt class="docutils literal">with ids</tt> ein. Lassen Sie alle anderen Einstellungen unverändert, und klicken Sie auf den <em>Find</em>-Button. Dann finden Sie ganz bestimmt das Template <tt class="docutils literal">login_success</tt>.</p>
<p>Diese Suche können Sie auch im Dateisystem durchführen, je nach Betriebssystem und verfügbaren Werkzeugen. Die schnellste Methode, um diese Datei unter Linux zu finden, ist die, in Ihr <tt class="docutils literal">CMFPlone</tt>-Verzeichnis zu gehen und Folgendes zu machen:</p>
<pre class="literal-block">
$ cd skins
$ find -name 'login_success*' -print
./plone_forms/login_success.pt
</pre>
<p>Unter Windows öffnen Sie den Ordner <tt class="docutils literal">CMFPlone</tt> im Windows Explorer und klicken auf den <em>Suchen</em>-Reiter. Geben Sie dann den Namen der Datei als <strong>login_success</strong> ein, und klicken Sie auf <em>Suchen</em>. Danach sollten Sie eine Liste der passenden Dateien erhalten.</p>
<p>Als Ergebnis dieser Suche sollte <tt class="docutils literal">CMFPlone/plone_forms/login_success.pt</tt> herauskommen. Wenn Sie die gleiche Suche im ZMI durchführen, klicken Sie nacheinander auf <em>portal_skins</em>, <em>plone_forms</em> und <em>login_success</em>.</p>
</div>
<div class="section" id="suche-nach-einem-textteil">
<h4>Suche nach einem Textteil</h4>
<p>Ein etwas grober Ansatz, der meist auch zum Erfolg führt, besteht darin, eine Volltextsuche im Code durchzuführen, um das Element zu finden, das die Seite darstellt. Wenn Sie z.B. die Seite in Abbildung 7.12 betrachten, sehen Sie, dass darauf der Text <em>Notice that the top</em> vorkommt. Die einfachste Methode, den Teil zu finden, der diesen Text anzeigt, ist, danach zu suchen.</p>
<a class="reference external image-reference" href="img/07-12.png/image_view_fullscreen"><img alt="img/07-12.png" class="original" src="img/07-12.png" /></a>
<p>Abbildung 7.12. Suche nach Text</p>
<p>In Zope können Sie diese Suche auch durchführen, indem Sie zum <tt class="docutils literal">portal_skins</tt>-Werkzeug gehen und auf den <em>Find</em>-Reiter klicken. Dort geben Sie <strong>Notice that the top</strong> im Feld <tt class="docutils literal">containing</tt> ein. Alle anderen Einstellungen lassen Sie unverändert und klicken dann auf den <em>Find</em>-Button. Auch dann sollten Sie ganz bestimmt das Template <tt class="docutils literal">login_success</tt> finden.</p>
<p>Diese Suche können Sie auch im Dateisystem durchführen, je nach Betriebssystem und verfügbaren Werkzeugen. Die schnellste Methode, diese Datei unter Linux zu finden, ist die, in Ihr <tt class="docutils literal">CMFPlone</tt>-Verzeichnis zu gehen und Folgendes einzugeben:</p>
<pre class="literal-block">
$ grep -ri &quot;Notice that the top&quot; *
plone_forms/login_success.pt: Notice that the top
</pre>
<p>Unter Windows öffnen Sie den Ordner <tt class="docutils literal">CMFPlone</tt> im Windows Explorer und klicken auf den <em>Suchen</em>-Reiter. Geben Sie dann als Inhalt der Datei <strong>Notice that the top</strong> ein, und klicken Sie auf <em>Suchen</em>. Danach sollten Sie eine Liste der passenden Dateien erhalten. Mit dieser etwas groben Technik haben Sie das Template  <tt class="docutils literal">login_success</tt> gefunden, das einem Benutzer diese Meldung anzeigt.</p>
<p>Diese Methode hat folgende Tücken:</p>
<ul class="simple">
<li>Passen Sie auf die Schreibweise von Inhalten in CSS auf! Suchen Sie immer unabhängig von der Schreibweise (ist in Windows standardmäßig eingestellt). Es ist ärgerlich, nach <tt class="docutils literal">home</tt> zu suchen, wenn es im Template tatsächlich <tt class="docutils literal">Home</tt> heißt und nur von CSS mit Kleinbuchstaben angezeigt wird.</li>
<li>Wenn Sie das in einer anderen Sprache als Englisch versuchen, wurde der Inhalt eventuell lokalisiert, wodurch die Suche fehlschlägt.</li>
<li>Gelegentlich gibt es keinen suchbaren Text, den man finden kann. In diesem Fall ist eine Suche über die URL ratsam.</li>
</ul>
</div>
</div>
<div class="section" id="neue-skins-und-ebenen-erstellen">
<h3>Neue Skins und Ebenen erstellen</h3>
<p>Bisher habe ich die Anpassung vorhandener Skins behandelt. Die Erstellung einer völlig neuen Skin oder einer neuen Ebene unterscheidet sich davon nur wenig. Ich werde einen springenden Punkt behandeln: die Platzierung Ihrer Templates und Scripts im Dateisystem.</p>
<p>Templates und Scripts im Dateisystem zu erstellen und neue Skins und Ebenen zu erstellen ist definitiv die beste Art, um eine langfristige Wartbarkeit und Flexibilität zu erreichen. Die Erstellung von Skin-Elementen ist nicht nur wesentlich leichter mit den gewohnten Werkzeugen im Dateisystem, sondern erlaubt Ihnen auch, Ihren Code sehr leicht auszuliefern. Das Schreiben im Dateisystem ist für fast alle Plone-Entwickler die Arbeitsweise der Wahl, bei Bedarf mit kleinen Änderungen im Verzeichnis <tt class="docutils literal">custom</tt>.</p>
<div class="section" id="neue-skins-erstellen">
<h4>Neue Skins erstellen</h4>
<p>Wie Sie gesehen haben, ist eine Skin nicht mehr als eine Ansammlung von Ebenen. Für meine neue Skin wollte ich all meinen eigenen Code an einer Stelle konzentrieren. Dazu habe ich das  <tt class="docutils literal">portal_skins</tt>-Werkzeug gewählt und habe einen neuen Ordner mit der ID <tt class="docutils literal">custom_chrome</tt> hinzugefügt.</p>
<p>Um dann eine neue Skin zu erstellen, müssen Sie <em>portal_skins</em> anklicken, den <em>Properties</em>-Reiter wählen und unter dem Text <em>Add a new skin</em> eine neue Skin hinzufügen. Sie müssen eine Reihe von Ebenen eingeben, die Sie für diese Skin einrichten möchten. In diesem Beispiel habe ich eine neue Skin namens  <em>Custom Chrome</em> sowie eine Reihe von Ebenen hinzugefügt, wie in Abbildung 7.13 zu sehen ist.</p>
<a class="reference external image-reference" href="img/07-13.png/image_view_fullscreen"><img alt="img/07-13.png" class="original" src="img/07-13.png" /></a>
<p>Abbildung 7.13. Hinzufügen der Skin Custom Chrome</p>
<p>Dann habe ich die Ebenen für die Skin hinzugefügt. In diesem Fall hatte die Skin keine Ebene namens <tt class="docutils literal">custom</tt> darin. Stattdessen hatte sie einen Ordner namens <tt class="docutils literal">custom_chrome</tt>. Nun haben Sie zwei Skins, die zwei Ebenen und zwei Ordner verwenden. Alle zum <tt class="docutils literal">custom_chrome</tt>-Ordner hinzugefügten Objekte beeinflussen diese Skin, aber nicht die Plone-Default-Skin.</p>
</div>
<div class="section" id="mehrere-skins-benutzen">
<h4>Mehrere Skins benutzen</h4>
<p>Wie schon erwähnt wurde, enthält eine Standard-Plone-Site zwei Skins: <em>Plone Default</em> und <em>Plone Tableless</em>. Im vorigen Abschnitt habe ich eine neue Skin hinzugefügt, <em>Custom Chrome</em>. Wie ich in Kapitel 4 beschrieben habe, können Sie die Standard-Skin mit der Plone-Schnittstelle setzen. Klicken Sie auf <em>Plone Konfiguration</em> und dann auf den <em>Aussehen</em>-Button. Dieser bildet die Möglichkeiten ab, die im ZMI verfügbar sind, wenn Sie dort <em>portal_skins</em> anklicken, den <em>Properties</em>-Reiter wählen und auf der Seite nach unten scrollen.</p>
<p>Aber Sie haben noch eine weitere Möglichkeit: eine Variable namens <tt class="docutils literal">REQUEST</tt>. Dies ist die Anfragevariable, die die Informationen über die Skin des Benutzers enthält. Standardmäßig ist das <tt class="docutils literal">plone_skin</tt>, was der Name des Cookies ist. Aber sie kann auch über andere Anfragevariablen wie den Abfrage-String übergeben werden. Diese Variable ist nur über das ZMI verfügbar.</p>
<p>Sie können Skins auch mit Hilfe eines Programms setzen. Damit können Entwickler unterschiedliche Benutzer abhängig von irgendeiner Geschäfts- oder Site-Logik mit jeweils einer eigenen Skin bedienen. Wenn ein Benutzer z.B. Inhalte für eine Site schreibt, sieht er die Plone-Standard-Skin. Anonyme Benutzer hingegen sehen eine völlig andere Skin. Nicht der Benutzer trifft dann die Entscheidung, sondern die Site. Wenn Sie wirklich wollen, können Sie die Skin abhängig vom Ordner machen, auf den zugegriffen wird, allerdings kann dieser Ansatz verwirrend sein, daher möchte ich ihn nicht empfehlen.</p>
<p>Um die Skin zu ändern, fügen Sie ein Script(Python)-Objekt namens <tt class="docutils literal">setSkin</tt> zur Wurzel Ihrer Plone-Site hinzu. Dann fügen Sie den folgenden Code hinzu:</p>
<pre class="literal-block">
##title=Skin changing script
##parameters=
req = context.REQUEST
if req['SERVER_URL'].find('internal.somesite.org') &gt; -1:
    context.changeSkin(&quot;Plone Default&quot;)
context.changeSkin(&quot;Custom Chrome&quot;)
</pre>
<p>Die eigentliche Logik für die Auswahl der Skin wird von der Geschäftslogik der Site abhängig sein. In diesem Fall erhalten alle, die auf  <a class="reference external" href="http://internal.somesite.org">http://internal.somesite.org</a> zugreifen, die Plone-Default-Skin, während alle, die auf <a class="reference external" href="http://external.somesite.org">http://external.somesite.org</a> zugreifen, die Custom Chrome-Skin erhalten. Leider ist ein Haken dabei, und zwar der, dass Sie die Skin nicht in Abhängigkeit von der Sicherheitsstufe des Benutzers wählen können (z.B. wenn authentifizierte Benutzer die eine Skin und Manager eine andere Skin sehen sollen). Dieser offensichtliche Bedarf kann momentan nicht erfüllt werden, ohne die Plone-Site ganz heftig zu hacken.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Man kann häufig beobachten, dass eine Skin von ungeprüften Client-Informationen abhängig gemacht wird, was aber nicht wirklich sicher ist, weil Sie der Information vom Client vertrauen. Wie Sie diese wirklich sicher machen, hängt von Ihren speziellen Netzwerkeinstellungen ab. In den meisten Fällen können Sie das einfach mit einer Firewall oder einem Proxy-Server wie Apache erledigen, der so konfiguriert werden könnte, dass alle externen Anfragen auf <a class="reference external" href="http://intern.einesite.org">http://intern.einesite.org</a> blockiert werden. Die Integration in Apache beschreibe ich in Kapitel 10.</p>
</div>
<p>Um diesen Code zu aktivieren, weisen Sie diesem Objekt eine Zugriffsregel zu. Das bedeutet, dass bei jedem Zugriff auf die Plone-Site das  Script (Python)-Objekt ausgeführt wird. Jedes Mal, wenn das Script läuft, wird die Skin abhängig vom Skript gesetzt. Um einem Skript eine Regel zuzuweisen, wählen Sie  <em>Set Access Rule</em> im Dropdown-Menü und geben dann den Namen Ihres Script (Python)-Objekts ein. Machen Sie nun einen Test, indem Sie Ihre Site besuchen und nachsehen, welche Skin Sie erhalten.</p>
<p>Mit Zugriffsregeln müssen Sie vorsichtig sein, weil sie bei jedem Zugriff auf diesen Ordner (oder die Plone-Site) aufgerufen werden. Sie müssen sicherstellen, dass sie korrekt sind und dass nichts Falsches darin passiert. Wenn Sie versehentlich ein fehlerhaftes oder unkorrektes Script(Python)-Objekt geschrieben haben und nicht einmal mehr im ZMI einen Zugriff darauf bekommen, um es zu reparieren, dann können Sie die Zugriffsregeln ausschalten, indem Sie Plone mit der folgenden Umgebungsvariablen neu starten:</p>
<pre class="literal-block">
SUPPRESS_ACCESSRULE = 1
</pre>
<p>Anhang A erklärt, wie Umgebungsvariablen gesetzt werden, falls Sie mit ihnen nicht vertraut sein sollten.</p>
</div>
<div class="section" id="eine-neue-skin-im-dateisystem-erstellen">
<h4>Eine neue Skin im Dateisystem erstellen</h4>
<p>Während dieser Kapitel habe ich durchgehend das ZMI benutzt. Aber die meisten Plone-Entwickler arbeiten tatsächlich mit dem Dateisystem. Eine Skin kann man im Dateisystem in der Tat sehr leicht erstellen.</p>
<p>Gehen Sie ins Home-Verzeichnis der Instanz Ihrer Plone-Installation. Erstellen Sie ein neues Verzeichnis im  <tt class="docutils literal">Products</tt>-Verzeichnis. Dessen Name ist der Name des Produkts, und laut Konvention ist er relativ kurz, ohne Leerzeichen oder Unterstriche und mit Groß- und Kleinschreibung. Beispiele hierfür sind  <tt class="docutils literal">PloneBookExample</tt>, <tt class="docutils literal">CMFPlone</tt> und <tt class="docutils literal">PloneSilverCity</tt>. Erstellen Sie in diesem Ordner eine neue Datei namens <tt class="docutils literal">__init__.py</tt> und ein Verzeichnis namens <tt class="docutils literal">skins</tt>. In der Datei <tt class="docutils literal">__init__.py</tt> müssen Sie die beiden folgenden Zeilen hinzufügen:</p>
<pre class="literal-block">
from Products.CMFCore import DirectoryView
DirectoryView.registerDirectory('skins', globals())
</pre>
<p>Als Nächstes starten Sie Plone erneut und klicken auf <em>portal_skins</em>, um ein <tt class="docutils literal">FSDV</tt> hinzuzufügen. Dann wird eine Liste der registrierten Verzeichnisse geöffnet. Scrollen Sie nach unten, bis Sie dasjenige finden, das dem gerade registrierten Verzeichinis entspricht. Das wird der Name des Verzeichnisses sein, mit <tt class="docutils literal">/skins</tt> am Ende. Geben Sie eine ID ein, die Sinn macht, und klicken Sie auf <em>Add</em>. Nun haben Sie ein leeres Verzeichnis, in dem Sie die Ebenen Ihrer Skin erstellen können.</p>
</div>
<div class="section" id="fehlersuche-in-skins">
<h4>Fehlersuche in Skins</h4>
<p>Ein weiterer Grund, aus dem ich mit Ihnen immer wieder das ZMI statt des Dateisystems benutzt habe, ist der, dass es Feedback bei Fehlern gibt und Sie damit vertraut macht, Objekte in andere Objekte zu platzieren. Noch eine positive Eigenschaft des ZMI ist, dass Änderungen sofort wirksam sind. Wenn Sie ein Objekt ändern und die Ansicht aktualisieren, sehen Sie sofort die Änderungen (vorausgesetzt, Sie haben keinen Cache).</p>
<p>Beim Dateisystem ist das nicht so. Wenn Sie etwas im Dateisystem ändern, wird es in Plone nicht aktualisiert. Der Grund dafür liegt in der Performance. Plone kann nicht wissen, dass Sie diese Änderung vorgenommen haben, d.h., es muss die Kopie dieses Objekts im Zope-Cache aktualisieren. Ohne sich auf Tricksereien mit Benachrichtigungen in Dateisystemen einzulassen ist eine Plone-Site in einem von zwei Zuständen: im Produktions- oder im Debug-Modus. Im Debug-Modus prüft Plone alle Verzeichnisse, findet veränderte Dateien und aktualisiert sich selbst. Das heißt, Sie können etwas ändern, und es wird sofort erscheinen. Im Produktionsmodus werden Ihre Änderungen jedoch erst dann wirksam, wenn Sie die Skin aktualisieren (siehe Kapitel 11) oder Zope neu starten.</p>
<p>Aus offensichtlichen Gründen ist bei der Entwicklung von Skins in Plone der Betrieb im Debug-Modus die richtige Wahl. Kapitel 2 zeigte, wie man die Konfiguration von Plone so ändern kann, dass es im Debug-Modus läuft. Hier eine kurze Wiederholung: Öffnen Sie die Datei <tt class="docutils literal">zope.conf</tt> im Verzeichnis <tt class="docutils literal">etc</tt> Ihrer Installation, und stellen Sie sicher, dass die Direktive <tt class="docutils literal"><span class="pre">debug-mode</span></tt> auf <tt class="docutils literal">on</tt> gesetzt ist.</p>
</div>
<div class="section" id="dateisystemobjekte-verwenden">
<h4>Dateisystemobjekte verwenden</h4>
<p>FSDVs können nur jene Zope-Objekte abbilden, die speziell zu diesem Zweck konfiguriert wurden. Das Zope-Objekt wird auf Grund der Erweiterung des Dateinamens bestimmt. Der Inhalt dieser Datei ist der Inhalt eines Attributs des Objekts, normalerweise der Hauptinhalt, z.B. der binäre Inhalt eines Bildes oder der Textinhalt des Templates.</p>
<p>Um ein Objekt in Ihrem leeren FSDV zu erstellen, gehen Sie einfach ins Verzeichnis <tt class="docutils literal">skins</tt> und beginnen damit, Dateien hinzuzufügen, die den Objekten entsprechen, die Sie erstellen möchten. Wenn die Datei einmal in Zope als Zope-Objekt geladen ist, wird diese Erweiterung abgeschnitten. Beispielsweise wird aus <tt class="docutils literal">ein_template.pt</tt> ein Dateisystem-Page Template mit der ID <tt class="docutils literal">ein_template</tt>. Tabelle 7.4 beschreibt diese Erweiterungen.</p>
<p>Tabelle 7.4. Erweiterungen</p>
<table border="1" class="docutils">
<colgroup>
<col width="49%" />
<col width="29%" />
<col width="22%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head"><strong>Ereiterungen</strong></th>
<th class="head"><strong>Objekt-Typ</strong></th>
<th class="head"><strong>Äquivalentes Zope-Objekt</strong></th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal">.pt, .zpt, .html, .htm</tt></td>
<td>Filesystem Page Template</td>
<td>Page Template</td>
</tr>
<tr><td><tt class="docutils literal">.cpt</tt></td>
<td>Controller Filesystem Page Template</td>
<td>Controller Page Template</td>
</tr>
<tr><td><tt class="docutils literal">.py</tt></td>
<td>Filesystem Script (Python)</td>
<td>Script (Python)</td>
</tr>
<tr><td><tt class="docutils literal">.cpy</tt></td>
<td>Controller Python Script</td>
<td>Controller Python Script</td>
</tr>
<tr><td><tt class="docutils literal">.vpy</tt></td>
<td>Controller Validator</td>
<td>Controller Validator</td>
</tr>
<tr><td><tt class="docutils literal">.doc, .pdf, .swf, .jar, .cab, .ico, .js, .css</tt></td>
<td>Filesystem File</td>
<td>File</td>
</tr>
<tr><td><tt class="docutils literal">.gif, .jpg, .jpeg, .png</tt></td>
<td>Filesystem Image</td>
<td>Image</td>
</tr>
<tr><td><tt class="docutils literal">.props</tt></td>
<td>Filesystem Properties Object</td>
<td>Folder with Properties</td>
</tr>
<tr><td><tt class="docutils literal">.zsql</tt></td>
<td>Filesystem Z SQL Method</td>
<td>ZSQL Method</td>
</tr>
<tr><td><tt class="docutils literal">.dtml</tt></td>
<td>Filesystem DTML Method</td>
<td>DTML Method</td>
</tr>
</tbody>
</table>
<p>Um also ein Bild in Ihrer Verzeichnisansicht zu erhalten, stellen Sie eine <tt class="docutils literal">.gif</tt>- oder <tt class="docutils literal">.jpeg</tt>-Datei hinein. Wenn Sie ein Script(Python)-Objekt wollen, fügen Sie eine Datei mit der Endung <tt class="docutils literal">.py</tt> hinzu.</p>
</div>
<div class="section" id="metadaten-von-dateisystemobjekten-setzen">
<h4>Metadaten von Dateisystemobjekten setzen</h4>
<p>Zusätzliche Informationen zu einem Objekt wie Titel, Sicherheit oder Cache werden in einer separaten Datei gespeichert. Diese Datei hat den gleichen Namen wie die Originaldatei, hat aber am Ende die Erweiterung  <tt class="docutils literal">.metadata</tt>. Falls die Originaldatei z.B. <tt class="docutils literal">logo.jpg</tt> lautet, so liegen ihre Metadaten in der Datei <tt class="docutils literal">logo.jpg.metadata</tt>.</p>
<p>Die Metadaten-Datei verwendet das Windows-Format der <tt class="docutils literal">.ini</tt>-Dateien mit <tt class="docutils literal">key = value</tt>-Paaren darin. Dieses Format wurde um Angaben zu Formularen für das <tt class="docutils literal">Form Controller</tt>-Objekt erweitert, was Sie im nächsten Abschnitt sehen werden. Alle Angaben und sogar das Vorhandensein dieser Datei selbst sind optional. Es folgt eine Beispieldatei:</p>
<pre class="literal-block">
[default]
title = Testobjekt
cache = RAMCache
proxy = Manager

[security]
Access contents information = 1:Manager,Anonymous
</pre>
<p>In dieser Datei können Sie folgende Werte setzen:</p>
<ul class="simple">
<li><strong>title</strong>: Dies ist der Titel, der dem Objekt im ZMI und in Plone zugewiesen wird. Er erscheint auch in den Plone-Templates.</li>
<li><strong>cache</strong>: Dies ist die ID des Cache-Objekts, in dem Sie das Objekt cachen möchten. Plone enthält standardmäßig zwei Cache-Objekte: einen RAM-Cache-Manager und einen HTTP-Cache-Manager. Kapitel 14 beschreibt die Funktion dieser zwei Objekte.</li>
<li><strong>proxy</strong>: Dies ist die Proxy-Rolle, die Sie auf dieses Objekt anwenden möchten. In Kapitel 9 finden Sie weitere Informationen dazu.</li>
<li><strong>security</strong>: Dies ist der Sicherheitsbereich, in dem mehrere Zeilen mit Sicherheitseinstellungen stehen können. Der Schlüssel enthält den Namen der Berechtigung, und die rechte Seite enthält die Akquisitionseinstellungen, gefolgt von den durch Kommata getrennten Rollen. Beispielsweise bedeutet <tt class="docutils literal">View = 0:Manager</tt>, dass nur Benutzer mit den Rollen Mitglied und Manager ein Objekt sehen können und dass Sicherheitseinstellungen für diese Berechtigung nicht akquiriert werden.</li>
</ul>
</div>
<div class="section" id="validierer-im-dateisystem-verwenden">
<h4>Validierer im Dateisystem verwenden</h4>
<p>Um einen Validierer im Dateisystem anzugeben, fügen Sie den Validierer in der <tt class="docutils literal">.metadata</tt>-Datei hinzu. Der Validierer-Abschnitt der <tt class="docutils literal">.metadata</tt>-Datei sähe wie folgt aus:</p>
<pre class="literal-block">
[validators]
validators = validate_script1, validate_script2
</pre>
<p>Damit werden die zwei Validierungsscripts <tt class="docutils literal">validate_script1</tt> und <tt class="docutils literal">validate_script2</tt> ausgeführt, und zwar in dieser Reihenfolge. Ein Validierungsscript wird die Daten untersuchen und Fehler zum Formular-Controller-Status hinzufügen, falls es Probleme gibt.</p>
<p>Die Optionen <tt class="docutils literal">contextType</tt> und <tt class="docutils literal">button</tt> benötigen eine leicht andere Syntax. Validierungen werden im gerade ausgeführten Kontext, z.B. einem Dokument oder Bild, ausgeführt. Sie könnten jeweils verschiedene Validierer für ein Dokument und für ein Bild ausführen. Um z.B. ein anderes Validierungsscript auszuführen, wenn es als Dokument ausgeführt wird, fügen Sie folgende Zeile hinzu:</p>
<pre class="literal-block">
validators.Document = validate_script2
</pre>
<p>Den Validierer können Sie abhängig von dem angeklickten Formular-Button variieren, indem Sie den Namen des Buttons im Formular links vom Validierer hinzufügen. Der Button-Name muss mit <tt class="docutils literal">form.button</tt> beginnen. Beispiel:</p>
<pre class="literal-block">
&lt;input type=&quot;submit&quot; name=&quot;form.button.button1&quot; value=&quot;First&quot; /&gt;
</pre>
<p>Die Metadaten-Datei sähe dann wie folgt aus:</p>
<pre class="literal-block">
validators..button1 = validate_script1
</pre>
<p>Das <tt class="docutils literal">..</tt> steht für ein Leerzeichen beim Kontexttyp. Wenn Sie also wie zuvor möchten, dass dieses Leerzeichen bei <tt class="docutils literal">button1</tt> in einem Dokument erscheint, dann sähe die Metadaten-Datei wie folgt aus:</p>
<pre class="literal-block">
validators.Document.button1 = validate_script5
</pre>
</div>
<div class="section" id="aktionen-im-dateisystem-verwenden">
<h4>Aktionen im Dateisystem verwenden</h4>
<p>Aktionen können Sie, wie Validierer auch, in der <tt class="docutils literal">.metadata</tt>-Datei angeben. Die Syntax für den Abschnitt <tt class="docutils literal">actions</tt> Ihrer Datei sähe wie folgt aus:</p>
<pre class="literal-block">
[actions]
action.success = traverse_to:string:script1
</pre>
<p>Wenn im vorherigen Beispiel das Formular abgeschickt wird und die Validierungsscripts einen Erfolg zurückgeben, wird die Aktion <tt class="docutils literal">traverse to</tt> mit dem Argument <tt class="docutils literal">string:script1</tt> aufgerufen. Dieses Argument ist eigentlich ein Ausdruck. Die Standardaktion bei einem Fehlschlag besteht darin, das aktuelle Formular neu zu laden. Das Formular hat Zugriff auf alle Fehlermeldungen über das <tt class="docutils literal">state</tt>-Objekt in seinen Optionen.</p>
<p>Noch einmal: Sie können eine bestimmte Aktion in einem bestimmten Kontext angeben. Um z.B. eine Aktion bei Erfolg auf einem Dokument anzugeben, können Sie Folgendes tun:</p>
<pre class="literal-block">
action.success.Documnent = traverse_to:string:document_script
</pre>
<p>Wieder können Sie die Aktion für den folgenden Button angeben:</p>
<pre class="literal-block">
&lt;input type=&quot;submit&quot; name=&quot;form.button.button1&quot; value=&quot;Button&quot; /&gt;
</pre>
<p>Dazu fügen Sie Folgendes zur <tt class="docutils literal">.metadata</tt>-Datei hinzu:</p>
<pre class="literal-block">
action.success..button1 = traverse_to:string:script1
</pre>
<p>Dieses Beispiel enthält keinen expliziten Kontext, d.h., es ist für alle Kontexttypen gültig.</p>
<div class="sidebar">
<p class="first sidebar-title">Beispiele für Dateisystem-Skins und die Buchbeispiele</p>
<!-- All the examples in this book have been collected in a skin for you to install. You can find this on the Plone book Web site at http://plone-book.agmweb.ca/Software/PloneBookExamples and on the Apress book Web site at http://www.apress.com. It's available as a *.zip* file of the skin; after you've downloaded and unzipped it, you'll find there's a file structure similar to the one mentioned earlier. -->
<p>Alle Beispiele in diesem Buch wurden in einer Skin gesammelt, die Sie installieren können. Sie finden sie auf der Website zum Plone-Buch unter <a class="reference external" href="http://plone-book.agmweb.ca/Software/PloneBookExamples">http://plone-book.agmweb.ca/Software/PloneBookExamples</a> sowie auf der Buch-Website von Apress unter <a class="reference external" href="http://www.apress.com">http://www.apress.com</a>. Die Skin ist verfügbar, nachdem Sie die <tt class="docutils literal">.zip</tt>-Datei heruntergeladen und ausgepackt haben. Sie werden eine Dateistruktur ähnlich zu der zuvor erwähnten finden.</p>
<!-- You have an *__init__.py* file and *skins* directory. In the *skins* directory you'll find a series of page templates, Controller Validator objects, and all the matching metadata files. If you want to install this, then copy the *PloneBookExamples* folder into the *Products* directory of your instance home. Restart Plone, and then click *plone setup*. Select Add/Remove Products,* *and* *you'll see an entry for *PloneBookExamples*; check it, and then click install. You've now installed the templates and can go to *feedbackForm* and get the page template you saw in the previous chapter. -->
<p>Sie werden eine Datei namens <tt class="docutils literal">__init__.py</tt> und ein Verzeichnis namens <tt class="docutils literal">skins</tt> vorfinden. In diesem Verzeichnis befinden sich eine Reihe von Page Templates, <tt class="docutils literal">Controller Validator</tt>-Objekten und alle entsprechenden Metadaten-Dateien. Wenn Sie das installieren möchten, kopieren Sie den Ordner <tt class="docutils literal">PloneBookExamples</tt> in das Verzeichnis <tt class="docutils literal">Products</tt> der Wurzel Ihrer Instanz. Starten Sie Plone neu, und klicken Sie dann auf <em>Plone Konfiguration</em>. Wählen Sie <em>Produkte hinzufügen/löschen</em>, und Sie werden den Eintrag <em>PloneBookExamples</em> sehen. Wählen Sie ihn, und klicken Sie dann auf <em>Installieren</em>. Nun haben Sie die Templates installiert und können zur <tt class="docutils literal">feedbackForm</tt> gehen. Sie erhalten das Page Template, das Sie im vorigen Kapitel gesehen haben.</p>
<!-- What the install procedure did was automate the process of adding an FSDV and then added a layer to each skin. If you click *portal_skins* and then select the Properties tab, you'll see that the new layer *plone_book_examples* has been added. -->
<p class="last">Die Installationsprozedur hat den Vorgang automatisiert, wobei erst ein FSDV und dann für jede Skin eine Ebene hinzufügt wird. Wenn Sie <em>portal_skins</em> anklicken und dann den Properties-Reiter wählen, werden Sie sehen, dass die neue Ebene <tt class="docutils literal">plone_book_examples</tt> hinzugefügt wurde.</p>
</div>
</div>
</div>
<div class="section" id="fallstudie-die-nasa-skin">
<h3>Fallstudie: Die NASA-Skin</h3>
<p>Im Januar 2004 landeten zwei NASA-Sonden auf dem Mars: <em>Spirit</em> und <em>Opportunity</em>. Diese ferngesteuerten Roboter klapperten die Mars-Oberfläche ab und sendeten Bilder und Oberflächenanalysen zurück. Die Sonden waren ein großer Erfolg und brachten erstaunliche Bilder von der Mars-Oberfläche, die die ganze Welt begeisterten.</p>
<p>Ein kleiner Teil dieses Ereignisses war eine Website mit der Adresse <a class="reference external" href="http://mars.telascience.org">http://mars.telascience.org</a>. Sie machte ein Programm namens  <em>Maestro</em> publik, dessen Zweck folgendes Zitat klarmacht:</p>
<blockquote>
<em>Sie können eine reduzierte Version des Programms herunterladen, mit dem NASA-Wissenschaftler die Sonden Spirit und Opportunity steuern. Es sind auch Updates für Maestro verfügbar, die echte Daten vom Mars enthalten, die Sie zu Ihrer Maestro-Kopie hinzufügen können.</em></blockquote>
<p>Das hat insofern mit Plone zu tun, weil die für die Site verantwortliche Gruppe sehr schnell und leicht eine Site entwickelt hat, die toll aussieht. In diesem Fall hat eine große Zahl von Community-Mitgliedern und Freiwilligen den Maestro-Teammitgliedern bei der Entwicklung der Site geholfen. Abbildung 7.14 zeigt die Plone-Site in Betrieb.</p>
<a class="reference external image-reference" href="img/07-14.png/image_view_fullscreen"><img alt="img/07-14.png" class="original" src="img/07-14.png" /></a>
<p>Abbildung 7.14. Die Maestro-Site</p>
<p>Vermutlich werden Sie einige Hinweise auf eine Plone-Site erkennen: die Reiter im oberen Bereich, die persönliche Leiste in der oberen rechten Ecke und die übliche Pfadnavigation. Abgesehen davon, sieht die Site recht anders als eine Standard-Plone-Site aus. In den folgenden Abschnitten werde ich die Schritte durchgehen, mit denen das erreicht wurde. Nun, eigentlich ist es sehr einfach, weil das meiste vom Look-and-Feel mit CSS realisiert wurde. Es gab wenige bis gar keine Änderungen, außer am <tt class="docutils literal">custom</tt>-Stylesheet und einige neue Bilder.</p>
<p>Zuerst betrachten wir die Änderungen außerhalb von CSS an der Site, nämlich an einigen Templates und Eigenschaften.</p>
<div class="section" id="portlets-und-einige-hauptelemente-entfernen">
<h4>Portlets und einige Hauptelemente entfernen</h4>
<p>Die Site verwendet keine Portlets. Sie wurden entfernt, weil es für diese Site keine relevanten Portlets gibt. Nachrichten erscheinen stattdessen auf der Homepage. Um die Portlets von Ihrer Site zu entfernen, gehen Sie zur Wurzel der Plone-Site und klicken auf den <em>Properties</em>-Reiter. Löschen Sie alle Werte in den Formularfeldern neben <tt class="docutils literal">left_slots</tt> und <tt class="docutils literal">right_slots</tt>.</p>
<p>Auf der Maestro-Site wurden ein paar Elemente entfernt. Manchmal habe ich festgestellt, dass es das Beste ist, was man mit Merkmalen tun kann, die auf einer Site einfach nicht benötigt werden. Es kann schwierig sein, jedes Element einer Benutzerschnittstelle in eine Plone-Site zu zwängen, aber das müssen Sie gar nicht immer tun. Entfernen Sie stattdessen einfach die unnötigen Elemente. In diesem Fall wurden auch einige Elemente entfernt: die Site-Aktionen, der <em>Suchen</em>-Kasten, die Fußzeile sowie das Kolophon.</p>
<p>Um das zu bewerkstelligen, wurden die Templates, die den Code produzieren, angepasst und so verändert, dass sie nichts darstellen. Um z.B. den <em>Suchen</em>-Kasten zu entfernen, klicken Sie im ZMI nacheinander auf <em>portal_skins</em>, <em>plone_templates</em> und <em>global_searchbox</em>. Klicken Sie dann auf den <em>Customize</em>-Button, und ändern Sie das Template wie folgt:</p>
<pre class="literal-block">
&lt;html
    xmlns=&quot;http://www.w3.org/1999/xhtml&quot;
    xml:lang=&quot;en&quot; lang=&quot;en&quot;
    i18n:domain=&quot;plone&quot;&gt;
    &lt;body&gt;

        &lt;div id=&quot;portal-searchbox&quot;
               metal:define-macro=&quot;quick_search&quot;
               tal:condition=&quot;nothing&quot;&gt;
            Nothing to see here.
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Diese Technik beim Entfernen von Elementen habe ich weiter oben gezeigt. Setzen Sie einfach <tt class="docutils literal">tal:condition</tt> im Makro-Element, um sicherzustellen, dass die Bedingung <tt class="docutils literal">false</tt> ist.</p>
</div>
<div class="section" id="farben-anpassen">
<h4>Farben anpassen</h4>
<p>Im Objekt <tt class="docutils literal">base_properties</tt> haben Sie die Grundfarben der Site gesetzt. Dieses Objekt wurde angepasst, und die Farben wurden wie folgt geändert (alle weiteren Elemente sind unverändert, sofern nichts anderes vermerkt ist):</p>
<pre class="literal-block">
linkColor: #776a44
globalBorderColor: #776a44
globalBackgroundColor: #e0d3ad
globalFontColor: #776a44
</pre>
<p>Die Farbänderung, die ich am stärksten bemerkt habe, ist die von  <tt class="docutils literal">globalBackgroundColor</tt>, was die Farbe der persönlichen Leiste von Blau auf Bräunlich ändert. Diese geringfügigen Farbänderungen ändern das Grund-Stylesheet, damit es besser zu den Bildern und dem allgemeinen Look-and-Feel passt.</p>
</div>
<div class="section" id="stylesheet-erstellen">
<h4>Stylesheet erstellen</h4>
<p>Der größte Teil dieser Site ist das Stylesheet, das in Anhang B vollständig wiedergegeben ist. Hier möchte ich nur einige wesentliche Teile davon hervorheben. Dieses Stylesheet basiert auf <tt class="docutils literal">ploneCustom.css</tt>, das im <tt class="docutils literal">custom</tt>-Ordner angepasst wurde. Dann wurden einige Elemente der Webseite in der neuen Datei <tt class="docutils literal">ploneCustom.css</tt> überschrieben.</p>
<p>Zuerst wird die Hintergrundfarbe für den ganzen Hauptteil auf <tt class="docutils literal">#343434</tt> gesetzt.</p>
<pre class="literal-block">
body {
    background: #343434;
}
</pre>
<p>Zweitens gilt, dass sich der eigentliche Inhalt einer Plone-Seite, der Teil, den Sie bearbeiten können, in einer Klasse namens <tt class="docutils literal">documentContent</tt> befindet. Da die Hintergrundfarbe des Elements <tt class="docutils literal">documentContent</tt> in der Hauptdatei <tt class="docutils literal">plone.css</tt> auf <tt class="docutils literal">white</tt> gesetzt ist, hat der Text einen weißen Hintergrund und bildet in der Mitte des Bildschirms einen weißen Bereich.</p>
<p>Anschließend wird das Bild vom Satelliten und Roboter als großes Bild mittels CSS oben auf der Website platziert. Der Code dafür ist folgender:</p>
<pre class="literal-block">
#portal-top {
   background: url(&quot;http://mars.telascience.org/header.jpg&quot;) transparent no-repeat;
   padding: 162px 0 0 0;
   position: relative;
}
</pre>
<p>Dieser CSS-Code setzt die Parameter für das Element mit der ID <tt class="docutils literal"><span class="pre">portal-top</span></tt>. Wenn Sie sich den HTML-Code einer Plone-Site anschauen, werden Sie oben auf der Seite direkt unter dem <tt class="docutils literal">body</tt>-Element das Element <tt class="docutils literal"><span class="pre">portal-top</span></tt> sehen. Dadurch, dass der Hintergrund für dieses Bild auf den URL des entsprechenden Bildes gesetzt wird, erscheint dieses Bild. Es ist 162 Pixel hoch, weswegen im <tt class="docutils literal">padding</tt>-Wert der obere Wert des <tt class="docutils literal"><span class="pre">#portal-top</span></tt>-Elements auf <tt class="docutils literal">162px</tt> gesetzt ist. Ohne diese Einstellung werden alle Bilder darunter nach oben geschoben und überschreiben das Bild.</p>
<p>Das Titelbild ist 677 Pixel breit, und Sie werden bemerken, dass der Text auf der Seite sauber unter das Bild passt und nicht links oder rechts darüber hinaussteht. Das erreichen Sie dadurch, dass Sie den Wert des Elements auf <tt class="docutils literal">680px</tt> setzen. Das HTML-Element <tt class="docutils literal"><span class="pre">visual-portal-wrapper</span></tt> ist direkt unter dem Hauptteil und setzt die Breite für die ganze Seite. Der Code dafür lautet wie folgt:</p>
<pre class="literal-block">
#visual-portal-wrapper {
   width: 680px;
   margin: 1em auto 0 auto;
}
</pre>
<p>Dies setzt die Breite aller Seiten auf einen festen Wert, was in Ordnung ist, solange Sie sicherstellen, dass die Breite kleiner als die Standardbreite von 800 Pixel ist. Egal wie groß der Benutzer das Browserfenster macht, der Hauptteil der Seite wird nie breiter als 680 Pixel, was garantiert, dass er passend zum Bild bleibt.</p>
<p>Die anderen offensichtlichen Änderungen sind die Reiter oben auf der Seite, die nun aus Bildern bestehen statt aus den Standardkästen in Plone. Die Reiter werden aus drei Bildern zusammengesetzt: einem Abstandshalter zwischen den Reitern sowie dem linken und dem rechten Teil eines Reiters. Die Kombination dieser drei Bilder ergibt den Effekt eines Reiters. Abbildung 7.15 zeigt diese drei Bilder.</p>
<a class="reference external image-reference" href="img/07-15.png/image_view_fullscreen"><img alt="img/07-15.png" class="original" src="img/07-15.png" /></a>
<p>Abbildung 7.15. Kombination dreier Bilder für den Reiter</p>
<p>Denken Sie beim Bearbeiten des CSS daran, dass jeder Reiter nichts weiter als ein Listeneintrag mit einem Link in einem Element mit der ID <tt class="docutils literal"><span class="pre">portal-globalnav</span></tt> ist. Um den Hintergrund-Abstandshalter zwischen den Reitern zu setzen, setzt die Skin zuerst den Hintergrund für das gesamte Element. Auch hier gilt, dass Sie durch das Setzen der Höhe auf 21 Pixel, d.h. auf die gleiche Größe wie beim Bild, garantieren, dass es ausreichend Platz für das Bild gibt. Der Code dafür ist folgender:</p>
<pre class="literal-block">
#portal-globalnav {
    background: url(&quot;http://mars.telascience.org/listspacer.gif&quot;) transparent;
    padding: 0;
    height: 21px;
    border: 0;
    margin: 0 0 1px 6px;
    clear: both;
}
</pre>
<p>Das Startbild verwenden Sie, um das Bild am linken Rand des Reiters zu setzen. Dieses setzen Sie, indem Sie den Wert des <tt class="docutils literal">li</tt>-Elements statt des <tt class="docutils literal">anchor</tt>-Elements wie folgt setzen:</p>
<pre class="literal-block">
#portal-globalnav li {
   display: block;
   float: left;
   height: 21px;
   background: url(&quot;/liststart.gif&quot;) transparent no-repeat;
   padding: 0 0 0 33px;
   margin: 0 0.5em 0 0;
}
</pre>
<p>Und schließlich setzen Sie den rechten Teil des Reiters, indem Sie ein Bild zum Ankerelement hinzufügen. Dazu ändern Sie das Ankerelement innerhalb des Reiters. Der folgende Code zeigt, wo Sie das Hintergrundbild als rechten Teil gesetzt haben:</p>
<pre class="literal-block">
#portal-globalnav li a {
    display: block;
    float: left;
    height: 21px;
    background: url(&quot;/listitem.gif&quot;) transparent right top;
    padding: 0 33px 0 0;
    border: 0;
    line-height: 2em;
    color: black;
    font-size: 90%;
    margin: 0;
}
</pre>
<p>Nun haben Sie die ziemlich standardmäßig aussehenden Plone-Reiter durch tolle Buttons ersetzt.</p>
</div>
<div class="section" id="eine-splash-seite-erstellen">
<h4>Eine Splash-Seite erstellen</h4>
<p>Diese Site verfügt über ein weiteres Schlüsselmerkmal. Die Eingangsseite der Site ist eine <em>Splash-Seite</em>, die eine hübsche Grafik anzeigt und den Benutzer dazu einlädt, die Site zu betreten. Eine solche Seite können Sie hinzufügen, indem Sie ins ZMI gehen und das Objekt <tt class="docutils literal">index_html</tt> entfernen, das normalerweise vorhanden ist. Erstellen Sie dann eine neue Datei namens <tt class="docutils literal">index_html</tt>. Darin schreiben Sie einen eigenen Code, um die Homepage zu erzeugen, inklusive eines eigenen CSS. Das Hauptelement davon ist ein Bild, das mit folgendem CSS-Code dort platziert wird:</p>
<pre class="literal-block">
div {
    background: url(/splash.jpg) transparent no-repeat;
    width: 260px;
    height: 335px;
    position: absolute;
    ...
}
</pre>
<p>Das restliche CSS platziert den Text und die Links innerhalb des Bildes. Diese Seite enthält keinerlei Plone-Elemente, sondern ist statisches HTML.</p>
</div>
<div class="section" id="schlussfolgerung">
<h4>Schlussfolgerung</h4>
<p>Das sieht nach einer einigermaßen komplexen Site aus, wobei der relativ einfache CSS-Code die meiste Arbeit erledigt. Durch die Verwendung von CSS und mit einigen HTML-Kenntnissen haben Sie das Look-and-Feel von Plone verändert, ohne viel über Plone wissen zu müssen. Durch die Platzierung der Bilder mittels CSS haben Sie außerdem wesentliche Eigenschaften beibehalten, die für die Zugänglichkeit der Site sorgen.</p>
<p>Ein großes Dankeschön geht an die NASA und alle beteiligten Leute aus der Plone-Community für ihre Hilfe bei dieser Site und Fallstudie. Dazu gehören besonders, aber nicht ausschließlich, John Graham, Alma Ong, Joe Geldart, Michael Zeltner und Tom Croucher.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch8.rst">
    <title>8. Workflows verwalten</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch8.rst</link>
    <description>Eine der vielen Stärken von Plone liegt in seiner Workflow-Komponente. Workflows sind ein zentrales Thema beim *Content-Management*, in dem es um die Trennung von Anwendungslogik, Inhalt und Präsentation geht. Deswegen wird in diesem Kapitel der Workflow in Plone detailliert behandelt.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Workflows verwalten</h2>
<p>Eine der vielen Stärken von Plone liegt in seiner Workflow-Komponente. Workflows sind ein zentrales Thema beim <em>Content-Management</em>, in dem es um die Trennung von Anwendungslogik, Inhalt und Präsentation geht. Deswegen wird in diesem Kapitel der Workflow in Plone detailliert behandelt.</p>
<p>Ich beginne mit einigen Schlüsseldefinitionen im Zusammenhang mit Workflows sowie mit den wichtigsten dabei beteiligten Werkzeugen zum Erstellen von Workflows. Nachdem diese Konzepte klar sind, werde ich dann beschreiben, wie Sie Ihre eigenen Workflows hinzufügen und bearbeiten können.</p>
<p>Über das gesamte Kapitel hinweg werde ich Ihnen einfache Änderungen zeigen, die Sie an jenem Workflow vornehmen können, der in Plone von vornherein enthalten ist. Außerdem werde ich auch eine Reihe von Beispielen zeigen, um Ihnen bei Aufgaben wie der Erstellung von Benachrichtigungen, beim Verschieben von Inhalten usw. zu helfen. Und schließlich werde ich einige fortgeschrittenere Eigenschaften bei der Entwicklung von Workflows sowie einige nützliche Werkzeuge dabei vorstellen.</p>
<div class="section" id="was-ist-ein-workflow">
<h3>Was ist ein Workflow?</h3>
<p>Unter einem <em>Workflow</em> versteht man eine Kette von Aktionen oder Ereignissen, die sich abspielen, um ein bestimmtes Ziel zu erreichen. Oftmals wird eine Geschäftslogik mit einem Workflow ausgedrückt. Alle Geschäftsbereiche haben ihre eigenen Regeln und Vorschriften zu Vorgängen, die sich in einer Firma so ereignen. Hier sind einige Beispiele dafür:</p>
<ul class="simple">
<li>Bevor die Arbeitsstundenaufstellung eines Mitarbeiters genehmigt wird, muss sie von einem Vorgesetzten angeschaut und bestätigt werden.</li>
<li>In einer Fabrik, die irgendwelche Teile herstellt, müssen die Benutzer für jedes zusammengesetzte Teil über die Bearbeitungsreihenfolge und Zustandsänderungen auf dem Weg durch die Fabrik benachrichtigt werden.</li>
<li>Bevor eine Seite auf einer Website veröffentlicht wird, muss sie von der Marketingabteilung und vom Webmaster genehmigt und von einem Linguisten übersetzt werden.</li>
</ul>
<p>Der Workflow kapselt die Geschäftslogik dieser Regeln und standardisiert die Art und Weise, über diese Vorgänge nachzudenken. Durch diese isolierte Geschäftslogik können Firmen eine Anwendung nun leicht gemäß ihrer Tätigkeit und den entsprechenden Prozessen abwandeln. Normale Anwendungen erzwingen oftmals einen Workflow in einem Geschäftsbereich, weil der Workflow darin fest kodiert ist.</p>
</div>
<div class="section" id="workflow-in-plone">
<h3>Workflow in Plone</h3>
<p>Das Workflow-Werkzeug von Plone bietet gewisse Eigenschaften und Beschränkungen, die man kennen muss, um Workflows in Plone zu verstehen. Das in Plone benutzte Workflow-Produkt ist  DCWorkflow, ein Open Source-Produkt der Zope Corporation. Es sind auch weitere Workflow-Systeme verfügbar, und einige davon werden in Plone eingesetzt, z.B.  CMFOpenFlow, ein aktivitätsbasierender Workflow  (<a class="reference external" href="http://www.reflab.it/community/Openprojects/CMFOpenflow">http://www.reflab.it/community/Openprojects/CMFOpenflow</a>). Im Moment ist DCWorkflow jedoch mächtig und einfach genug, um alle von den meisten Benutzern gewünschten Eigenschaften zu bieten.</p>
<p>DCWorkflow geht davon aus, dass im System ein Objekt existiert, das als Ziel des Workflows fungiert, z.B. ein gewisser Inhalt oder ein Widget. Weiterhin wird angenommen, dass alle Objekte desselben Typs vom selben Workflow erfasst werden. Durch das Umwidmen (engl. Repurposing) von Inhalten (Details dazu folgen in Kapitel 11) können Sie ähnliche Inhalte mit verschiedenen Workflows bearbeiten.</p>
<p>Da DCWorkflow in Plone enthalten ist, gibt es nichts zusätzlich zu installieren. Im Zope Management Interface (ZMI) wird es vom Objekt <tt class="docutils literal">portal_workflow</tt> dargestellt.</p>
<div class="section" id="konzipieren-eines-workflows">
<h4>Konzipieren eines Workflows</h4>
<p>Bevor ich einen Workflow erkläre, werde ich erst ein wenig die Terminologie dazu erklären: die Begriffe <em>Zustände</em> und <em>Übergänge</em>.</p>
<p>Ein <em>Zustand</em> (<em>state</em>) ist eine Information über ein Inhaltselement zu einem bestimmten Zeitpunkt. Beispielzustände sind <em>Privat</em>, <em>Veröffentlicht</em>, <em>Offen</em> und <em>Entwurf</em>. Alle Workflows verfügen über einen Ausgangszustand, in dem alle Inhalte starten. Der Workflow verschiebt den Inhalt dann durch eine Reihe von Zuständen, und zwar entweder durch eine Interaktion mit dem Benutzer oder durch einen automatisierten Prozess. Wenn der Inhalt einen Endzustand erreicht hat, bleibt er für lange Zeit in diesem Zustand (normalerweise für immer). Während der Workflow aktiv ist, kann ein Inhalt auch einen von mehreren verschiedenen Endzuständen erreichen.</p>
<p>Damit dieser Inhalt von einem Zustand zum anderen gelangt, muss ein <em>Übergang</em> (engl. <em>Transition</em>) dazwischen vorhanden sein. Ein Übergang verbindet einen Start- mit einem Endzustand. Mit einem Übergang können viele verschiedene Eigenschaften verbunden sein, wie Sie später sehen werden, aber im Moment müssen Sie nur wissen, dass ein Übergang einen Inhalt zwischen zwei Zuständen bewegt. Normalerweise wird ein Übergang durch eine äußere Einwirkung ausgelöst, z.B. von einem Benutzer, der auf einer Webseite einen Button anklickt, oder von einem Skript, das von einer Seite aus aufgerufen wird.</p>
<p>Die Visualisierung eines Workflows kann etwas verwirrend sein, besonders dann, wenn man über etwas so Vages wie einen Inhalt spricht. Daher ist es hilfreich, an ein alltägliches Beispiel zu denken. In diesem Fall zeigt das folgende Beispiel den Workflow meiner Kreditkartenrechnung, die mich einmal im Monat beglückt:</p>
<ol class="arabic simple">
<li>Die Kreditkartenfirma erstellt eine Rechnung und sendet sie per Post an mich.</li>
<li>Ich erhalte die Rechnung und lege sie auf meinen Schreibtisch. Manchmal liegt die Rechnung dort eine ganze Weile, während ich bis zum Monatsende warte. Gelegentlich muss ich bei Leuten wegen bestimmter Ausgaben nachfragen, z.B. &quot;Was für Kleider hast du da gekauft?&quot;</li>
<li>Alle ungeklärten Probleme gehen an die Kreditkartenfirma weiter, die möglicherweise eine neue Rechnung ausstellt (was allerdings sehr selten passiert).</li>
<li>Am Monatsende, wenn ich normalerweise die ganze Buchhaltung mache, bezahle ich dann die Rechnung.</li>
</ol>
<p>Aus dieser Beschreibung können Sie nun ein paar Zustände ableiten. Wenn Sie sich die obigen Schritte anschauen, stellen Sie fest, dass Sie wirklich keinen Bedarf für verschiedene Zustände beim Erhalt der Rechnung haben, wozu auch das Öffnen und Ablegen auf meinen Schreibtisch gehört. Ebenso wenig müssen Sie sich mit allen Überprüfungen herumschlagen, die vorgenommen werden. Das sind zwar alles wichtige Schritte, die ausgeführt werden, aber einen Workflow für jeden einzelnen Schritt zu erstellen wäre bei weitem zu umständlich. Sie können stattdessen den Workflow mit den folgenden Zuständen zusammenfassen:</p>
<ul class="simple">
<li><strong>Entwurf</strong>: Die Kreditkartenrechnung wurde erstellt und mir zugeschickt.</li>
<li><strong>Überprüfung</strong>: Die Kreditkartenrechnung wurde empfangen, liegt auf meinem Schreibtisch und wird geprüft.</li>
<li><strong>Bezahlt</strong>: Die Kreditkartenrechnung wurde bezahlt, ins Regal abgelegt und für immer vergessen.</li>
</ul>
<p>Nun haben Sie die Zustände gefunden und können sich Gedanken zu den notwendigen Übergängen machen. Zu jedem Zustand gibt es mindestens einen Übergang, der die Rechnung von einem Zustand in einen anderen bewegt:</p>
<ul class="simple">
<li><strong>Verschicken</strong>: Die Bank verschickt die Kreditkartenrechnung.</li>
<li><strong>Bezahlen</strong>: Ich bezahle die Kreditkartenrechnung.</li>
<li><strong>Ablehnen</strong>: Etwas auf der Rechnung stimmt nicht, und sie wird nicht genehmigt.</li>
</ul>
<p>Abbildung 8.1 zeigt diesen Satz von Zuständen und Übergängen. In dieser Abbildung werden Zustände mit Namen von Kästen dargestellt, und Zustandsübergänge werden von Pfeilen mit kursiv geschriebenen Namen dargestllt.</p>
<a class="reference external image-reference" href="img/08-01.png/image_view_fullscreen"><img alt="img/08-01.png" class="original" src="img/08-01.png" /></a>
<p>Abbildung 8.1. Einfacher Zustandsautomat bei der Bezahlung von Kreditkartenrechnungen</p>
<p>Jetzt haben Sie den Geschäftsvorgang der Bezahlung einer Kreditkartenrechnung in einem Workflow extrahiert. Der nächste Schritt besteht im Nachdenken über Rollen und Sicherheit für diese Kreditkartenrechnung. Dieser Workflow enthält nun die Geschäftslogik einer Anwendung zur Bearbeitung von Kreditkartenrechnungen.</p>
</div>
<div class="section" id="rollen-und-sicherheit-in-workflows">
<h4>Rollen und Sicherheit in Workflows</h4>
<p>In jedem komplizierten System haben Sie Benutzer mit allen möglichen Rollen und Gruppen. Diese Rollen verleihen Plone eine große Flexibilität bei der Sicherheit, können es aber auch komplizierter machen. Kapitel 9 behandelt lokale Rollen und Gruppen, aber dieser Abschnitt behandelt einige wichtige Punkte dazu, wie diese Themen mit dem Workflow zusammenhängen.</p>
<p>Wenn ein Inhaltselement von einem Workflow-Zustand in einen anderen übergeht, kann der Workflow-Prozess die Sicherheitseinstellungen auf diesem Inhalt ändern. Diese Sicherheitseinstellungen bestimmen, welcher Benutzer welche Aktion auf welchem Inhalt ausführen kann. Durch die Manipulation der Sicherheitseinstellungen über den Workflow kann man die Sicherheit eines Inhaltselements über seinen Lebenszyklus hinweg ändern. Benutzer von statischen Systemen oder von Zope sind oftmals verwirrt, weil in Zope alle Inhaltselemente über ihren gesamten Lebenszyklus die gleichen Sicherheitseinstellungen haben.</p>
<p>Was das Kreditkartenbeispiel angeht, können Sie die Sicherheitseinstellungen für die Kreditkartenrechnung ableiten. Eine Möglichkeit, diese darzustellen, besteht darin, eine Tabelle anzulegen, die die Sicherheit für die Übergänge zwischen verschiedenen Zuständen allgemein erweitert, wie in Tabelle 8.1 zu sehen ist.</p>
<p>Tabelle 8.1. Übergänge und ihre erzeugenden Zustände</p>
<table border="1" class="docutils">
<colgroup>
<col width="33%" />
<col width="33%" />
<col width="33%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Zustand</th>
<th class="head">Ich</th>
<th class="head">Bank</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Entwurf</td>
<td>&nbsp;</td>
<td>Abschicken</td>
</tr>
<tr><td>Überprüfung</td>
<td>Bezahlen</td>
<td>Ablehnen</td>
</tr>
<tr><td>Bezahlt</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
<p>In diesem Stadium sehen Sie in Tabelle 8.1 die Übergänge und wer sie erzeugen kann. Sie haben aber noch nicht über den Zugriff nachgedacht, der immer dann erfolgt, wenn ein Benutzer eine Aktion auf einem Objekt ausführt. Wann kann jemand z.B. die Rechnung bearbeiten, und wann kann sie angezeigt werden? Plone verwendet hierfür den Begriff <em>Aktion</em>, wie in Tabelle 8.2 zu sehen ist. Hoffentlich habe nur ich Zugriff auf meine eigenen Kreditkartenauszüge! Ebenso kann die Bank die Kreditkartenrechnung jederzeit anzeigen und Fragen dazu beantworten.</p>
<p>Tabelle 8.2. Aktionen und ihre erzeugenden Zustände</p>
<table border="1" class="docutils">
<colgroup>
<col width="28%" />
<col width="28%" />
<col width="43%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Zustand</th>
<th class="head">Ich</th>
<th class="head">Bank</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Entwurf</td>
<td>&nbsp;</td>
<td>Anzeigen, Bearbeiten</td>
</tr>
<tr><td>Überprüfung</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
</tr>
<tr><td>Bezahlt</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
</tr>
</tbody>
</table>
<p>Tatsächlich stellt sich heraus, dass ich meine Kreditkartenrechnung nicht bearbeiten kann, nur die Bank kann das. Ich kann meine Rechnung ablehnen und zurückschicken, aber die Bank will sicher nicht, dass ich die Rechnung bearbeiten kann. In dieser Situation nehmen Sie an, dass die Bank der Besitzer der Kreditkartenrechnung ist. Hiermit wird das Konzept eines <em>Besitzers</em> (engl. <em>Owner</em>) demonstriert. Ich habe evtl. mehrere Kreditkartenrechnungen von mehreren Banken, und bei jeder können Sie sich die Bank als deren Besitzer vorstellen. Jede Bank besitzt ihre eigenen Kreditkartenrechnungen, aber nicht die anderer Banken. Tabelle 8.3 kombiniert Übergänge und Aktionen, wobei die Begriffe <em>Ich</em> und <em>Bank</em> jeweils in <em>Bezahler</em> und <em>Besitzer</em> geändert wurden.</p>
<p>Tabelle 8.3. Übergänge und Aktionen kombiniert, plus Rollen der Leute</p>
<table border="1" class="docutils">
<colgroup>
<col width="18%" />
<col width="38%" />
<col width="45%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Zustand</th>
<th class="head">Bezahler</th>
<th class="head">Besitzer</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Entwurf</td>
<td>&nbsp;</td>
<td>Abschicken, Anzeigen, Bearbeiten</td>
</tr>
<tr><td>Überprüfung</td>
<td>Bezahlen, Ablehnen, Anzeigen</td>
<td>Anzeigen</td>
</tr>
<tr><td>Bezahlt</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
</tr>
</tbody>
</table>
<p>Natürlich ist das ein sehr gestelltes Beispiel, aber es illustriert, wie Sie einen Workflow auf einfache Zustände anwenden können. Es können hier auch weitere Übergänge erfolgen, z.B. wäre ich sehr froh, wenn meine Kreditkartenrechnungen von jemand anderem bezahlt würden, aber das ist leider so unwahrscheinlich, dass Sie das nicht zum Workflow oder zur Sicherheit aufnehmen sollten.</p>
<p>Bevor ich Ihnen zeige, wie Sie Workflows erstellen und bearbeiten, zeige ich Ihnen jetzt noch die Standard-Workflows, die in Plone enthalten sind.</p>
</div>
<div class="section" id="einfuhrung-in-plone-workflows">
<h4>Einführung in Plone-Workflows</h4>
<p>Plone enthält bereits eine Reihe von Standard-Workflows für Ihre Plone-Site. Diese Workflows bieten eine logische Art und Weise, Inhalte durch eine Plone-Site zu schleusen. Eine Standard-Plone-Site enthält zwei Workflows: den Default-Workflow und den Ordner-Workflow (Folder-Workflow). In den folgenden Abschnitten werden beide vorgestellt.</p>
<div class="section" id="default-workflow">
<h5>Default-Workflow</h5>
<p>In Kapitel 3 habe ich den Default-Workflow und die Standardeinstellungen bei der Veröffentlichung von Inhalten behandelt. Dort habe ich für jeden Zustand im Workflow dessen Sicherheit und Einstellungen beschrieben. Aber da ein Bild mehr sagt als tausend Worte, sehen Sie in Abbildung 8.2 diese Workflow-Zustände.</p>
<a class="reference external image-reference" href="img/08-02.png/image_view_fullscreen"><img alt="img/08-02.png" class="original" src="img/08-02.png" /></a>
<p>Abbildung 8.2. Default-Workflow in Plone für Inhalte</p>
<p>Abbildung 8.2 zeigt die Hauptzustände und -übergänge. Die grau gepunktete Linie in dieser Abbildung stellt eine Art Trennung zwischen Sicherheitsbereichen dar. Links von dieser Linie sind es die Besitzer von Inhalten, die damit interagieren, und rechts von der Linie interagieren normalerweise die Prüfer mit den Inhalten.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Der Besitzer des Inhalts ist jene Person, die den Inhalt ursprünglich erstellt hat. Ein Besitzer ist ein bestimmtes Mitglied einer Plone-Site. Zwar existieren auf einer Plone-Site viele Mitglieder, aber nur eine Person kann Besitzer eines Inhalts auf einer Plone-Site sein. Diese Besitzerrolle ist insofern speziell, als sie sich erst dann ergibt, wenn ein Objekt angelegt wird.</p>
</div>
<p>Wie beim Kreditkartenbeispiel ist auch hier ein zugehöriger Satz an Rechten für den Default-Workflow vorhanden. Tabelle 8.4 gibt alle Rechte und Zustände an.</p>
<p>Tabelle 8.4. Rechte im Default-Workflow</p>
<table border="1" class="docutils">
<colgroup>
<col width="15%" />
<col width="18%" />
<col width="17%" />
<col width="14%" />
<col width="18%" />
<col width="17%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Zustand</th>
<th class="head">Anonym</th>
<th class="head">Authentifiziert</th>
<th class="head">Besitzer</th>
<th class="head">Manager</th>
<th class="head">Prüfer</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Offen</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
<td>Bearbeiten</td>
<td>Bearbeiten</td>
</tr>
<tr><td>Privat</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>Bearbeiten</td>
<td>Bearbeiten</td>
<td>Anzeigen</td>
</tr>
<tr><td>Veröffentlicht</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
<td>Bearbeiten</td>
<td>Anzeigen</td>
</tr>
<tr><td>Sichtbar</td>
<td>Anzeigen</td>
<td>Anzeigen</td>
<td>Bearbeiten</td>
<td>Bearbeiten</td>
<td>Anzeigen</td>
</tr>
<tr><td colspan="6">Anzeigen bezieht sich auf folgende Rechte: Access, Contents, Information und View</td>
</tr>
<tr><td colspan="6">Bearbeiten bezieht sich auf folgende Rechte: Modify, Portal und Content</td>
</tr>
</tbody>
</table>
<p>Wie Sie in Tabelle 8.4 sehen können, werden Inhalte nur dann standardmäßig vor anderen verborgen, wenn sie im Zustand <em>Privat</em> sind. Inhalte im Zustand <em>Veröffentlicht</em> kann nur der Manager bearbeiten. Im Abschnitt &quot;Rechte bearbeiten&quot;, der weiter unten folgt, werde ich Ihnen zeigen, wie Sie diese Rechte ganz einfach über das Web ändern können.</p>
</div>
<div class="section" id="ordner-workflow">
<h5>Ordner-Workflow</h5>
<p>Ebenfalls in Kapitel 3 habe ich den Ordner-Workflow beschrieben, als ich die Veröffentlichung von Inhalten behandelt habe. Aber wie ich dort bemerkt habe, gibt es für Ordner keinen offenen Zustand. Deswegen haben Sie einen etwas einfacheren Workflow, wie in Abbildung 8.3 gezeigt.</p>
<a class="reference external image-reference" href="img/08-03.png/image_view_fullscreen"><img alt="img/08-03.png" class="original" src="img/08-03.png" /></a>
<p>Abbildung 8.3. Ordner-Workflow in Plone für Inhalte</p>
</div>
<div class="section" id="weitere-workflows">
<h5>Weitere Workflows</h5>
<p>Für eine Plone-Website sind zahlreiche Workflows verfügbar, darunter ein privater Workflow, ein Community-Workflow, ein One-Step Publication-Workflow usw. ZopeZen enthält einen Workflow, und PloneCollectorNG enthält ebenfalls einen Workflow. DCWorkflow enthält vier Workflows.</p>
<p>Zurzeit enthält das Produkt PloneWorkflows im Collective-Projekt auf SourceForge (<a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>) zwei Workflows: den Community-Workflow und den One-Step Publication-Workflow. Der Community-Workflow ähnelt bis auf ein paar Änderungen dem Plone-Workflow. Der One-Step Publication-Workflow verfügt über zwei Zustände: privat und veröffentlicht.</p>
<p>Im Moment gibt es keine einfache Möglichkeit, Workflows zu installieren bzw. zu deinstallieren, ebenso wenig wie es eine wirklich einfache Methode gibt, den Zustand von Inhalten zu wechseln. Wenn Sie z.B. den One-step Publication-Workflow bei einem vorhandenen Zustand installieren, müssen Sie auch die Zustände für alle Objekte reparieren und sie in einen der neuen Zustände überführen. In diesem Fall ist das wahrscheinlich einfach: Alles im Zustand <em>Veröffentlicht</em> sollte so bleiben, wie es ist, und alles andere sollte in den Zustand <em>Privat</em> wechseln.</p>
</div>
</div>
</div>
<div class="section" id="workflows-hinzufugen-und-bearbeiten">
<h3>Workflows hinzufügen und bearbeiten</h3>
<p>Nachdem ich den Default-Workflow beschrieben habe, komme ich zum Hauptpunkt, der Sie wahrscheinlich am meisten interessiert: Wie können Sie die Defaults ändern? Nun, wie bei den meisten Dingen in Plone können Sie alle Workflows über das ZMI erstellen, bearbeiten und löschen. Das Werkzeug, mit dem der Workflow gesteuert wird, lautet <tt class="docutils literal">portal_workflow</tt>. In den folgenden Abschnitten behandle ich, wie Workflows zugewiesen werden, und gehe dann im Detail durch alle Einstellungen bei einem Workflow durch.</p>
<div class="section" id="workflows-auf-inhaltstypen-setzen">
<h4>Workflows auf Inhaltstypen setzen</h4>
<p>Nach einem Klick auf <em>portal_workflow</em> sehen Sie eine Liste von Workflow-Zuweisungen. Ein Merkmal von DCWorkflow ist, das jedem Inhaltstyp genau ein Workflow zugewiesen ist. Abbildung 8.4 zeigt diese Zuweisungen.</p>
<a class="reference external image-reference" href="img/08-04.png/image_view_fullscreen"><img alt="img/08-04.png" class="original" src="img/08-04.png" /></a>
<p>Abbildung 8.4. Die Workflow-Liste nach Typ</p>
<p>Auf dieser Seite sehen Sie eine Liste aller Inhaltstypen und des darauf angewendeten Workflows. Falls kein Workflow angegeben ist, d.h. falls der Wert leer ist, wird kein Workflow angewendet. Der Standardwert z.B. für den Typ Portal Site (Plone Site) ist leer. Ganz bestimmt möchten Sie nicht den Zustand der Plone-Site selbst wechseln, sondern nur den Zustand der Objekte darin. Falls der Wert (Default) lautet, wird der Default-Workflow unten auf der Seite auf diesen Inhaltstyp angewendet. In Abbildung 8.4 wird der Workflow <tt class="docutils literal">folder_workflow</tt> für Themen und Ordner benutzt, und bei allen anderen Inhaltstypen wird <tt class="docutils literal">plone_workflow</tt> angewendet. Die Namen eines Workflows beziehen sich auf den Namen von Workflow-Objekten, die innerhalb des Workflow-Werkzeugs importiert oder erstellt werden. Weitere Informationen über verfügbare Workflows finden Sie, wenn Sie auf den <em>Contents</em>-Reiter klicken. Danach wird eine Liste von Workflows geöffnet, die im System geladen sind, wie in Abbildung 8.5 zu sehen ist.</p>
<a class="reference external image-reference" href="img/08-05.png/image_view_fullscreen"><img alt="img/08-05.png" class="original" src="img/08-05.png" /></a>
<p>Abbildung 8.5. Verfügbare Workflows</p>
<p>Sie können Workflows hinzufügen, indem Sie auf den Button <em>Add Workflow</em> klicken. Dadurch wird eine Liste verfügbarer Workflows geöffnet. Um einen Workflow zu erstellen, wählen Sie einen Workflow-Typ und geben einen Workflow-Namen ein. Um einen leeren, aber über das Web konfigurierbaren Workflow zu erstellen, wählen Sie <em>dc_workflow</em> und geben einen passenden Namen ein, z.B. <strong>mein_workflow</strong>.</p>
</div>
<div class="section" id="bearbeiten-eines-workflows">
<h4>Bearbeiten eines Workflows</h4>
<p>Unter dem <em>Contents</em>-Reiter können Sie auf einen Workflow klicken, um zu den Management-Bildschirmen für alle Zustände, Übergänge und Eigenschaften zu diesem Workflow zu gelangen. Die Reiter oben auf der Seite beschreiben recht gut die Funktionalität eines Workflows: <em>States</em> (Zustände), <em>Transitions</em> (Übergänge), <em>Variables</em> (Variablen), <em>Worklists</em>, <em>Scripts</em> und <em>Permissions</em> (Rechte). Ich werde all diese Reiter und einige andere verfügbare Optionen durchgehen. Falls nichts Gegenteiliges gesagt wird, sind alle folgenden Reiter von der Workflow-Hauptseite aus erreichbar.</p>
<p>Das Erstellen und Bearbeiten von Workflows verlangt mitunter viel Klickerei und kann ein wenig verwirrend sein. Wenn Sie als Entwickler scharf darauf sind, das Dateisystem zu benutzen, können Sie all das aus Python heraus tun, wenn Sie möchten. Ich werde das im Abschnitt &quot;Workflows in Python schreiben&quot; gegen Ende des Kapitels behandeln.</p>
<div class="section" id="ausgangszustand-setzen">
<h5>Ausgangszustand setzen</h5>
<p>Um den Ausgangszustand zu setzen, gehen Sie zum <em>States</em>-Reiter und prüfen die  verfügbaren Zustände. Neben einem der Zustände werden Sie ein Sternchen wie in Abbildung 8.6 sehen.</p>
<a class="reference external image-reference" href="img/08-06.png/image_view_fullscreen"><img alt="img/08-06.png" class="original" src="img/08-06.png" /></a>
<p>Abbildung 8.6. Ausgangszustand für diesen Workflow setzen</p>
<p>Auf dieser Seite setzen Sie den Ausgangszustand für Ihren Workflow, indem Sie das Kontrollkästchen neben dem Zustand markieren und dann auf <em>Set Initial State</em> klicken. Alle Inhalte, die diesen Workflow benutzen, werden in diesem Ausgangszustand erzeugt. Alle anderen schon erzeugten Inhalte bleiben in ihrem Zustand. Bei einer späteren Zustandsänderung wird dieser Zustand nicht verändert. Zu jedem Workflow können Sie nur einen Ausgangszustand einstellen.</p>
<div class="sidebar">
<p class="first sidebar-title">Wie können Sie den Ausgangszustand auf Privat setzen?</p>
<!-- On some sites it may make sense for content to not show up at all or be accessible to users other than administrators and owners only after it has been completed. The best way to do this is to set the default state for the object to something that provides this security-for example, private. In the private state, only reviewers, managers, and owners can actually see the item. -->
<p>Auf manchen Sites kann es Sinn machen, wenn gewisse Inhalte überhaupt nicht erscheinen oder für andere Benutzer als Administratoren und Besitzer erst dann zugänglich sind, wenn sie vollständig fertig sind. Das erreicht man am besten, indem man den Ausgangszustand des Objekts so wählt, das diese Sicherheit vorhanden ist, z.B. <em>Privat</em>. Im privaten Zustand können nur Redakteure, Manager und Besitzer das Objekt sehen.</p>
<!-- To set the default state to private in the ZMI, click *portal_workflow*, and select the Contents tab. Next, click *plone_workflow*, select the States tab, and then select the default state by checking the box next to *visible*. Finally, click the Save Changes button. New content will now be in the private state and not accessible to the general public. -->
<p class="last">Um den Ausgangszustand im ZMI auf <em>Privat</em> zu setzen, klicken Sie auf <em>portal_workflow</em> und wählen den <em>Contents</em>-Reiter. Klicken Sie dann auf <em>plone_workflow</em>, wählen Sie den <em>States</em>-Reiter, und wählen Sie anschließend den Ausgangszustand, indem Sie das Kontrollkästchen neben <em>private</em> markieren. Am Ende klicken Sie auf den Button <em>Save Changes</em>. Nun werden neue Inhalte im Zustand <em>Privat</em> erstellt und sind für die Allgemeinheit nicht zugänglich.</p>
</div>
</div>
<div class="section" id="zustande-bearbeiten">
<h5>Zustände bearbeiten</h5>
<p>Unter dem <em>States</em>-Reiter werden die in diesem Workflow vorhandenen Zustände aufgelistet. Am Anfang dieses Kapitels habe ich erklärt, dass ein Zustand ein Objekt zu einem bestimmten Zeitpunkt darstellt. Jeder Zustand hat eine eindeutige ID, normalerweise ein einfaches Verb wie <em>pending</em> (offen) oder <em>published</em> (veröffentlicht). Um einen Zustand hinzuzufügen, geben Sie eine ID ein und klicken unten auf der Seite auf <em>Add</em>.</p>
<p>Außerdem werden Sie die folgenden Optionen sehen:</p>
<ul class="simple">
<li><strong>Title</strong>: Der Titel des Zustands wird in Ihrer Plone-Site angezeigt. Er ist ein benutzerfreundlicher Name für den Zustand.</li>
<li><strong>Description</strong>: Dies ist eine längere Beschreibung des Zustands. Momentan können die Benutzer diese Beschreibung nicht sehen, aber das kann sich in Zukunft noch ändern.</li>
<li><strong>Possible transitions</strong>: Hier werden die möglichen Übergänge aufgelistet, die aus diesem Zustand heraus erfolgen können. Diese Liste erscheint nur dann, wenn Sie tatsächlich einen Übergang im System haben. Wählen Sie einfach die Übergänge aus, die aus diesem Zustand erfolgen müssen. Durch die Wahl eines Übergangs für diesen Zustand machen Sie diesen Zustand zum Startpunkt des gewählten Übergangs.</li>
</ul>
<p>Um einen Zustand zu ändern, geben Sie die Änderungen ein und klicken dann auf <em>Save</em>, um diese zu bestätigen. Der <em>Permissions</em>-Reiter wird geöffnet und zeigt die Rechte an, die auf dem Objekt in diesem Zustand angewendet werden. Das kann bedeuten, dass sich die Rechte des Objekts ändern, wenn es in diesen Zustand übergeht. Das Formular ist ziemlich selbsterklärend. Damit ein anonymer Benutzer das Objekt sehen kann, markieren Sie die Kästchen für <em>View</em> und <em>Anonymous</em> und klicken Sie auf <em>Save</em>, wie in Abbildung 8.7 zu sehen ist.</p>
<a class="reference external image-reference" href="img/08-07.png/image_view_fullscreen"><img alt="img/08-07.png" class="original" src="img/08-07.png" /></a>
<p>Abbildung 8.7. Seite für Zustandsrechte</p>
<p>Wenn Sie die Rechte an einem bestimmten Workflow-Zustand ändern, haben Sie ein Problem, das Sie lösen müssen. Diese neuen Workflow-Rechte werden nicht auf den vorhandenen Inhalten in diesem Zustand gesetzt. Diese Inhalte haben weiterhin die alten Workflow-Rechte, und Sie müssen sie aktualisieren. Wenn Sie mit Ihren Änderungen fertig sind, gehen Sie in die Workflow-Ausgangsseite und klicken auf <em>Update Security Settings</em>, wie in Abbildung 8.4 gezeigt wird. Es kann eine Weile dauern, diese Aktualisierung durchzuführen, je nachdem, wie viele Objekte davon betroffen sind.</p>
<p>Der <em>Variables</em>-Reiter gestattet Ihnen die Zuweisung eines Wertes an eine Variable, falls das Objekt in diesem Zustand ist. Der Workflow bestimmt für jeden Zustand die Liste der verfügbaren Variablen. Weitere Informationen dazu finden Sie im Abschnitt &quot;Variablen bearbeiten&quot;.</p>
</div>
<div class="section" id="ubergange-bearbeiten">
<h5>Übergänge bearbeiten</h5>
<p>Der <em>Transitions</em>-Reiter listet die Übergänge auf, die in diesem Workflow erfolgen können. Zu Beginn dieses Kapitels habe ich Ihnen gezeigt, wie ein Übergang Änderungen darstellt, die auf dem Objekt erfolgen. Jeder Übergang hat einige Variablen, die auf der Zusammenfassungsseite angezeigt werden. Um einen Übergang hinzuzufügen geben Sie eine ID ein und klicken unten auf der Seite auf <em>Add</em>, wie in Abbildung 8.8 zu sehen ist.</p>
<a class="reference external image-reference" href="img/08-08.png/image_view_fullscreen"><img alt="img/08-08.png" class="original" src="img/08-08.png" /></a>
<p>Abbildung 8.8. Seite mit Übergangsdetails</p>
<p>Wenn Sie nun auf einen Übergang klicken, öffnen Sie die Details zu diesem Übergang:</p>
<ul class="simple">
<li><strong>Title</strong>: Dies ist der Titel des Übergangs.</li>
<li><strong>Description</strong>: Dies ist die detaillierte Beschreibung des Übergangs.</li>
<li><strong>Destination state</strong>: Das ist der Zielzustand dieses Übergangs. Der Ursprungszustand wird bei der Zuweisung des Übergangs an einen Zustand definiert.</li>
<li><strong>Trigger type</strong>: Hiermit wird angegeben, wodurch der Übergang ausgelöst wird. <em>Automatic</em> bedeutet, dass er sofort ausgelöst wird, wenn dieser Zustand erreicht wird. <em>Initiated by user action</em> ist die am häufigsten gewählte Einstellung und bedeutet, dass ein Benutzer den Übergang ausgelöst hat, indem er einen Link angeklickt hat.</li>
<li><strong>Script (before)</strong>: Dieses Skript wird ausgeführt, bevor der Übergang erfolgt.</li>
<li><strong>Script (after)</strong>: Dieses Skript wird ausgeführt, nachdem der Übergang erfolgt ist.</li>
<li><strong>Guard</strong>: Dies ist der Wächter bei diesem Zustand (wird gleich erklärt).</li>
<li><strong>Display in actions box</strong>: Hier wird angegeben, wie dieser Übergang in Plone dargestellt wird. Eine Eingabe an dieser Stelle garantiert, dass der Übergang auch als Aktion eingegeben wird. Sie erhalten diesen Übergang als Aktion, wenn Sie nach Aktionen fragen.</li>
</ul>
<p>Unter diesen Werten ist der Zielzustand recht interessant. Ich habe zwar schon erwähnt, dass Übergänge normalerweise den Zustand wechseln, aber das muss nicht so sein. Da bei jedem Übergang Skripten ausgeführt werden können und etwas protokolliert werden kann, ist es manchmal nützlich, den Zustand <em>nicht</em> zu wechseln. Betrachten Sie als Beispiel hierfür den Abschnitt &quot;Änderungen mit einem Workflow verfolgen&quot; weiter unten in diesem Kapitel. Wenn Ihr Übergang den Zustand wechselt, wählen Sie den neuen Zustand als Zielzustand.</p>
<p>Ein Übergang kann mehrere Startpunkte, aber nur ein Ziel haben. Wenn Sie mehrere Ziele brauchen, müssen Sie mehrere Übergänge anlegen. Sie können auch Skripten angeben, die vor und nach diesem Übergang ausgeführt werden. Zwei häufige Beispiele hierfür sind das Verschieben eines Objekts im Workflow und das Senden von Benachrichtigungen per E-Mail. Beide Beispiele werden im Abschnitt &quot;Häufige Aufgaben und Beispiele&quot; behandelt.</p>
<p>Bevor ein Übergang erfolgt, wird er von einem Sicherheitswächter (Guard) überprüft, um sicherzustellen, dass der Benutzer auch das Recht hat, den Übergang auszuführen. Dieser Wächter besteht aus den folgenden drei Komponenten:</p>
<ul class="simple">
<li><strong>Permission(s)</strong>: Dies sind die notwendigen Rechte. Mehrere Rechte sollten durch ein Semikolon (;) voneinander getrennt sein.</li>
<li><strong>Role(s)</strong>: Dies sind die notwendigen Rollen. Mehrere Rollen sollten durch ein Semikolon (;) voneinander getrennt sein.</li>
<li><strong>Expression</strong>: Dies ist ein Workflow-Ausdruck. Weitere Informationen dazu finden Sie im Abschnitt &quot;Workflow-Ausdrücke bearbeiten&quot; weiter unten in diesem Kapitel. Für jeden angegebenen Wert muss der Wächter den Wert <em>logisch wahr</em> ergeben, bevor weitergemacht werden kann. Falls ein Test bei einem der Werte fehlschlägt, wird der Übergang nicht ausgeführt. Meistens sind bei einem Wächter nur ein oder zwei Werte angegeben.</li>
</ul>
</div>
<div class="section" id="variablen-bearbeiten">
<h5>Variablen bearbeiten</h5>
<p>Der Variables-Reiter listet die Variablen auf, die im Workflow erzeugt und geändert werden. Bisher habe ich diese Variablen nicht groß erwähnt, sondern habe mich auf Zustände und Übergänge konzentriert. In diesem Abschnitt geht es nun um Variablen.</p>
<p>Es ist nicht immer möglich, alle in einem Workflow benötigten Informationen in Zuständen und Übergängen zu kapseln. Und ich rate Ihnen auch nicht, das unbedingt zu versuchen. Stattdessen können Sie Variablen verwenden, um einige Workflow-bezogene Informationen zu speichern. Im Kreditkartenbeispiel könnte die Rechnung z.B. auf mehrere Arten beglichen werden, z.B. per Online-Banking, Scheck usw. Sie könnten den Betrag (z.B. 100 Euro) in einer Variablen speichern. Sollte die Rechnung abgelehnt oder verändert werden, würde dieser Betrag aktualisiert. Der wesentliche Punkt bei einer Variablen ist der, dass man etwas hat, das sich zwischen den Zuständen und Übergängen ändern kann.</p>
<p>Klicken Sie nun wieder in der Workflow-Hauptseite auf den <em>Variables</em>-Reiter, um eine Liste aller Variablen zu bekommen. Um eine Variable hinzuzufügen, geben Sie eine Variablen-<em>ID</em> ein und klicken unten auf der Seite auf <em>Add</em>. Um zu bestimmen, in welchem Zustand ein Objekt zu einer bestimmten Zeit ist, speichert DCWorkflow den aktuellen Zustand in einer Variablen im Objekt. Standardmäßig lautet der Name dieser Variable <tt class="docutils literal">review_state</tt>.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Sollten Sie diesen Namen ändern müssen, weil es einen Konflikt mit einem anderen Namen gibt, können Sie das unten auf der Seite tun. Dabei geht allerdings der Zustand all Ihrer aktuellen Objekte verloren. Seien Sie also bei einer solchen Änderung vorsichtig.</p>
</div>
<p>Alle Workflow-Variablen verfügen über die folgenden Eigenschaften:</p>
<ul class="simple">
<li><strong>Description</strong>: Dies ist eine Beschreibung der Variablen.</li>
<li><strong>Make available to catalog</strong>: Diese Variablen werden in einer Liste platziert, auf die der Katalog zugreifen kann. Dabei werden kein Index oder Metadaten zum Katalog hinzugefügt. Das müssen Sie weiterhin von Hand machen.</li>
<li><strong>Store in workflow</strong>: Hiermit wird bestimmt, ob die Information im Workflow oder im Objekt gespeichert wird.</li>
<li><strong>Variable update mode</strong>: Dies bestimmt, wann die Variable aktualisiert wird.</li>
<li><strong>Default value</strong>: Das gibt einen Standardwert als String an.</li>
<li><strong>Default expression</strong>: Dies ist der Standardwert als Ausdruck. Wenn er vorhanden ist, wird er an Stelle des String-Wertes verwendet (weitere Informationen finden Sie im Abschnitt &quot;Workflow-Ausdrücke bearbeiten&quot; weiter unten in diesem Kapitel).</li>
<li><strong>Info. guard</strong>: Das sind Sicherheitseinstellungen beim Zugriff auf diese Variable. Diese Wächtereinstellungen ähneln jenen eines Übergangs. Hierbei wird der Wächter allerdings dann ausgeführt, wenn auf die Variable zugegriffen wird.</li>
</ul>
</div>
<div class="section" id="worklists-bearbeiten">
<h5>Worklists bearbeiten</h5>
<p>Der <em>Worklists</em>-Reiter bietet einen Zugriff auf alle Worklists, die in diesem Workflow zugewiesen sind. Eine <em>Worklist</em> ist eine Methode, den Workflow nach Informationen über die Anzahl von Objekten in diesem Workflow zu fragen. Ich möchte z.B. gern den Workflow einfach nach allen ausstehenden Kreditkartenrechnungen für mich fragen.</p>
<p>Um eine Worklist hinzuzufügen, geben Sie eine ID ein und klicken auf <em>Add</em>. Alle Worklists verfügen über folgende Eigenschaften:</p>
<ul class="simple">
<li><strong>Description</strong>: Dies ist eine Beschreibung der Worklist.</li>
<li><strong>Cataloged variable matches</strong>: Dies ist der Wert, auf den die Workflow-Variable eines Objekts passen muss, damit es zu dieser Worklist hinzugefügt wird. Die passende Variable ist die Workflow-Zustandsvariable, die in der Variablenliste angegeben ist (der Standardname dieser Variablen lautet <tt class="docutils literal">review_state</tt>).</li>
<li><strong>Display in actions box</strong>: Diese Information soll in der Benutzerschnittstelle angezeigt werden. Die Eingabe eines Wertes an dieser Stelle stellt auch sicher, dass der Übergang als Aktion eingetragen wird. Sie erhalten diesen Übergang als Aktion, wenn Sie nach den Aktionen fragen.</li>
<li><strong>Guard</strong>: Dies ist ein Wächter für den Zugriff auf diese Worklist.</li>
</ul>
<p>Kommen wir wieder zum Kreditkartenbeispiel zurück: Wenn ich gerne wüsste, welche Kreditkartenrechnungen ich alle überprüfen muss, könnte ich diese Information in einer Worklist angeben. Zuerst würde die Variable <tt class="docutils literal">review_state</tt> den aktuellen Zustand jedes Elements enthalten. Alle zu überprüfenden Kreditkartenrechnungen wären im Zustand <em>review</em> (überprüfen). Zweitens würde ich eine Worklist namens <tt class="docutils literal">review_queue</tt> hinzufügen, und der Variablenwert wäre <tt class="docutils literal">pending</tt> (offen). Nun würde ich die Worklist nach allen Elementen in <tt class="docutils literal">review_queue</tt> fragen.</p>
<p>Obwohl Worklists eine bequeme Art sind, diese Information zu speichern, benutzt Plone selbst keine Worklists. Stattdessen benutzt Plone direkt ZCatalog, um Objekte abzufragen, die sich in einem Workflow befinden. Da die DCWorkflow-Worklist das Katalog-Werkzeug verwendet, ist das Ergebnis das gleiche.</p>
</div>
<div class="section" id="skripten-bearbeiten">
<h5>Skripten bearbeiten</h5>
<p>Der <em>Scripts</em>-Reiter listet die Skripten auf, die in diesem Workflow verfügbar sind. Diese Liste ist eigentlich ein normaler Ordner im ZMI, und Sie können darin fast alles hinzufügen. Der Hauptgrund dafür ist vermutlich der, ein Skript hinzuzufügen, um eine kompliziertere Behandlung von Übergängen zu bewerkstelligen, d.h., Sie sollten an dieser Stelle nur Script (Python)-Objekte hinzufügen.</p>
<p>Um ein Skript unter dem Scripts-Reiter hinzuzufügen, wählen Sie im <em>Add</em>-Dropdown-Menü die Option <em>Script (Python)</em> und geben dem Skript eine ID. Dem Skript wird genau ein Objekt übergeben, nämlich das Ausdrucksobjekt des Basis-Workflows. Weitere Informationen darüber finden Sie im Abschnitt &quot;Workflow-Skripten bearbeiten&quot; weiter unten in diesem Kapitel. Wenn Sie z.B. auf das eigentliche Objekt im Workflow zugreifen müssen, können Sie ein Python-Skript wie folgt benutzen:</p>
<pre class="literal-block">
##parameters=state_change
obj = state_change.object
</pre>
<p>Was in diesem Skript passiert, entscheidet dessen Entwickler. Sie können hier fast alles machen. Sie können Ereignisse auslösen, und Sie können auf andere Workflows und Übergänge zugreifen. Einige Beispielskripten finden Sie in den Abschnitten &quot;Benachrichtigungen per E-Mail versenden&quot; und &quot;Objekte verschieben&quot; im weiteren Verlauf dieses Kapitels. Wenn das Skript ausgeführt wird, wird es unter dem Benutzer ausgeführt, der den Übergang eingeleitet hat. Sie könnten dem Skript eine Proxy-Rolle zuweisen, wenn es unter einem anderen Benutzer ausgeführt werden muss. Was die Übergänge angeht, so können Sie dieses Skript in den Einstellungen zu <em>script (after)</em> und <em>script (before)</em> an beliebig viele Übergänge zuweisen. Sie können das Skript entweder vor oder nach einem Übergang ausführen.</p>
</div>
<div class="section" id="rechte-bearbeiten">
<h5>Rechte bearbeiten</h5>
<p>Der <em>Permissions</em>-Reiter listet die von diesem Workflow verwalteten Rechte auf. Diese Rechte haben Sie schon bei der Behandlung von Zuständen gesehen. Unter diesem Reiter setzen Sie die Liste der Rechte, die in diesen Zuständen verwaltet werden. Ein neues Recht fügen Sie hinzu, indem Sie es im Dropdown-Menü auswählen und auf <em>Add</em> klicken.</p>
<div class="sidebar">
<p class="first sidebar-title">Wie können Sie ein veröffentlichtes Dokument bearbeiten?</p>
<!-- Well, you can't edit a published document in the default workflow unless you have the manager role. If you allow the owner of the document to edit it, then you really should review it again. However, this seems to be a common request and is a trivial thing to change. In the ZMI, click *portal_workflow*, and select the Contents tab. Then click *plone_workflow*, and select the States tab. Finally, click *published* and then select the Permissions tab. Check the box that corresponds to allowing the owner to modify portal content. -->
<p>Nun, im Default-Workflow können Sie ein schon veröffentlichtes Dokument nicht bearbeiten, es sei denn, Sie haben die Rolle eines Managers. Wenn Sie dem Besitzer des Dokuments erlauben, es zu bearbeiten, sollten Sie es wirklich nochmal prüfen. Allerdings scheint das eine häufige Frage zu sein, und man kann es ganz schnell so einrichten, dass das geht. Klicken Sie im ZMI auf <em>portal_workflow</em>, und wählen Sie den <em>Contents</em>-Reiter. Dann klicken Sie auf <em>plone_workflow</em> und wählen den <em>States</em>-Reiter. Und schließlich klicken Sie auf <em>published</em> und wählen den <em>Permissions</em>-Reiter. Markieren Sie das Kästchen, das einem Besitzer erlaubt, Portal-Inhalte zu ändern.</p>
<a class="reference external image-reference" href="img/08-08a.png/image_view_fullscreen"><img alt="img/08-08a.png" class="original" src="img/08-08a.png" /></a>
<!-- Click Save Changes to save your permissions. Because you've updated the security settings, you'll have to click *portal_workflow*, select the Contents tab, and click *Update security settings*. This will update all the objects in your site and ensure that your permissions have been applied to existing objects. Now owners can edit their documents while they're in the published state. -->
<p class="last">Klicken Sie auf <em>Save Changes</em>, um Ihre Rechte zu speichern. Da Sie die Sicherheitseinstellungen aktualisiert haben, müssen Sie auf <em>portal_workflow</em> klicken, den <em>Contents</em>-Reiter wählen und auf <em>Update security settings</em> klicken. Dadurch werden alle Objekte in Ihrer Site aktualisiert, und es wird garantiert, dass Ihre Rechte auf den vorhandenen Objekten angewendet wurden. Ab jetzt können Dokumente von ihren Besitzern bearbeitet werden, während sie im Zustand <em>Veröffentlicht</em> sind.</p>
</div>
</div>
<div class="section" id="workflow-skripten-bearbeiten">
<h5>Workflow-Skripten bearbeiten</h5>
<p>Skripten bieten dem Entwickler die Möglichkeit, bei der Ausführung eines Übergangs eine zusätzliche Anwendungslogik auszuführen. Das kann fast alles sein, was Sie tun möchten. Sie könnten z.B prüfen, ob gewisse Bedingungen erfüllt sind (z.B. ob die Rechtschreibung beim Dokument geprüft wurde) oder ob spezielle Aktionen ausgeführt wurden. Wenn ein Objekt seinen Zustand wechselt, wird das Skript aufgerufen.</p>
<p>Wenn ein Skript aufgerufen wird, wird ihm ein zusätzlicher Parameter übergeben. Dieser zusätzliche Parameter ermöglicht den Zugriff auf alle Arten von übergangsbezogenen Elementen und Attributen. Dieser Parameter wird auch <tt class="docutils literal">state_change</tt>-Parameter genannt und hat folgende Attribute:</p>
<ul class="simple">
<li><strong>status</strong>: Dies ist der Status des Workflows.</li>
<li><strong>object</strong>: Dies ist das Objekt, das im Workflow einen Zustandswechsel durchmacht.</li>
<li><strong>workflow</strong>: Dies ist das aktuelle Workflow-Objekt zu dem betroffenen Objekt.</li>
<li><strong>transition</strong>: Dies ist das aktuell ausgeführte Übergangsobjekt.</li>
<li><strong>old_state</strong>: Dies ist der Ursprungszustand des Objekts.</li>
<li><strong>new_state</strong>: Dies ist der Zielzustand des Objekts.</li>
<li><strong>kwargs</strong>: Dies sind die Schlüsselargumente, die an die Methode <tt class="docutils literal">doActionFor</tt> übergeben werden.</li>
<li><strong>getHistory</strong>: Dies ist eine Methode, die keine Parameter hat und eine Kopie der Workflow-Historie des Objekts zurückgibt.</li>
<li><strong>getPortal</strong>: Dies ist eine Methode, die keine Parameter hat und das Plone-Wurzelobjekt zurückgibt.</li>
<li><strong>ObjectDeleted(ordner)</strong>: Dies sagt dem Workflow, dass das Objekt, dessen Zustand wechselt, gelöscht wurde. Diese Methode erwartet das Objekt, zu dem der Benutzer zurückkehren soll. Übergeben Sie der Ausnahme den Ordner, zu dem der Benutzer umgeleitet werden soll (siehe den Abschnitt &quot;Objekte verschieben&quot; weiter unten in diesem Kapitel).</li>
<li><strong>ObjectMoved(neuesObjekt, neuesObjekt)</strong>: Dies sagt dem Workflow, dass das Objekt, dessen Zustand wechselt, verschoben wurde. Übergeben Sie der Ausnahme den Ordner, zu dem der Benutzer umgeleitet werden soll (siehe den Abschnitt &quot;Objekte verschieben&quot; weiter unten in diesem Kapitel).</li>
<li><strong>getDateTime</strong>: Dies ist eine Methode, die keine Parameter hat und das zum Übergang gehörende <tt class="docutils literal">DateTime</tt>-Objekt zurückgibt.</li>
</ul>
<p>Um z.B. herauszufinden, zu welchem Zielzustand ein Übergang erfolgt und wann, können Sie das folgende Script (Python)-Objekt verwenden, das Ihnen genau diese Information liefert. Dieses Skript schreibt die Information über den Übergang in die Protokolldatei:</p>
<pre class="literal-block">
##parameters=state_change
st = 'From %s to %s on %s' % (
    state_change.old_state,
    state_change.new_state,
    state_change.getDateTime())
context.plone_log(st)
</pre>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Wenn Sie ein Script (Python)-Objekt schreiben, müssen Sie eventuell etwas in die Protokolldatei ausgeben, um sich die Fehlersuche leichter zu machen. Ein Skript namens <tt class="docutils literal">plone_log</tt> tut genau das. Es erwartet einen String, den es an die Protokollfunktionen von Plone übergibt. Daher sind Aufrufe von <tt class="docutils literal">context.plone_log</tt> bei der Fehlersuche sehr hilfreich.</p>
</div>
<p>Bei der Zuweisung eines Skripts an einen Übergang haben Sie zwei Möglichkeiten: <tt class="docutils literal">before</tt> und <tt class="docutils literal">after</tt>. Wie diese Namen schon besagen, wird ein Skript, das an <tt class="docutils literal">before</tt> zugewiesen wird, ausgeführt, bevor der Übergang erfolgt. Das eignet sich für Skripten, die prüfen, ob etwas noch vor dem Übergang passieren soll, die also z.B. testen, ob ein weiteres abhängiges Objekt oder eine Seite hochgeladen wurde oder ob etwas frei von Tippfehlern ist. Das an <tt class="docutils literal">after</tt> zugewiesene Skript wird ausgeführt, nachdem der Übergang beendet ist. Sollte aber eine irgendwann ausgelöste, nicht abgefangene Ausnahme in einem Skript auftreten, dann führt das  dazu, dass der Übergang fehlschlägt, das Objekt in seinem ursprünglichen Zustand bleibt und dem Benutzer die Ausnahme angezeigt wird.</p>
</div>
<div class="section" id="workflow-ausdrucke-bearbeiten">
<h5>Workflow-Ausdrücke bearbeiten</h5>
<p>In diesem Kapitel haben Sie durchgehend Werte gesehen, die als Workflow-Ausdrücke ausgedrückt werden können. Der Wert beispielsweise, der einer Variablen zugewiesen wird, ist das Ergebnis eines Workflow-Ausdrucks. Dieser Ausdruck ist nichts Ungewöhnliches, sondern lediglich ein TAL-Ausdruck (Template Attribute Language) mit einigen anderen verfügbaren Variablen. TAL-Ausdrücke haben Sie bereits in Kapitel 5 kennen gelernt, d.h., Sie sollten mit diesen Ausdrücken und ihren Optionen wie Python-, String- und Pfadausdrücken vertraut sein.</p>
<p>Anders als bei den normalen TAL-Ausdrücken werden einige zusätzliche Parameter mit Bezug auf den konkreten Workflow über den Namespace übergeben. Der Namespace eines Workflow-Ausdrucks enthält Folgendes:</p>
<ul class="simple">
<li><strong>here</strong>: Dies ist das Objekt, das im Workflow einen Zustandswechsel erfährt.</li>
<li><strong>container</strong>: Dies ist der Container des Objekts, das im Workflow den Zustandswechsel erfährt.</li>
<li><strong>state_change</strong>: Dies ist das Zustandswechselobjekt, das im Abschnitt &quot;Workflow-Skripten bearbeiten&quot; erwähnt wurde.</li>
<li><strong>transition</strong>: Dies ist der gerade ausgeführte Übergang; identisch mit <tt class="docutils literal">state_change.transition</tt>.</li>
<li><strong>status</strong>: Dies ist der ursprüngliche Zustand, identisch mit <tt class="docutils literal">state_change.old_state</tt>.</li>
<li><strong>workflow</strong>: Dies ist der Workflow für dieses Objekt.</li>
<li><strong>scripts</strong>: Dies sind die in diesem Workflow verfügbaren Skripten.</li>
<li><strong>user</strong>: Dies ist der Benutzer, der den Übergang ausführt.</li>
</ul>
</div>
</div>
</div>
<div class="section" id="haufige-aufgaben-und-beispiele">
<h3>Häufige Aufgaben und Beispiele</h3>
<p>Ich werde Ihnen nun einige Aufgaben vorstellen, die Sie leicht mit einem Workflow bewerkstelligen können. Wenn ein Benutzer einen Übergang im Workflow bewirkt, wird dieser Übergang mit den Rechten dieses konkreten Benutzers ausgeführt. In vielen dieser Beispiele verfügt ein normaler Benutzer möglicherweise nicht über die notwendigen Rechte, um die Aufgabe auszuführen. Mitglieder haben z.B. normalerweise kein Recht, auf die Liste der Mitglieder zuzugreifen, sofern ihnen dieses Recht nicht explizit gewährt worden ist.</p>
<p>Um dieses Rechteproblem zu lösen, wurde da, wo es vermerkt ist, einigen der folgenden Script (Python)-Objekte eine leicht andere Rolle gegeben. Um auf einem Skript eine Proxy-Rolle zu setzen, greifen Sie auf den <em>Proxy</em>-Reiter eines Objekts zu und wählen dann den Benutzer, der das Skript ausführen soll, wie in Abbildung 8.9 zu sehen ist.</p>
<a class="reference external image-reference" href="img/08-09.png/image_view_fullscreen"><img alt="img/08-09.png" class="original" src="img/08-09.png" /></a>
<p>Abbildung 8.9. Proxy-Einstellungen eines Skripts setzen</p>
<p>Natürlich würden Sie dabei dafür sorgen, dass Ihre Skripten mit den minimal notwendigen Rollen ausgeführt werden, je nachdem, was Ihr Skript genau macht.</p>
<div class="section" id="einfuhrung-in-workflow-ausdrucke">
<h4>Einführung in Workflow-Ausdrücke</h4>
<p>Im Folgenden sehen Sie einige nützliche Beispiele von Workflow-Ausdrücken, die an verschiedenen Stellen verwendet werden können.</p>
<p>Benutzen Sie Folgendes, um die Kommentare eines Übergangs bzw. einen leeren String zu erhalten:</p>
<pre class="literal-block">
python:state_change.kwargs.get('comment', '')
</pre>
<p>Um den Titel eines Ordners zu erhalten, in dem das Objekt enthalten ist, benutzen Sie Folgendes:</p>
<pre class="literal-block">
container/Title
</pre>
<p>Um zu testen, ob der alte Zustand gleich <em>Offen</em> (pending) ist, schreiben Sie Folgendes:</p>
<pre class="literal-block">
python: state_change.old_state == 'pending'
</pre>
<p>Um an den Benutzer zu kommen, der den Übergang ausführt, benutzen Sie Folgendes:</p>
<pre class="literal-block">
user/getId
</pre>
<p>Wenn Sie also mitverfolgen möchten, welcher Benutzer zuletzt den Zustand eines Objekts verändert hat, könnten Sie eine Variable namens <tt class="docutils literal">last_user</tt> zum Workflow hinzufügen. Das machen Sie, indem Sie zum Workflow gehen, dann auf den <em>Variables</em>-Reiter klicken und anschließend die Variable <tt class="docutils literal">last_user</tt> hinzufügen. Wenn Sie die Variable <tt class="docutils literal">Default expr</tt> auf <tt class="docutils literal">user/getId</tt> setzen, würde dieser Wert jedes Mal für Sie gespeichert, wenn das Objekt seinen Zustand ändert.</p>
</div>
<div class="section" id="anderungen-mit-einem-workflow-verfolgen">
<h4>Änderungen mit einem Workflow verfolgen</h4>
<p>Ein Kunde von mir wollte für eine spezielle Anwendung mitverfolgen, wann immer und aus welchem Grund ein Element bearbeitet wurde, so dass bei einer späteren Prüfung des Elements zu jeder Änderung ein Kommentar vorhanden wäre. Mit Hilfe eines Workflows war das recht einfach zu realisieren.</p>
<p>In diesem Fall bestand der Workflow aus nur einem Zustand, aber das würde sogar bei fast allen Workflows funktionieren. Bei diesem speziellen Workflow wurde ein Übergang namens <tt class="docutils literal">edit</tt> hinzugefügt, der nicht wirklich den Objektzustand verändert. Der Zielzustand dieses Übergangs war <tt class="docutils literal">(Remain in state)</tt>, d.h., es fand kein Wechsel statt.</p>
<p>Wenn ein Objekt bearbeitet wird, wird eine Methode aufgerufen, die den Übergang ausführt. Wenn z.B. ein Dokument bearbeitet wird, lautet die aufgerufene Methode <tt class="docutils literal">document_edit.cpy</tt>. Dieses Skript finden Sie, wenn Sie nacheinander <tt class="docutils literal">portal_skins</tt>, <tt class="docutils literal">plone_form_scripts</tt> und <tt class="docutils literal">document_edit</tt> anklicken. Sie müssen lediglich eine Zeile vor der letzten Zeile dieses Skripts hinzufügen:</p>
<pre class="literal-block">
context.portal_workflow.doActionFor(new_context,
 'edit', comment='')
</pre>
<p>Die Methode <tt class="docutils literal">doActionFor</tt> in <tt class="docutils literal">portal_workflow</tt> führt den gegebenen Übergang (in diesem Fall <tt class="docutils literal">edit</tt>) auf dem übergebenen Objekt durch (in diesem Fall <tt class="docutils literal">context</tt>). Jedes Mal, wenn das Objekt bearbeitet wird, wird dieser <tt class="docutils literal">edit</tt>-Übergang ausgelöst. Das führt dann dazu, dass eine Zeile zur Kommentarliste hinzugefügt wird, in der neben einem Kommentar steht, wer das Objekt bearbeitet hat und wann es hinzugefügt wurde.</p>
<p>Eigentlich gibt es keinen Kommentar, wenn ein Objekt bearbeitet wird. Das heißt, um es ganz richtig zu machen, müssten Sie das Bearbeiten-Template des Dokuments so modifizieren, dass es ein Kommentarfeld enthält. Dann könnten Sie mit dem <tt class="docutils literal">content_status_history</tt>-Formular auf diese Kommentarliste zugreifen, wo unten die Liste der Änderungen angezeigt wird. Dieses Formular erreichen Sie, indem Sie im Workflow-Menü auf <em>erweitert</em> klicken.</p>
</div>
<div class="section" id="objekte-verschieben">
<h4>Objekte verschieben</h4>
<p>Eine nützliche Fähigkeit ist die, ein Objekt im Workflow zu verschieben. Sie könnten z.B. immer dann, wenn Sie eine Pressemitteilung veröffentlichen, alle Pressemitteilungen in einen Ordner namens <tt class="docutils literal">Press Release</tt> verschieben. Inhalte könnten irgendwo erstellt und bearbeitet werden, um dann bei ihrer Veröffentlichung in diesen Ordner verschoben zu werden. Das Beispielskript in Listing 8.1 verschiebt das vom Workflow erfasste Objekt in den <tt class="docutils literal">Members</tt>-Ordner. Um dieses Skript hinzuzufügen, gehen Sie im ZMI zum Workflow-Werkzeug und klicken auf den <em>Scripts</em>-Reiter. Dann wählen Sie <em>Script (Python)</em> im Dropdown-Menü aus. Nun geben Sie dem neuen Objekt den Namen <strong>moveObject</strong> und geben dann den Code aus Listing 8.1 in diesem Skript ein.</p>
<p>Listing 8.1. Ein Objekt verschieben</p>
<pre class="literal-block">
##parameters=state_change
# get the object and its ID
obj = state_change.object
id = obj.getId()

# get the src folder and the destination folder
dstFldr = context.portal_url.Members
srcFldr = obj.aq_parent

# perform the move
objs = srcFldr.manage_cutObjects([id,])
dstFldr.manage_pasteObjects(objs)

# get the new object
new_obj = dstFldr[id]

# pass new_obj to the error, *twice*
raise state_change.ObjectMoved(new_obj, new_obj)
</pre>
<p>Sie müssen noch ein paar andere Dinge machen. Zuerst weisen Sie dieses Skript einem Übergang zu. Ich benutze ein solches Skript normalerweise im Publish-Übergang. Dazu gehen Sie zum Übergang und weisen <tt class="docutils literal">moveObject</tt> den Wert <tt class="docutils literal">script (after)</tt> zu.</p>
<p>Zweitens gibt es noch ein kleines Problem: Dieses Skript verschiebt Objekte in den <tt class="docutils literal">Members</tt>-Ordner. Wahrscheinlich schwebt Ihnen ein besserer Ort dafür vor. Um ein solches Verschieben vorzunehmen, muss ein Benutzer die geeigneten Rechte haben, um Objekte zwischen diesen Ordnern zu bewegen. Normalerweise kann nur ein Manager Objekte in den <tt class="docutils literal">Members</tt>-Ordner verschieben. Daher müssen Sie dem Skript die Proxy-Rolle eines Managers geben. Das tun Sie, indem Sie erst auf <em>Scripts</em> und dann auf <em>moveObject</em> klicken und anschließend den <em>Proxy</em>-Reiter wählen. Weisen Sie diesem Skript dann die Manager-Rolle zu. Weitere Informationen über Sicherheit und lokale Rollen finden Sie in Kapitel 9.</p>
<p>Wenn Sie sich den Code anschauen, sehen Sie, dass das Skript zuerst das Objekt und seine Objekt-ID aus dem Namespace des Übergangs holt. Dann holt es sich den Ursprungs- und Zielordner. Danach benutzt es die <tt class="docutils literal">ObjectManager</tt>-API von Zope zum Kopieren und Einfügen. Natürlich könnten Sie diese Ordner auch im Programm selbst bestimmen, beispielsweise abhängig vom Benutzer, der den Übergang durchführt, oder vom Typ des verschobenen Inhalts. Schließlich haben Sie das Objekt und übergeben es an die Ausnahme <tt class="docutils literal">ObjectMoved</tt>.</p>
<p>Die Ausnahme (Exception) <tt class="docutils literal">ObjectMoved</tt> ist eine spezielle Ausnahme in DCWorkflow. Indem das neue Objekt zweimal als Parameter an die Ausnahme übergeben wird, wird dieses neue Objekt an das Plone-Frontend weitergegeben. Hier muss man aufpassen, denn wenn der Benutzer als Folge der Änderung zum Objekt geleitet wird, ist es das Objekt an seinem neuen Zielort und nicht das Objekt am Ursprungsort. Natürlich könnten Sie auch eine Funktion schreiben, die das Objekt wieder zurückbewegt, nachdem es abgelehnt wurde - vielleicht in den Startordner des Benutzers.</p>
<p>Ein weiterer spezieller und eher ungewöhnlicher Fall ist der, ein Objekt im Workflow zu löschen. Normalerweise ist das Löschen eines Objekts eine Aktion des  umgebenden Objekts, daher ist es im Workflow ungewöhnlich. Für diese Aufgabe können Sie die Ausnahme <tt class="docutils literal">ObjectDeleted</tt> auslösen. Listing 8.2 zeigt das Skript, mit dem man ein Objekt löschen kann.</p>
<p>Listing 8.2. Ein Objekt löschen</p>
<pre class="literal-block">
##parameters=state_change

# get the object
obj = state_change.object
id = obj.getId()

# get the parent folder, delete the object
srcFldr = obj.aq_parent
srcFldr.manage_delObjects([id,])

# raise the object deleted method and pass
# the folder you want to return to
raise state_change.ObjectDeleted(srcFldr)
</pre>
<p>Dieses Skript könnten Sie <tt class="docutils literal">deleteObject</tt> nennen und Objekte damit erfolgreich im Workflow löschen. Noch einmal: Durch das Auslösen der Ausnahme weiß Plone, was es zu tun hat. In diesem Fall bringt es den Benutzer zu dem Ordner, der das Objekt enthält.</p>
</div>
<div class="section" id="benachrichtigungen-per-e-mail-versenden">
<h4>Benachrichtigungen per E-Mail versenden</h4>
<p>Wenn Sie eine Website haben, die von ihren Benutzern nicht so oft besucht wird, dann ist es sinnlos, darauf Informationen darüber anzuzeigen, welche Inhalte geprüft werden müssen. Sie können den Workflow aber zu einem einfachen Benachrichtigungssystem machen, indem Sie damit E-Mails an die Benutzer verschicken. Der Nachrichtenkanal über E-Mail ist nur ein Beispiel. Es könnte auch einer mit Instant Messages, SMS-Mitteilungen per Telefon usw. sein. Ich über lasse es Ihnen, sich weitere Möglichkeiten auszudenken.</p>
<p>In diesem Beispiel senden Sie E-Mails über das <tt class="docutils literal">MailHost</tt>-Objekt auf dem Server an alle Benutzer mit der Rolle eines Redakteurs im System. Mit diesen E-Mails teilen Sie ihnen mit, dass neue Elemente für die Überprüfung bereitstehen. Dieses Skript ist etwas komplizierter als die anderen, die ich Ihnen bisher gezeigt habe, da es mehrere Schritte durchmacht: Definieren der Variablen, Finden der Kontonamen aller Redakteure, Finden und Senden einer E-Mail. Sie sehen das Skript in Listing 8.3.</p>
<p>Listing 8.3. Eine Benachrichtigung per E-Mail versenden</p>
<pre class="literal-block">
##parameters=state_change
# the objects we need
object = state_change.object
mship = context.portal_membership
mhost = context.MailHost
administratorEmailAddress = context.email_from_address

# the message format, %s will be filled in from data
message = &quot;&quot;&quot;
From: %s
To: %s
Subject: New item submitted for approval - %s

%s

URL: %s
&quot;&quot;&quot;
</pre>
<p>Hiermit werden die Nachricht und die benötigten Objekte aufgesetzt. Neben dem Objekt, das einen Zustandswechsel erfährt, benötigen Sie auch eine Referenz auf das Mitglieder-Werkzeug <tt class="docutils literal">portal_membership</tt> und den SMTP-Server (Simple Mail Transfer Protocol) über <tt class="docutils literal">MailHost</tt>. Die Nachricht lässt sich leicht so konfigurieren, dass Sie eine E-Mail in einem beliebigen Format schicken können.</p>
<p>Dann benutzen Sie die Methode <tt class="docutils literal">listMembers</tt> des Objekts <tt class="docutils literal">portal_membership</tt>, um eine Liste der Mitglieder zu erhalten. Bei jedem Mitglied können Sie dann nachprüfen, ob in der Liste der Rollen zu diesem Benutzer die Redakteursrolle enthalten ist, indem Sie die Methode <tt class="docutils literal">getRoles</tt> aufrufen:</p>
<pre class="literal-block">
for user in mship.listMembers():
    if &quot;Reviewer&quot; in mship.getMemberById(user.id).getRoles():
</pre>
<p>Aufmerksame Leser werden bemerken, dass eine Iteration über alle Mitglieder einer Plone-Site etwas langsam sein könnte, falls Sie Tausende von Benutzern haben sollten. Im nächsten Kapitel werden Sie das Skript modifizieren, um eine Liste von Benutzern aus einer speziellen Gruppe zu erhalten.</p>
<p>Eine E-Mail zu verschicken macht nur dann Sinn, wenn Sie die E-Mail-Adresse eines Benutzers haben, d.h., hier prüfen Sie zuerst, ob es eine gültige E-Mail-Adresse gibt. Nun muss die E-Mail nur noch formatiert und verschickt werden. Dazu können Sie die String-Ersetzungsfunktionen von Python benutzen und vier Parameter übergeben, die dem <tt class="docutils literal">%s</tt> in der Variable <tt class="docutils literal">message</tt> entsprechen, die am Anfang des Skripts gesetzt werden. Nach dieser Ersetzung enthält die Variable <tt class="docutils literal">msg</tt> die E-Mail, die Sie verschicken möchten. Um sie abzuschicken, rufen Sie einfach die Methode <tt class="docutils literal">send</tt> von <tt class="docutils literal">MailHost</tt> auf und übergeben dabei den String mit der E-Mail:</p>
<pre class="literal-block">
if user.email:
    msg = message % (
         administratorEmailAddress,
         user.email,
         object.TitleOrId(),
         object.Description(),
         object.absolute_url()
         )
    mhost.send(msg)
</pre>
<p>Dadurch wird die folgende E-Mail verschickt:</p>
<pre class="literal-block">
From: administrator&#64;agmweb.ca
To: andy&#64;agmweb.ca
Subject: New item submitted for approval - Plone's great

We all know Plone is a great product, but with the newest release
it's gotten even better...

URL: http://agmweb.ca/Members/andym/News_Item_123
</pre>
<p>Anhang B zeigt das vollständige Listing für dieses Skript.</p>
</div>
<div class="section" id="plonecollectorng-verwenden">
<h4>PloneCollectorNG verwenden</h4>
<p>PloneCollectorNG ist ein Bugtracking-Programm für Plone. Sie werden noch viele weitere solcher Programme finden, aber dieses eine benutze und empfehle ich im Zusammenhang mit Plone. Tatsächlich scheinen Entwickler sehr häufig solche Fehlerverwaltungsprogramme zu schreiben. Eine sehr nette Eigenschaft von Workflows ist, dass Ihre Benutzer damit ganz wesentlich die Art und Weise ändern können, wie eine Anwendung funktioniert. Die Entwicklung von Produkten, die mit DCWorkflow zusammenarbeiten, ermöglicht es, dass Ihre Anwendung flexibel bleibt. Sie finden PloneCollectorNG unter <a class="reference external" href="http://www.zope.org/Members/ajung/PloneCollectorNG">http://www.zope.org/Members/ajung/PloneCollectorNG</a>.</p>
<p>Das Produkt fügt bei der Installation eine Reihe von Inhaltstypen hinzu, darunter PloneIssueNG, also einen Fehlerbericht (<em>issue</em>). Anstatt genau festzuschreiben, wie der Bericht durch die Datenbank geht, wird ihm ein eigener Workflow zugewiesen. Dieser Workflow enthält die passenden Zustände, Übergänge, Variablen und Worklists.</p>
<p>Sie können zu jedem Zeitpunkt herausfinden, in welchem Zustand ein Objekt ist, indem Sie die Methode <tt class="docutils literal">getInfoFor</tt> in <tt class="docutils literal">portal_workflow</tt> aufrufen. Diese nützliche Methode erwartet ein Objekt und die Variable, die gesucht werden soll. Im Workflow von PloneCollectorNG lautet diese Variable <tt class="docutils literal">state</tt>, und im Plone-Workflow lautet sie <tt class="docutils literal">review_state</tt>. Um z.B. den Zustand eines Objekts herauszufinden, benutzen Sie Folgendes:</p>
<pre class="literal-block">
portal_workflow.getInfoFor(obj, &quot;state&quot;)
</pre>
<p>Die möglichen Zustände eines Objekts finden Sie heraus, indem Sie das Zustandsobjekt direkt im Workflow wie folgt untersuchen:</p>
<pre class="literal-block">
portal_workflow['pcng_workflow'].states._mapping.keys()
</pre>
<p>Wenn Ihre Benutzer ein einfaches Fehlerberichtssystem haben möchten, dann können Sie hiermit recht einfach diesen Workflow über das Web entsprechend anpassen (falls bei der Entwicklung der Anwendung die Workflow-Werkzeuge berücksichtigt wurden). Vergleichen Sie das einmal mit Bugzilla, einem anderen populären Bugtracking-System, wo zum Ändern eines Zustands oder eines Übergangs viele Stunden an Arbeitszeit eines Perl-Programmierers nötig sind, um alle festkodierten Referenzen auf Zustände von Fehlern herauszufinden.</p>
</div>
<div class="section" id="workflows-verteilen-und-schreiben">
<h4>Workflows verteilen und schreiben</h4>
<p>Wenn Sie einen tollen Workflow für Ihre Anwendung haben, dann stehen Ihnen einige verschiedene Möglichkeiten offen, den Workflow zu schreiben und zu verteilen. Die folgenden Abschnitte stellen einige dieser Möglichkeiten vor und beschließen damit die Diskussion zu Workflows.</p>
<div class="section" id="schreiben-uber-das-zmi">
<h5>Schreiben über das ZMI</h5>
<p>Der vielleicht einfachste, aber arbeitsintensivste Weg, einen Workflow zu schreiben, ist der über das ZMI. Auch, wenn das ZMI viele Leute in den Wahnsinn treibt, ist es doch eine einfach Art, die Optionen einzustellen. Leider ist es aber so, dass Sie, nachdem Sie mit dem Schreiben im ZMI angefangen haben, in diesem Paradigma gefangen sind. Mit anderen Worten: Es gibt keinen einfachen Weg, diesen Workflow im Dateisystem zu bearbeiten oder zu verändern. Natürlich habe ich aber weiter oben in diesem Kapitel beschrieben, wie Sie einen Workflow über das Web bearbeiten können.
Eine andere Alternative ist, den Workflow in einem UML-Tool als Zustandsdiagramm zu erstellen und mit ArchGenXML (siehe Kapitel 13) zu generieren.</p>
<p>Um einen Workflow aus dem ZMI zu exportieren, klicken Sie auf <em>portal_workflow</em> und dann auf den <em>Contents</em>-Reiter. Wählen Sie die gewünschten Workflows aus, indem Sie die Kästchen links im ZMI markieren, und klicken Sie dann auf <em>import/export</em>. Im oberen Teil der Exportseite wählen Sie <em>Download to local machine</em> und klicken auf <em>export</em>. Es wird eine Datei mit der Erweiterung <tt class="docutils literal">.zexp</tt> erzeugt, die gespeichert und weiterverteilt werden kann. Bei der Wahl von XML Format hat die Datei das Format XML (Extensible Markup Language) mit der Erweiterung <tt class="docutils literal">.xml</tt>.</p>
<p>Wenn Sie einen Workflow mit der Erweiterung <tt class="docutils literal">.zexp</tt> oder <tt class="docutils literal">.xml</tt> erhalten, können Sie diesen Workflow sehr einfach in Ihr Plone importieren. Kopieren Sie die Datei im Dateisystem ins Import-Verzeichnis von Zope. Das kann das Verzeichnis von der Wurzelinstanz oder von Zope sein.</p>
<p>Klicken Sie dann auf <em>portal_workflow</em>, wählen Sie den <em>Contents</em>-Reiter, und klicken Sie auf <em>import/export</em>. Im unteren Teil der Seite werden Sie ein kleines Formular sehen, in dem man einen Import-Dateinamen angeben kann. Geben Sie hier den Dateinamen ein, und lassen Sie die Option <em>Take ownership of imported objects</em> markiert. Klicken Sie auf den <em>Import</em>-Button, um den Workflow zu importieren. Nun wird der Workflow importiert und bekommt den Namen, der beim Export angegeben wurde.</p>
</div>
<div class="section" id="workflows-in-python-schreiben">
<h5>Workflows in Python schreiben</h5>
<p>Der vermutlich beliebteste Weg, Workflows zu schreiben, ist der, es direkt in Python zu tun, weil man dies vollständig in Python machen und leicht verteilen kann. Erstellen Sie zuerst ein Python-Modul im Dateisystem. Ganz oben in der Datei importieren Sie die passenden Werkzeuge wie folgt:</p>
<pre class="literal-block">
from Products.CMFCore.WorkflowTool import addWorkflowFactory
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
</pre>
<p>Als Zweites erstellen Sie eine Funktion, die den Workflow erzeugt. Anhang A listet das API zum Schreiben eines Workflows etwas detaillierter auf. Aber Sie könnten auch mogeln und sich all die tollen Beispiele anschauen, die im PloneWorkflow-Projekt der Collective-Sammlung verfügbar sind (<a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>) oder sogar die, die in Plone enthalten sind. Beispiel:</p>
<pre class="literal-block">
def sample(id):
    &quot;&quot;&quot; Sample workflow &quot;&quot;&quot;
    ob = DCWorkflowDefinition(id)
    ob.states.addState('private')
    ob.states.addState('public')
    # add transitions
    return ob
</pre>
<p>Und schließlich registrieren Sie den Workflow wie folgt im System:</p>
<pre class="literal-block">
addWorkflowFactory(sample,
                   id='sample_workflow',
                   title='Sample workflow')
</pre>
<p>Dieses Skript muss nun Teil einer Produkt-Installation werden. Kapitel 12 behandelt das Schreiben und die Installation von Produkten.</p>
<p>Nun gibt es dafür natürlich auch eine Abkürzung namens DCWorkflowDump. Dabei wird für Sie der Code aus dem ZMI genommen und in ein Python-Modul geschrieben. Den Quellcode für DCWorkflowDump finden Sie im Collective-Projekt unter <a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>, aber Sie finden auch eine Zip-Datei des Codes auf der Website zum Plone-Buch unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a>.</p>
<p>Um DCWorkflowDump zu installieren, packen Sie die Datei aus und kopieren das Verzeichnis namens <tt class="docutils literal">DCWorkflowDump</tt> in das <tt class="docutils literal">Products</tt>-Verzeichnis Ihrer Plone-Installation. Dass Sie im richtigen Verzeichnis sind, merken Sie, wenn das <tt class="docutils literal">Products</tt>-Verzeichnis auch das <tt class="docutils literal">DCWorkflow</tt>-Verzeichnis und andere Dinge enthält. Dann starten Sie Ihre Plone-Instanz neu.</p>
<p>Nachdem Sie Plone neu gestartet haben, gehen Sie im ZMI zu dem bestimmten Workflow, wo Sie einen neuen Reiter namens <em>dump</em> bemerken werden. Klicken Sie auf diese Seite, um zu diesem Dump-Schirm zu gelangen, und klicken Sie dann auf <em>Dump it!</em>, um den Workflow auf den Bildschirm zu schreiben. Dabei wird Ihr Workflow für Sie in Python formatiert. Speichern Sie die Datei in Ihr Produkt, und Sie haben eine Python-Datei, die Sie manipulieren können. Dieses Produkt ist ein tolles Werkzeug, weil es Ihnen erlaubt, den Workflow im ZMI zu erstellen und dann mit Hilfe von Python zu verteilen und zu ändern.</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch9.rst">
    <title>9. Sicherheit und Benutzer einstellen</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch9.rst</link>
    <description>Plone verfügt über ein mächtiges und feinmaschiges Sicherheitsmodell. Es enthält eine Unmenge an Optionen für die Sicherheit auf allen Ebenen, d.h., jedes Objekt kann eigene Sicherheitseinstellungen für einen Benutzer, eine Rolle, eine Gruppe usw. haben.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Sicherheit und Benutzer einstellen</h2>
<p>Plone verfügt über ein mächtiges und feinmaschiges Sicherheitsmodell. Es enthält eine Unmenge an Optionen für die Sicherheit auf allen Ebenen, d.h., jedes Objekt kann eigene Sicherheitseinstellungen für einen Benutzer, eine Rolle, eine Gruppe usw. haben.</p>
<p>Um dieses Kapitel in einen größeren Zusammenhang zu setzen, möchte ich Sie mit dem folgenden interessanten Zitat bekannt machen:</p>
<blockquote>
<p><em>&quot;Sicherheit ist schwer zu erreichen.&quot;</em></p>
<p>Jim Fulton, Chefentwickler von Zope</p>
</blockquote>
<p>Das Sicherheitsmodell von Plone ist so mächtig und vielschichtig, dass es sehr schwierig sein kann, es zu verwalten und Fehler darin zu finden. Vermutlich sind aber die richtigen Sicherheitseinstellungen das Wichtigste an einer Plone-Site. Eine Sicherheitslücke auf Ihrer Site ist daher vielleicht der schlimmste Patzer, den Sie machen können. Aus diesem Grund behandle ich die Sicherheit in Plone sehr umfassend.</p>
<p>In diesem Kapitel behandle ich zuerst die gesamte Terminologie und die wichtigsten Schnittstellen, mit denen es Ihre Benutzer zu tun haben werden. Dann zeige ich Ihnen, wie Sie Benutzer und Gruppen über die Plone-Schnittstelle hinzufügen und bearbeiten können. Anschließend gehe ich die wichtigsten Werkzeuge und APIs (Application Programming Interfaces) zur Verwaltung von Benutzer- und Sicherheitseinstellungen durch. Dann behandle ich, wie man Python-Werkzeuge verwendet, um per Skript Änderungen an Benutzern und ihren Eigenschaften vorzunehmen. Und am Ende behandle ich die Sicherheit auf Servern und gehe ausführlicher auf die Benutzerauthentifikation ein. Dabei kommt auch ein detailliertes Beispiel dafür vor, wie man Benutzer von einem LDAP-Server (Lightweight Directory Access Protocol) einbindet.</p>
<div class="section" id="benutzer-verwalten">
<h3>Benutzer verwalten</h3>
<p>Eine der häufigsten Aufgaben für den Administrator einer Plone-Site besteht in der Verwaltung der Mitglieder der Site. Zur Administration zählt dabei normalerweise die Wiedergewinnung von Passwörtern und Änderung von Mitgliedereinstellungen. Über das Web können Sie ziemlich viele Aufgaben erledigen, aber der beste Freund eines jeden Administrators ist natürlich eine Skriptsprache wie Python, um Änderungen en masse durchzuführen. Wenn Sie es mit sehr vielen Benutzern zu tun haben, wird der Abschnitt &quot;Skripten von Benutzern&quot; weiter unten in diesem Kapitel von besonderem Interesse für Sie sein.</p>
<div class="section" id="benutzer-rollen-und-gruppen">
<h4>Benutzer, Rollen und Gruppen</h4>
<p>Zu den wichtigsten Konzepten von Plone gehören Benutzer, Rollen und Gruppen. Bevor ich Ihnen zeige, wie Sie diese bearbeiten können, möchte ich etwas detaillierter darstellen, worum es dabei jeweils geht.</p>
<div class="section" id="benutzer">
<h5>Benutzer</h5>
<p>Eine Person, die eine Plone-Site benutzt, wird auch als <em>Benutzer</em> bezeichnet. Der Benutzer wird von Plone authentifiziert oder auch nicht, wobei nicht authentifizierte Benutzer auch <em>anonyme Benutzer</em> genannt werden. Authentifizierte Benutzer sind mit einem vorhandenen Benutzerkonto angemeldet. Sollten sie kein Konto haben, dann können sie sich normalerweise selbst eines erstellen.</p>
<p>Anonyme Benutzer stellen insofern die <em>niedrigste</em> Stufe von Benutzern dar, als für sie die meisten Einschränkungen gelten. Sobald sich Benutzer anmelden, erhalten sie die Rollen zugewiesen, die ihnen ihr Konto zuordnet. Benutzer werden mit einem kurzen Bezeichner identifiziert, z.B. <tt class="docutils literal">andym</tt>. Standardmäßig werden in Plone keine Benutzer für Sie erstellt, bis auf den einen, der vom Installationsprogramm in Zope hinzugefügt wird, damit Sie einen Administratorzugriff haben. Dessen Name hängt davon ab, was Sie im Installationsprogramm angegeben haben. Meistens ist das <tt class="docutils literal">admin</tt>.</p>
</div>
<div class="section" id="rollen">
<h5>Rollen</h5>
<p>Zu einer Plone-Site gehört eine Reihe von <em>Rollen</em>, womit eine logische Kategorisierung von Benutzern gemeint ist. Anstatt die Rechte aller Benutzer individuell einzustellen, werden die Rechte an jede Rolle einzeln zugewiesen. Jedem Benutzer können keine oder mehrere Rollen zugewiesen werden. Ein Benutzer kann z.B. ein Mitglied und ein Manager sein. Jede Rolle wird mit einem einfachen eindeutigen Namen, z.B. <tt class="docutils literal">Member</tt>, bezeichnet.</p>
<p>Eine Plone-Site enthält fünf vordefinierte Rollen, die in zwei Gruppen unterteilt sind: zuweisbare und nicht zuweisbare Rollen. Zuweisbare Rollen können Sie an Benutzer zuweisen, die diese Rollen ab dem Zeitpunkt ihrer Anmeldung erhalten. Nicht zuweisbare Rollen sind solche, die bereits in einer Plone-Site vorhanden sind und nicht einem bestimmten Benutzer zugewiesen werden können. Die Rolle eines anonymen Benutzers weisen Sie einem Benutzer beispielsweise nicht zu.</p>
<p>Es gibt folgende nicht zuweisbare Rollen:</p>
<ul class="simple">
<li><strong>Anonymous</strong>: Dies ist ein Benutzer, der nicht in der Site angemeldet ist. Das könnte ein Benutzer sein, der kein Konto hat, oder einer, der noch nicht angemeldet ist.</li>
<li><strong>Authenticated</strong>: Diese Rolle haben alle in der Site angemeldeten Benutzer,  egal, welche Rolle sie sonst noch haben. Per definitionem ist ein Benutzer entweder anonym oder authentifiziert, aber nicht beides gleichzeitig. Da ein authentifizierter Benutzer sehr wenige Abstufungsmöglichkeiten bietet, wird bei den meisten Anwendungen vom Gebrauch dieser Rolle abgeraten.</li>
</ul>
<p>Folgende zuweisbare Rollen gibt es:</p>
<ul class="simple">
<li><strong>Besitzer (Owner)</strong>: Diese spezielle Rolle erhalten Benutzer, wenn sie ein Objekt erzeugen. Sie gilt für einen Benutzer nur bei diesem Objekt, und die entsprechende Information wird auf dem Objekt selbst gespeichert. Normalerweise weisen Sie diese Rolle nicht explizit zu, sondern Plone macht das für Sie.</li>
<li><strong>Mitglieder (Members)</strong>: Dies ist die Standardrolle von Benutzern, die sich auf Ihrer Site registriert haben. Alle, die sich mit dem <em>Mitglied werden</em>-Button über die Plone-Schnittstelle registrieren, haben diese Rolle.</li>
<li><strong>Redakteure (Reviewer)</strong>: Dies ist ein Benutzer mit mehr Rechten als ein Mitglied, aber weniger Rechten als ein Manager. Redakteure sind Benutzer, die von anderen Mitgliedern eingegebene Inhalte bearbeiten oder prüfen können. Sie können aber nicht die Konfiguration der Site oder ein Benutzerkonto verändern.</li>
<li><strong>Manager</strong>: Manager dürfen auf einer Plone-Site fast alles machen, d.h., Sie sollten diese Rolle nur an vertrauenswürdige Entwickler und Administratoren vergeben. Ein Manager darf Inhalte löschen oder bearbeiten, Benutzer entfernen, die Site-Konfiguration ändern und sogar Ihre Plone-Site löschen.</li>
</ul>
</div>
<div class="section" id="gruppen">
<h5>Gruppen</h5>
<p>Das Konzept von Gruppen unterscheidet sich von dem von Rollen. Rollen bedeuten, dass ein Benutzer mit einer Rolle andere Rechte hat als jemand mit einer anderen Rolle, während eine <em>Gruppe</em> eine logische Kategorisierung von Benutzern darstellt. Die Marketing-Abteilung könnte z.B. eine Gruppe sein und die Entwicklungsabteilung eine andere. Jeder Benutzer kann zu keiner oder vielen Gruppen gehören. Gruppen sind optional, d.h., Sie müssen keine Gruppen verwenden, aber das Plone-Team fand Gruppen sehr nützlich und hat sie daher eingebaut.</p>
<p>Die Entwickler einer Site können Gruppen nach Belieben benutzen, beispielsweise, um eine Abteilung oder eine bestimmte Art von Benutzern zu gruppieren. Den meisten Benutzern, die Plone zum ersten Mal einsetzen, rate ich, die Gruppen unverändert zu lassen. Per Voreinstellung werden keine Gruppen für Sie erzeugt.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Gruppen implementieren Sie mit dem <em>Group User Folder</em> (GRUF). Diese Gruppen sind kein Teil von Zope, sondern ein extra Werkzeug für Plone. GRUF wurde von Ingeniweb entwickelt und zur Verfügung gestellt.</p>
</div>
</div>
</div>
<div class="section" id="der-zugriffsrechte-reiter">
<h4>Der Zugriffsrechte-Reiter</h4>
<p>Als ich in Kapitel 3 die Veröffentlichung von Dokumenten behandelt habe, habe ich den <em>Zugriffsrechte</em>-Reiter übersprungen, weil er eine Einrichtung für Fortgeschrittene ist, die Sie vermutlich nicht immer verwenden möchten. Der <em>Zugriffsrechte</em>-Reiter ist eine Aktion in <tt class="docutils literal">portal_actions</tt>, d.h., wenn Sie nicht möchten, dass er erscheint, gehen Sie im Zope Management Interface (ZMI) zu diesem Werkzeug und deaktivieren die Option <em>visible</em>. Der <em>Zugriffsrechte</em>-Reiter ist aber recht nützlich, weil Sie damit Benutzern und Gruppen verschiedene lokale Rollen an einem Objekt in Plone zuweisen können.</p>
<p>Wenn Sie einen Inhalt in einer Plone-Site hinzugefügt haben und Sie möchten, dass eine andere Person diesen Inhalt bearbeiten kann, müssen Sie ihr mehr Rechte an diesem einen Objekt geben. Das bezeichnet man als eine <em>lokale Rolle</em>, die Ihnen erlaubt, einem Benutzer weitergehende Rechte an einem Objekt zu geben. Wenn ich ein Dokument in Plone schreibe, werde ich zum Besitzer dieses Dokuments und erhalte gewisse Rechte. Wenn ich vor der Veröffentlichung mit meinem Kollegen Ralf an diesem Dokument zusammenarbeiten möchte, dann muss ich Ralf erweiterte Rechte geben, damit er das Dokument bearbeiten darf. Dazu gehe ich zum <em>Zugriffsrechte</em>-Reiter und gebe meinem Kollegen dort die nötigen Rechte.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Lokale Rollen können Sie auf einem Ordner oder einem Dokument vergeben. Wenn Sie einem Benutzer eine lokale Rolle an einem Ordner geben, dann erhält er diese lokale Rolle auf allen Objekten in diesem Ordner.</p>
</div>
<p>Der <em>Zugriffsrechte</em>-Reiter erscheint nur dort, wo Sie das Recht haben, Zugriffsrechte zu verändern, dazu gehört ihr eigener Ordner. Klicken Sie erst auf <em>Mein Ordner</em> und dann auf <em>Zugriffsrechte</em>. Abbildung 9.1 zeigt, wie das Formular für den <em>Zugriffsrechte</em>-Reiter aussieht. Es besteht aus drei Hauptkomponenten, in denen Sie einem Benutzer oder einer Gruppe eine lokale Rolle an diesem Objekt zuordnen können. Hier sehen Sie auch, wer bereits bestimmte Rollen hat.</p>
<a class="reference external image-reference" href="img/09-01.png/image_view_fullscreen"><img alt="img/09-01.png" class="original" src="img/09-01.png" /></a>
<p>Abbildung 9.1. Zugriff auf den <em>Zugriffsrechte</em>-Reiter</p>
<p>Um einen Benutzer zu finden, dem die Rolle zugewiesen werden soll, geben Sie einen Suchbegriff ein, z.B. <em>Gavin</em>, wobei eine Liste von Benutzern geöffnet wird, die Ihrem Suchkriterium entsprechen. Dann können Sie den Benutzer anklicken und im Dropdown-Menü die gewünschte Rolle auswählen. In Abbildung 9.2 gebe ich dem Benutzer Gavin die Rolle des Besitzers an diesem Ordner.</p>
<a class="reference external image-reference" href="img/09-02.png/image_view_fullscreen"><img alt="img/09-02.png" class="original" src="img/09-02.png" /></a>
<p>Abbildung 9.2. Zuweisen einer Rolle an einen Benutzer</p>
<p>In meinem vorigen Beispiel wollte ich Rechte an einen einzelnen Benutzer zuweisen, aber bei einer großen Anzahl von Benutzern kann das sehr umständlich sein, es sei denn, Sie haben ihnen Gruppen zugewiesen. Wenn ich möchte, dass die gesamte Marketing-Abteilung mein Dokument bearbeiten darf, kann ich das damit tun. Um die verfügbaren Gruppen zu erhalten, klicken Sie auf <em>View groups</em>, wonach eine Liste von Gruppen für diese Site geöffnet wird und Sie eine lokale Rolle an eine Gruppe zuweisen können. In Abbildung 9.3 gebe ich der Entwicklungsabteilung die Rolle des Besitzers an diesem Ordner.</p>
<a class="reference external image-reference" href="img/09-03.png/image_view_fullscreen"><img alt="img/09-03.png" class="original" src="img/09-03.png" /></a>
<p>Abbildung 9.3. Zuweisen einer Rolle an eine Gruppe</p>
<p>In Abbildung 9.4 können Sie schließlich sehen, welche Benutzer und Gruppen Rollen mit Rechten an dieser Seite haben, um sie bei Bedarf zu entfernen. Sobald Sie jemand anderem einmal eine lokale Rolle an einem Objekt gegeben haben, erlauben Sie ihm, den <em>Zugriffsrechte</em>-Reiter zu öffnen. Dann hindert ihn nichts mehr daran, Rollen für Sie aus dem Inhalt zu entfernen.</p>
<a class="reference external image-reference" href="img/09-04.png/image_view_fullscreen"><img alt="img/09-04.png" class="original" src="img/09-04.png" /></a>
<p>Abbildung 9.4. Rollen anzeigen und entfernen</p>
</div>
<div class="section" id="administration-uber-das-web">
<h4>Administration über das Web</h4>
<p>Über die Plone-Schnittstelle können Sie sehr einfach den Benutzer verändern, der gewissen Gruppen zugewiesen worden ist, Benutzerangaben ändern, Gruppen hinzufügen usw. Das meiste hiervon können Sie über das Plone-Control Panel erledigen. Klicken Sie einfach auf <em>Plone Konfiguration</em>, und wählen Sie dann <em>Benutzer- und Gruppenverwaltung</em>. Dann sehen Sie zwei Reiter: <em>Benutzer</em> und <em>Gruppen</em>.</p>
<p>Klicken Sie auf den <em>Benutzer</em>-Reiter, um an die Liste der Benutzer auf dem System zu kommen. Das Formular ist ziemlich selbsterklärend: Sie können einen Benutzer entfernen, ein Passwort neu setzen (dadurch erhält der Benutzer ohnehin eine E-Mail) oder eine E-Mail ändern, und all das aus dem Formular heraus, das Sie in Abbildung 9.5 sehen.</p>
<a class="reference external image-reference" href="img/09-05.png/image_view_fullscreen"><img alt="img/09-05.png" class="original" src="img/09-05.png" /></a>
<p>Abbildung 9.5. Benutzer bearbeiten</p>
<p>Durch einen Klick auf einen Benutzer kommen Sie zum Formular mit den Benutzereinstellungen dieses Benutzers, können dort Änderungen vornehmen und dann auf <em>Speichern</em> klicken. Um einen neuen Benutzer hinzuzufügen, klicken Sie auf <em>Neuen Benutzer hinzufügen</em>. Danach wird das Formular für die Registrierung des Benutzers geöffnet, wo Sie auch Angaben zu diesem Benutzer ändern können. Da die Anzahl von Benutzern auf einer Site ziemlich groß werden kann, werden die Daten in der vertrauten Plone-Art auf mehrere kurze Listen aufgeteilt. Sie können einen Suchbegriff eingeben, mit dem über alle Benutzer hinweg nach jenen mit den passenden Namen und E-Mail-Adressen gesucht wird.</p>
<p>Wenn Sie auf den <em>Gruppen</em>-Reiter klicken, können Sie Gruppen hinzufügen, bearbeiten und löschen. Eine Gruppe fügen Sie hinzu, indem Sie auf den Button <em>Neue Gruppe hinzufügen</em> klicken. Danach wird ein Formular für eine Gruppe geöffnet. Darin ist <em>Name</em> das einzige Feld, das ausgefüllt werden muss. Dieser Name sollte ein kurzer beschreibender Name der Gruppe sein. Normalerweise hat eine Gruppe immer direkt mit einer Geschäfts- oder Site-Aktivität zu tun.</p>
<p>Jetzt, wo Sie eine Gruppe mit einigen Benutzern erstellt haben, können Sie Benutzer und Gruppen einander zuordnen, und zwar mit dem Plone-Control Panel. Sie können entweder einen Benutzer anklicken und diesem einige Gruppen zuordnen oder eine Gruppe anklicken und ihr Benutzer zuordnen.</p>
<div class="section" id="wann-benutzt-man-gruppen">
<h5>Wann benutzt man Gruppen?</h5>
<p>Gruppen sind optional, und vielleicht werden Sie nie einen Bedarf für Gruppen haben. Ein wichtiger Anwendungsfall von Gruppen besteht allerdings im Anlegen eines <em>Workspaces</em>. In einer einfachen Plone-Site können Benutzer Inhalte in ihrem eigenen Ordner anlegen und bearbeiten. Jedes dort erstellte Element hat diejenige Person als Besitzer, die es erstellt hat. Aber leider skaliert das nicht besonders gut. Und schließlich geht es eigentlich darum, dass einige Leute ein Dokument zusammen bearbeiten und austauschen können!</p>
<p>An dieser Stelle kommen Gruppen und Workspaces ins Spiel. So wie es einen Ordner für Mitglieder gibt, der alle Benutzerordner der Mitglieder enthält, gibt es auch einen Ordner namens <tt class="docutils literal">GroupWorkspaces</tt>. Dieser wird standardmäßig immer dann erstellt, wenn eine Gruppe hinzugefügt wird, und in diesem Ordner gibt es für jede Gruppe einen weiteren Ordner. Wenn Sie also eine Gruppe namens <em>Marketing</em> hinzufügen, können Sie einen Ordner unter <tt class="docutils literal">GroupWorkspaces/Marketing</tt> finden. Alle Benutzer in der Marketing-Gruppe haben das Recht, Inhalte im <tt class="docutils literal">Marketing</tt>-Workspace zu erstellen, zu bearbeiten und zu löschen. Mit anderen Worten: Sie haben nun einen Ordner für diese Gruppe. Das ist gleichbedeutend damit, eine Gruppe hinzuzufügen und ihrem Ordner dann eine lokale Rolle zuzuordnen.</p>
<p>Das ist nur ein Beispiel dafür, wie nützlich eine Gruppe sein kann. Ein anderes wäre die Benutzung von Gruppen beim Workflow. Im vorigen Kapitel habe ich den Workflow erläutert und gezeigt, wie Sie eine E-Mail an bestimmte Leute schicken können, wenn irgendetwas passiert. Wenn z.B. ein Mitglied der Marketing-Gruppe ein Objekt hinzufügt, können Sie eine E-Mail an alle Benutzer dieser Gruppe schicken, statt einfach an alle Benutzer. Im Abschnitt &quot;Die anderen Benutzer einer Gruppe bestimmen&quot; lernen Sie, wie Sie das machen.</p>
<p>Auf der Plone-Website z.B. sind die Benutzer verschiedenen Entwicklergruppen zugeordnet, die für verschiedene Teile von Plone verantwortlich sind, z.B. dem Release-Team und dem Dokumentations-Team.</p>
</div>
<div class="section" id="gruppen-verwalten">
<h5>Gruppen verwalten</h5>
<p>Aus dem Plone-Control Panel heraus können Sie Gruppen auf zwei verschiedene Arten verwalten. Entweder gehen Sie zu einem Benutzer und klicken auf seine Gruppen, oder Sie gehen zu einer Gruppe und klicken auf ihre Benutzer. Auf beide Arten können Sie ganz einfach Gruppen zu einem Benutzer hinzufügen und entfernen. Um einen Benutzer zu einer Gruppe hinzuzufügen, gehen Sie besser zur Benutzersuchseite und klicken erst auf einen Benutzer und dann den Gruppen-Reiter, der die entsprechenden Gruppen anzeigt. In Abbildung 9.6 sehen Sie z.B. die Gruppen des Benutzers <tt class="docutils literal">andym</tt>.</p>
<a class="reference external image-reference" href="img/09-06.png/image_view_fullscreen"><img alt="img/09-06.png" class="original" src="img/09-06.png" /></a>
<p>Abbildung 9.6. Gruppen für diesen Benutzer</p>
<p>Um den Benutzer zu einer neuen Gruppe hinzuzufügen, markieren Sie das Kästchen für die Gruppe und klicken dann auf <em>Füge den Benutzer der selektierten Gruppe hinzu</em>.</p>
<p>Umgekehrt können Sie einen Benutzer aus einer Gruppe entfernen, indem Sie das Kästchen neben der Gruppe markieren und auf <em>Selektierte Gruppen löschen</em> klicken. Eine ähnliche Schnittstelle sehen Sie bei der Gruppenverwaltung, wenn Sie auf <em>Plone Konfiguration</em>, <em>Gruppen- und Benutzerverwaltung</em> und <em>Gruppen</em> klicken. Klicken Sie auf eine Gruppe und dann auf <em>Gruppenmitglieder</em>, und Sie erhalten eine Liste von Mitgliedern in dieser Gruppe, in der Sie Mitglieder hinzufügen und löschen können.</p>
</div>
<div class="section" id="rollen-an-gruppen-vergeben">
<h5>Rollen an Gruppen vergeben</h5>
<p>Nun haben Sie gesehen, dass sowohl Benutzer als auch Gruppen Rollen haben können. Das mag Ihnen seltsam vorkommen, aber denken Sie z.B. an eine Gruppe von Vorgesetzten, die alles mit dem Inhalt tun können muss, der von einem ihrer Mitarbeiter erstellt worden ist. Um das auf der Site zu machen, müssen Sie die Rolle eines Redakteurs haben. Um eine Gruppe von Vorgesetzten einzurichten, klicken Sie auf <em>Plone Konfiguration</em>, <em>Benutzer- und Gruppenverwaltung</em>, <em>Gruppen</em> und dann auf <em>Neue Gruppe hinzufügen</em>. Nennen Sie diese Gruppe <strong>Vorgesetzter</strong>, und füllen Sie das Formular vollständig aus. Im nächsten Formular erhalten Sie eine Liste der Gruppen und der ihnen zugeordneten Rollen. Um dieser Gruppe die Rolle des Redakteurs zuzuweisen, wählen Sie die Kontrollkästchen, die der Redakteursrolle für diese Gruppe entsprechen, wie in Abbildung 9.7 zu sehen ist.</p>
<a class="reference external image-reference" href="img/09-07.png/image_view_fullscreen"><img alt="img/09-07.png" class="original" src="img/09-07.png" /></a>
<p>Abbildung 9.7. Einrichten der Redakteursrolle für die Gruppe Vorgesetzter</p>
<p>Sie haben es leicht gemacht, den Benutzern die Redakteursrolle zuzuweisen, und nun können Sie die Redakteure über die Plone-Schnittstelle verwalten. Außerdem ist es einfach, die Redakteure mit einem Programm zu bestimmen, weil Sie die Gruppe untersuchen und eine Liste ihrer Mitglieder erhalten können.</p>
<p>Die Idee von Gruppen mit Rollen ist tatsächlich ein kleiner Paradigmenwechsel, verglichen mit der normalen Entwicklung unter Zope, weil Sie dort an individuelle Benutzer gewöhnt sind, denen Rollen zugeordnet sind. Das können Sie in Plone natürlich weiterhin haben, aber die Zuweisung von Rollen an Gruppen ist in Plone sehr einfach.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Per definitionem gilt: Bei der Bestimmung der Rechte eines Benutzers an einem Objekt werden verschiedene Faktoren berücksichtigt. Erstens werden die Rollen bestimmt, die einem Benutzer zugewiesen sind. Zweitens werden die Rollen bestimmt, die ein Benutzer aus seinen Gruppen erhält. Daraus ergibt sich die Gesamtmenge an Rollen, über die ein Benutzer verfügt.</p>
</div>
</div>
</div>
</div>
<div class="section" id="registrierungswerkzeuge-fur-benutzer">
<h3>Registrierungswerkzeuge für Benutzer</h3>
<p>Um Mitglied auf Ihrer Site zu werden, müssen sich Benutzer darauf erst einmal registrieren. Benutzer können sich sehr leicht selbst registrieren, indem sie in der oberen rechten Ecke einer Plone-Site auf <em>Mitglied werden</em> klicken. Das habe ich detailliert am Anfang von Kapitel 3 behandelt. Der Registrierungsvorgang für Benutzer ist ziemlich einfach, aber es sind einige Optionen dabei verfügbar. Dieser Vorgang wird von drei wichtigen Werkzeugen gesteuert: <tt class="docutils literal">portal_registration</tt>, <tt class="docutils literal">portal_memberdata</tt> und <tt class="docutils literal">portal_membership</tt>, die nun in den folgenden Abschnitten vorgestellt werden.</p>
<div class="section" id="portal-registrierung">
<h4>Portal-Registrierung</h4>
<p>Das Werkzeug <tt class="docutils literal">portal_registration</tt> bietet eine wichtige Aktion in Plone an, nämlich ein Mitglied zu werden. Ein Klick auf diesen Link öffnet das Mitglied-Formular. Standardmäßig können alle noch nicht angemeldeten Benutzer (auch anonyme) diesen Link anklicken, um Mitglied zu werden.</p>
<p>Wenn sich Benutzer mit dem Mitglied-Formular registrieren, haben sie zwei einfache Möglichkeiten für eine Plone-Site: mit und ohne Validierung einer E-Mail-Adresse. Die einzig wahre Methode, eine E-Mail-Adresse zu validieren, besteht darin, eine Nachricht an diese Adresse zu schicken, um zu sehen, ob eine entsprechende Antwort zurückkommt. Die Validierung von E-Mails ist standardmäßig ausgeschaltet, d.h., wenn Benutzer sich registrieren, geben sie per Voreinstellung ihren Namen, E-Mail und Passwort in Plone ein. Dann können sie sich anmelden und die Site ganz normal benutzen. Dieses Formular haben Sie in Kapitel 3 gesehen. Falls aber die Validierung von E-Mail-Adressen eingeschaltet ist, können die Benutzer nur einen Namen, einen Benutzernamen und eine E-Mail-Adresse eingeben, wie in Abbildung 9.8 zu sehen ist.</p>
<a class="reference external image-reference" href="img/09-08.png/image_view_fullscreen"><img alt="img/09-08.png" class="original" src="img/09-08.png" /></a>
<p>Abbildung 9.8. Benutzer-Registrierung mit eingeschalteter E-Mail-Validierung</p>
<p>Nach einem Klick auf den Link in der erhaltenen E-Mail, gelangen die Benutzer zu einem Anmeldeschirm, und der Registrierungsvorgang wird normal fortgesetzt.</p>
<p>Um die Validierung in der Plone-Schnittstelle zu aktivieren, klicken Sie auf <em>Plone Konfiguration</em> und <em>Portal Einstellungen</em>. Unter <em>Passworteigenschaften</em> wählen Sie <em>Erzeuge für das Mitglied ein Passwort und sende es ihm per E-Mail zu.</em> und klicken auf <em>Speichern</em>, um die Änderung zu bestätigen.</p>
<p>Wenn Sie die an Benutzer verschickte E-Mail anschauen oder bearbeiten möchten, können Sie das Page Template bearbeiten, aus dem sie erzeugt wird. Sie finden das Template, indem Sie auf <em>plone_skins</em>, <em>plone_templates</em> und <em>registered_notify_template</em> klicken.</p>
<div class="sidebar">
<p class="first sidebar-title">Hinzufügen einer Aktion für Benutzer</p>
<!-- If you want to add any other actions for users, before they join, this is the best place to add them. For example, if you wanted to add a page that outlined a privacy policy, this could be a good place. To do this, first add the page and all the information you want to contain in that policy. It would make sense to make the ID of the page something useful, such as *privacy.html*, and put it in the root of your Plone site. -->
<p>Wenn Sie weitere Aktionen für Benutzer hinzufügen möchten, bevor diese Mitglied werden, ist das der beste Ort dafür. Wenn Sie z.B. eine Seite hinzufügen möchten, die Angaben zum Datenschutz macht, wäre hier ein guter Platz dafür. Fügen Sie dazu erst die Seite mit allen erforderlichen Angaben hinzu. Es wäre sinnvoll, dieser Seite eine nützliche ID zu geben, z.B. <tt class="docutils literal">datenschutz.html</tt>, und sie in dem Wurzelobjekt Ihrer Plone-Site zu platzieren.</p>
<!-- In the ZMI, go to *portal_registration* and add a new action with the following information: -->
<p>Gehen Sie im ZMI zu <tt class="docutils literal">portal_registration</tt>, und fügen Sie eine neue Aktion mit den folgenden Angaben hinzu:</p>
<pre class="literal-block">
Name: Datenschutz
Id: datenschutz
Action: string: ${portal_url}/datenschutz.html
Condition: not: member
Permission: Add portal member
Category: user
Visible: angehakt
</pre>
<!-- You'll now get the privacy link to your privacy page, if aren't logged in. By making the category *user*, you'll ensure it appears in the personal bar. -->
<p class="last">Nun erhalten Sie einen Link auf Ihre Datenschutz-Seite, wenn Sie noch nicht angemeldet sind. Durch die Wahl von <em>user</em> als Kategorie stellen Sie sicher, dass der Link in der persönlichen Leiste erscheint.</p>
</div>
</div>
<div class="section" id="mitgliederdaten">
<h4>Mitgliederdaten</h4>
<p>Das Werkzeug <tt class="docutils literal">portal_memberdata</tt> enthält die Mitgliederdaten aller Benutzer. Zu einem Plone-Benutzer gibt es eine Reihe von Angaben, z.B. zu Skins, zum Zeitpunkt seiner letzten Anmeldung, zu seinem WYSIWYG-Editor (What You See Is What You Get) usw. Wenn ein Benutzer Mitglied auf einer Site wird, wird ein Standarddatensatz in <tt class="docutils literal">portal_memberdata</tt> erzeugt. Die tatsächlich darin erzeugten Eigenschaften sehen Sie mit diesem Werkzeug. Klicken Sie auf <em>portal_memberdata</em>, und wählen Sie <em>Properties</em>, um die Eigenschaften im Standarddatensatz zu sehen. In Plone sind das folgende:</p>
<ul class="simple">
<li><strong>e-mail</strong>: Die E-Mail-Adresse des Benutzers.</li>
<li><strong>portal_skin</strong>: Diese Eigenschaft ist veraltet und sollte ignoriert werden.</li>
<li><strong>listed</strong>: Zeigt diesen Benutzer im <tt class="docutils literal">Members</tt>-Ordner an (boolescher Wert). Ist standardmäßig eingeschaltet.</li>
<li><strong>login_time</strong>: Das Datum der letzten Anmeldung in dieser Sitzung des Benutzers.</li>
<li><strong>last_login_time</strong>: Das Datum der letzten Anmeldung des Benutzers.</li>
<li><strong>fullname</strong>: Der vollständige Name des Benutzers.</li>
<li><strong>error_log_update</strong>: Wird vom Fehlerprotokollformular verwendet, bitte ignorieren.</li>
<li><strong>formtooltips</strong>: In älteren Versionen von Plone gab es Optionen für die Anzeige von Hilfen in Formularen. Heute ist das nicht mehr relevant, daher bitte ignorieren.</li>
<li><strong>visible_ids</strong>: Zeigt die IDs (oder Namen) von Objekten an. Wenn aktiviert, ist das erste Feld im <em>Bearbeiten</em>-Formular aller Inhaltstypen immer der Name. Durch eine Änderung des Namens können Benutzer ein Objekt umbenennen. Ist per Voreinstellung aktiviert.</li>
<li><strong>wysiwyg_editor</strong>: Der Editor, der in Formularen benutzt werden soll.</li>
</ul>
<p>Über die Zope-Schnittstelle können Sie Einträge in dieser Liste hinzufügen oder löschen. Allerdings wird dabei nicht automatisch das Formular der Benutzerschnittstelle neu erstellt, das die Benutzer tatsächlich bearbeiten. In Kapitel 3 haben Sie gesehen, dass Benutzer durch einen Klick auf <em>Meine Einstellungen</em> auf die meisten dieser Eigenschaften zugreifen und sie ändern können. Wenn Sie diese Einstellungen ändern möchten, müssen Sie dieses Formular anpassen. Die in diesen Feldern angegebenen Werte sind voreingestellte Werte für einen neu registrierten Benutzer. Standardmäßig werden z.B. alle Mitglieder unter dem Mitglieder-Reiter aufgelistet, außer die Benutzer entscheiden sich explizit dagegen.</p>
<p>Wenn Sie z.B. nicht möchten, dass alle Mitglieder standardmäßig bei der Suche aufgelistet werden, müssen Sie die Einstellung in diesem Formular ändern. Finden Sie im Formular <tt class="docutils literal">portal_memberdata</tt> die Eigenschaft <tt class="docutils literal">listed</tt>, und entfernen Sie die Markierung für den Wert im Formular. Klicken Sie dann auf <em>Save Changes</em>, und alle neuen Benutzer werden nicht mehr aufgelistet.</p>
<p>Das Werkzeug <tt class="docutils literal">portal_groupdata</tt> enthält die entsprechenden Daten für Gruppen. Die Standardeigenschaften bei einer Gruppe lauten wie folgt:</p>
<ul class="simple">
<li><strong>title</strong>: Ein Titel der Gruppe</li>
<li><strong>description</strong>: Eine Beschreibung der Gruppe</li>
<li><strong>email</strong>: Eine E-Mail-Adresse</li>
<li><strong>listed</strong>: Angabe, ob die Gruppe für Benutzer aufgelistet wird</li>
</ul>
<p>Diese Werkzeuge speichern Benutzer- und Gruppendaten in den Werkzeugen selbst und nicht im <tt class="docutils literal">acl_users</tt>-Ordner. Wenn Sie Benutzerinformationen von einem Plone-Server zum anderen verschieben möchten, müssen Sie diese Werkzeuge ebenfalls verschieben. Nur den <tt class="docutils literal">acl_users</tt>-Ordner zu verschieben reicht nicht aus. Das können Sie tun, indem Sie diese Werkzeuge importieren und exportieren. Vor dem Import auf der neuen Plone-Site müssen Sie allerdings das existierende Werkzeug löschen, sonst wird ein Fehler ausgelöst.</p>
</div>
<div class="section" id="mitgliedschaften">
<h4>Mitgliedschaften</h4>
<p>Das Werkzeug <tt class="docutils literal">portal_membership</tt> verwaltet einige weitere Eigenschaften. Insbesondere verknüpft es die Mitgliederdaten mit den Mitgliedern. Beim Zugriff per ZMI auf <tt class="docutils literal">portal_membership</tt> haben Sie eine große Anzahl von Optionen, von denen folgende die wichtigsten sind:</p>
<ul class="simple">
<li><strong>Set members folder</strong>: Dies ist der Ordner, der die Mitglieder-Ordner enthält. Dieser Ordner muss vorhanden sein. Per Voreinstellung hat er den Namen <tt class="docutils literal">Members</tt>.</li>
<li><strong>Control creation of member areas</strong>: Standardmäßig wird für jeden Benutzer ein Mitgliederbereich erstellt, wenn er Mitglied wird, was allerdings optional ist. Klicken Sie auf <em>Turn folder creation off</em>, um das abzuschalten. Standardmäßig werden diese Bereiche erstellt.</li>
</ul>
<p>Unter dem <em>Actions</em>-Reiter finden Sie eine Reihe von Aktionen, die bei angemeldeten Benutzern Anwendung finden, z.B. <em>Meine Favoriten</em>, <em>Meine Einstellungen</em> usw. Diese haben alle die Kategorie <em>user</em>, damit die Aktionen in der oberen rechten Ecke erscheinen.</p>
<p>Das Werkzeug <tt class="docutils literal">portal_groups</tt> bietet ähnliche Möglichkeiten wie <tt class="docutils literal">portal_membership</tt>, aber für Gruppen. Auch hier gilt, dass bei der Erzeugung einer Gruppe ein Gruppen-Workspace erstellt wird, in dem alle Mitglieder dieser Gruppe Inhalte erstellen und bearbeiten können.</p>
</div>
<div class="section" id="nutzliche-apis">
<h4>Nützliche APIs</h4>
<p>Das Werkzeug <tt class="docutils literal">portal_membership</tt> enthält einen der meistverwendeten Sätze an API-Funktionen. Oftmals möchten Sie wichtige Informationen herausfinden, wie z.B. den gerade angemeldeten Benutzer, ob der Benutzer anonym ist usw. Das Werkzeug <tt class="docutils literal">portal_membership</tt> bietet Ihnen hierfür die nötigen Methoden, von denen die folgenden am wichtigsten sind:</p>
<ul class="simple">
<li><strong>isAnonymousUser()</strong>: Gibt <tt class="docutils literal">True</tt> zurück, falls der Benutzer anonym ist.</li>
<li><strong>getAuthenticatedMember()</strong>: Gibt den gerade angemeldeten Benutzer mitsamt seinen Eigenschaften aus <tt class="docutils literal">portal_metadata</tt> zurück. Wenn keiner angemeldet ist, wird ein spezieller Benutzer <tt class="docutils literal">nobody</tt> ohne <tt class="docutils literal">portal_metadata</tt>-Eigenschaften zurückgegeben.</li>
<li><strong>listMemberIds()</strong>: Gibt die IDs aller Benutzer zurück.</li>
<li><strong>listMembers()</strong>: Gibt alle Benutzerobjekte zurück.</li>
<li><strong>getMemberById(id)</strong>: Gibt das Benutzerobjekt zu einer gegebenen ID zurück.</li>
<li><strong>getHomeFolder(id=None)</strong>: Gibt den Startordner zu einer gegebenen ID zurück. Die ID ist optional. Ohne ID wird der Startordner des aktuellen Mitglieds zurückgegeben.</li>
<li><strong>getHomeUrl(id=None)</strong>: Gibt eine URL zum Startordner des Mitglieds zurück. Die ID ist optional. Ohne ID wird die URL des Startordners des aktuellen Mitglieds zurückgegeben.</li>
</ul>
<p>Der von diesen Funktionen zurückgegebene Benutzer wird mit Daten aus dem Werkzeug <tt class="docutils literal">portal_memberdata</tt> versehen, so dass diese Eigenschaften Attribute des Benutzerobjekts werden. Das folgende Beispiel ist ein kleines Script (Python)-Objekt, um an die E-Mail-Adresse des Benutzers <tt class="docutils literal">Andy</tt> zu gelangen:</p>
<pre class="literal-block">
##parameters=
u = context.portal_membership.getMemberById(&quot;andy&quot;)
return u.email
</pre>
</div>
<div class="section" id="cookie-authentifikation">
<h4>Cookie-Authentifikation</h4>
<p>Plone verwendet standardmäßig eine Cookie-Authentifikation bei der Identifikation seiner Benutzer, d.h., Benutzer müssen Cookies in ihrem Browser eingeschaltet haben, damit sie sich anmelden können. Diese Authentifikation erfolgt auf einer Plone-Site mit dem Objekt <tt class="docutils literal">cookie_authentication</tt>, das die notwendige Funktionalität für die Anmeldung von Benutzern enthält. Falls Sie wirklich eine HTTP-Authentifikation (Hypertext Transfer Protocol) benutzen möchten, können Sie dieses Objekt einfach löschen. Allerdings möchte ich das wirklich nicht empfehlen, denn bei den meisten Sites eignet sich eine HTTP-Authentifikation nicht.</p>
<p>Dieses Objekt bietet folgende Eigenschaften, die Sie im ZMI bearbeiten können:</p>
<ul class="simple">
<li><strong>Authentication cookie name</strong>: Der Name des Cookies, das für die persistente Benutzer-Authentifikation benutzt wird. Das funktioniert über ein persistentes Token für den Benutzer, das die Anmeldung des Benutzers enthält. Der Standardwert hierfür lautet <tt class="docutils literal">__ac</tt>.</li>
<li><strong>User name form variable</strong>: Der Name der Variablen im Anmeldeformular, die den Benutzernamen enthält. Der Standardwert hierfür ist <tt class="docutils literal">__ac_name</tt>.</li>
<li><strong>User password form variable</strong>: Der Name der Variablen im Anmeldeformular, die das Passwort enthält. Standardwert ist <tt class="docutils literal">__ac_password</tt>.</li>
<li><strong>User name persistence form variable</strong>: Der Name der Variablen im Anmeldeformular, die das persistente Token enthält. Standardwert ist <tt class="docutils literal">__ac_persistent</tt>.</li>
<li><strong>Login page ID</strong>: Wenn ein Benutzer sich anmelden muss, ist dies die Seite, zu der er gelangt, um die Anmeldung abzuschließen. Standardwert ist <tt class="docutils literal">require_login</tt>.</li>
<li><strong>Logout page ID</strong>: Wenn ein Benutzer dabei ist, sich abzumelden, gelangt er zu einer netten Seite mit einer Nachricht darauf. Diese Seite hat diese ID. Der Standardwert ist <tt class="docutils literal">logged_out</tt>.</li>
<li><strong>Failed authorization page ID</strong>: Diese Seite erscheint, wenn die Autorisierung fehlschlägt. Standardmäßig ist sie leer, da Plone etwas anderes macht.</li>
<li><strong>Use cookie paths to limit scope</strong>: Setzt das Cookie lokal zum aktuellen Ordner und allen Ordnern darunter. Lassen Sie hier den Standardwert (leer) stehen, damit Sie sich für die gesamte Site authentifizieren, unabhängig davon, wo Sie auf <em>Einloggen</em> klicken.</li>
</ul>
<p>Um andere Cookies als die Standard-Cookies zu verwenden, ändern Sie einfach den Wert in diesem Formular und klicken auf <em>Save</em>. Aber seien Sie sich bewusst, dass, wenn Sie den Namen des Cookies ändern, alle vorhandenen Cookies auf den Rechnern Ihrer Benutzer ignoriert werden, d.h., die Benutzer müssen sich alle neu anmelden. Wenn Sie gern eine andere Anmeldeseite hätten, könnten Sie entweder das Page Template <tt class="docutils literal">require_login</tt> anpassen oder den Wert dieser Variablen ändern.</p>
</div>
<div class="section" id="der-eigentliche-benutzerordner">
<h4>Der eigentliche Benutzerordner</h4>
<p>Wenn Sie im ZMI auf den Ordner <tt class="docutils literal">acl_users</tt> klicken, erhalten Sie Zugriff auf den eigentlichen Benutzerordner einer Plone-Site. Dabei wird die Schnittstelle zum Gruppenbenutzerordner (GRUF, Group User Folder) geöffnet, in der Sie eine Vielzahl von Optionen haben.</p>
<p>Die GRUF-Schnittstelle ist im Prinzip den Benutzeroptionen recht ähnlich, die Sie im Plone-Control Panel haben. Sie können Benutzer und Gruppen über eine recht einfach zu verstehende Schnittstelle hinzufügen und bearbeiten. Mit einem Klick auf <em>Benutzer</em> und <em>Gruppen</em> können Sie diese Elemente bearbeiten. Wenn Sie auf den <em>Contents</em>-Reiter klicken, erhalten Sie eine Auswahl zwischen Benutzern und Gruppen. Klicken Sie auf <em>Users</em> und dann auf <em>acl_users</em>. Damit kommen Sie schließlich zum aktuellen Benutzerordner eines Benutzers. Dieser sieht wie der Standard-Benutzerordner aus. Sie sehen eine Liste von Benutzern, und um einen davon zu bearbeiten, klicken Sie einfach auf den Benutzernamen, wie in Abbildung 9.9 zu sehen ist.</p>
<a class="reference external image-reference" href="img/09-09.png/image_view_fullscreen"><img alt="img/09-09.png" class="original" src="img/09-09.png" /></a>
<p>Abbildung 9.9. Bearbeiten des Benutzerdatensatzes</p>
<p>An dieser Stelle können Sie das Passwort eines Benutzers oder seine Rollen verändern. Sie werden bemerken, dass hier die Gruppe <em>Management</em> tatsächlich als Rolle dargestellt ist, um sicherzugehen, dass keine Namenskollisionen entstehen. Der Name wird zu <tt class="docutils literal">group_Management</tt> verändert. Wenn Sie aus einem Benutzer ein Mitglied dieser Gruppe machen möchten, könnten Sie das hier tun. Aber Sie können hier nicht viel machen, was Sie nicht auch auf höchster Ebene tun könnten, daher würde ich nicht bis auf diese Stufe hinunter gehen, es sei denn, Sie müssen hier z.B. das Passwort ändern oder eine Domain setzen.</p>
</div>
</div>
<div class="section" id="rechte-setzen">
<h3>Rechte setzen</h3>
<p>Nun haben Sie Benutzer, Rollen und Gruppen kennen gelernt, aber es gibt noch mehr. Die niedrigste Stufe bei Sicherheitseinstellungen sind die Rechte. Wie aus der Bezeichnung hervorgeht, bedeutet, einem Benutzer ein <em>Recht</em> zu geben, dass er die Möglichkeit hat, etwas zu tun, z.B. ein Objekt anzuzeigen, ein Dokument zu erstellen, eine Liste mit dem Inhalt eines Ordners zu bekommen usw. Jedes Recht wird mit einem sinnvollen Namen eindeutig bezeichnet, z.B. <em>View</em>, <em>Add portal content</em> oder <em>List folder contents</em>.</p>
<p>Rechte werden nicht an einen einzelnen Benutzer vergeben, sondern an eine Rolle. Jede Rolle erhält bestimmte Rechte, und dann bekommt der Benutzer bestimmte Rollen. Alle Sicherheitseinstellungen von Zope finden Sie über das ZMI unter dem <em>Security</em>-Reiter. Dazu gehören die Plone-Site, das Zope-Wurzelobjekt, alle Objekte und Inhalte in einer Plone-Site sowie die Skins. Nach einem Klick auf den <em>Security</em>-Reiter können Sie in einem Raster alle Rechte sehen und die Rollen, auf die sie abgebildet werden (siehe auch Abbildung 9.10).</p>
<a class="reference external image-reference" href="img/09-10.png/image_view_fullscreen"><img alt="img/09-10.png" class="original" src="img/09-10.png" /></a>
<p>Abbildung 9.10. Sicherheitseinstellungen</p>
<p>In Abbildung 9.10 sehen Sie, dass dieses Objekt eine Reihe von Sicherheitseinstellungen hat. Angezeigt werden sie in Form eines Rasters mit Kontrollkästchen darin. Links stehen die Rechte in alphabetischer Reihenfolge, und oben stehen die Rollen, ebenfalls in alphabetischer Reihenfolge. Diese Seite ist ziemlich groß und unpraktisch, aber es gibt zwei nützliche Abkürzungen. Klicken Sie auf das Recht, um alle Rollen zu diesem Recht zu erhalten. In Abbildung 9.11 sehen Sie z.B. die Einstellungen zu dem Recht <em>Access future portal content</em>.</p>
<a class="reference external image-reference" href="img/09-11.png/image_view_fullscreen"><img alt="img/09-11.png" class="original" src="img/09-11.png" /></a>
<p>Abbildung 9.11. Rechte-Einstellungen</p>
<p>Und Sie können auf eine Rolle klicken, um alle Einstellungen zu dieser Rolle zu erhalten, was viel einfacher ist, als eine lange Liste (siehe Abbildung 9.12).</p>
<a class="reference external image-reference" href="img/09-12.png/image_view_fullscreen"><img alt="img/09-12.png" class="original" src="img/09-12.png" /></a>
<p>Abbildung 9.12. Einstellungen für die Redakteursrolle</p>
<p>Bei all diesen Rechten muss man einfach nur die Kästchen für die gewünschten Rechte markieren oder die Optionen im Auswahlmenü wählen und auf <em>Save</em> klicken. Falls die Einstellung <em>Acquire Permission</em> gewählt ist, werden die Sicherheitseinstellungen für dieses Recht akquiriert, sonst werden die Rechte nicht akquiriert. <em>Akquisition</em> bezeichnet die Möglichkeit eines Objekts, in der Objekthierarchie nach Rechten zu suchen und die gefundenen Rechte zu den Gesamtrechten zu kombinieren.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Die Rechte-Seite schaltet für Sie die Berechtigungen für den Manager ein. Ihren Manager auszuschließen wäre sehr schlecht, daher ist es nicht verkehrt, sie standardmäßig eingeschaltet zu haben.</p>
</div>
<p>Betrachten Sie nun das Recht <em>Access contents information</em>. Gehen Sie im ZMI zum Plone-Wurzelobjekt, und klicken Sie auf den <em>Security</em>-Reiter. In der Standardeinstellung für dieses Recht sind keine Rollen aktiviert, d.h., die Einstellungen sind für alle Benutzer leer. Allerdings ist die Option <em>Acquire Settings</em> markiert, d.h., Sie müssen in den Elternobjekten in der Hierarchie nachschauen, um die Rechte dieses Objekts zu bestimmen. Gehen Sie nun zum Zope-Wurzelordner, und klicken Sie auf den <em>Security</em>-Reiter. Danach wird die Liste der Rechte für den Wurzelordner geöffnet, in der es ganz bestimmt einige Einstellungen für das Recht <em>Access contents information</em> in diesem Ordner gibt, und zwar haben die Rollen <em>Anonymous</em> und <em>Manager</em> dieses Recht.</p>
<p>Da Rechte akquiriert werden, erhalten alle Unterordner ebenfalls diese Sicherheitseinstellungen. Das heißt, die Plone-Site und jedes Objekt in der Plone-Site wird diese Rechte haben. Wenn Sie also eine Sicherheitseinstellung für die gesamte Site festlegen möchten, müssen Sie lediglich das Recht in dem Plone-Wurzelobjekt konfigurieren, und die meisten Objekte werden diese Rechte akquirieren.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Eine Ausnahme bilden Objekte im Workflow, die die Akquisition explizit ausschalten. Das wird später im Abschnitt &quot;Sicherheit und Workflow&quot; weiter unten in diesem Kapitel beschrieben.</p>
</div>
<p>In Zope können Sie Rechte auf allen Objekten über das ZMI setzen. Das kann das Zope-Wurzelobjekt, eine Plone-Site, ein Ordner wie z.B. der <tt class="docutils literal">Members</tt>-Ordner oder sogar ein gewisser Inhalt sein. Jedes Objekt hat seinen eigenen Satz an Rechten, aber nicht alle Objekte haben die gleiche Auswahl an Rechten. Das Recht <em>Add...</em> haben beispielsweise alle Ordner. Aber da diese Rechte in einem Objekt keinen Sinn machen, das kein Ordner ist (ein Objekt muss per definitionem ein Ordner sein, damit man darin Elemente hinzufügen kann), sind sie nicht vorhanden.</p>
<p>Alle Produkte oder Teile von Python-Code in Ihrer Zope-Site können ihre eigenen Sicherheitsrechte definieren, daher kann es etwas schwierig werden, genau zu definieren, was Sie mit einem bestimmten Recht tun dürfen. Tabelle 9.1 beschreibt einige der wichtigsten Rechte und was sie tun.</p>
<p>Tabelle 9.1. Häufige Plone-Rechte</p>
<table border="1" class="docutils">
<colgroup>
<col width="29%" />
<col width="71%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Recht</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Access contents
information</td>
<td>Dieses Recht erlaubt den Zugriff auf ein Objekt, ohne
das Objekt notwendigerweise anzuzeigen. Ein Benutzer
möchte z.B. den Titel des Objekts in einer
Ergebnisliste sehen, obwohl er den Inhalt der Datei
nicht sehen kann.</td>
</tr>
<tr><td>Add...</td>
<td>Es gibt zahlreiche Rechte beim Hinzufügen, abhängig
vom Typ des Objekts, das ein Benutzer erstellen
möchte. Bei einer normalen Plone-Site sind alle Rechte
zusammen unter <em>Add portal content</em> gruppiert.</td>
</tr>
<tr><td>Add portal member</td>
<td>Hiermit hat man die Möglichkeit, Mitglied einer
Plone-Site zu werden und ein Benutzerkonto zu
bekommen.</td>
</tr>
<tr><td>Copy or Move</td>
<td>Hiermit darf man ein Objekt kopieren oder verschieben.
Auch wenn Benutzer dieses Recht haben, müssen Sie
dennoch das Recht haben, das Objekt an irgendeinem
Ort einzufügen.</td>
</tr>
<tr><td>Delete objects</td>
<td>Hiermit hat man das Recht, ein Objekt zu löschen. Im
normalen Zope wird dieses Recht im Ordner geprüft. In
Plone wird diese Prüfung bei allen Objekten
vorgenommen.</td>
</tr>
<tr><td>List folder contents</td>
<td>Hiermit bekommt man eine Liste des Inhalts eines
Ordners. Dabei wird nicht geprüft, ob Sie das Recht
haben, das aufgelistete Objekt anzuzeigen.</td>
</tr>
<tr><td>List portal members</td>
<td>Hiermit hat man das Recht, eine Mitgliederliste der
Site zu sehen und nach Mitgliedern zu suchen.</td>
</tr>
<tr><td>Modify portal content</td>
<td>Dies ist ein zusammengefasstes Recht für beliebige
Änderungen an einem Objekt, z.B. Ändern des Inhalts,
seiner Stichwörter oder anderer Eigenschaften.
Dieses Recht gilt für fast alle Objekte.</td>
</tr>
<tr><td>Set own password</td>
<td>Hiermit hat man das Recht, sein eigenes Passwort in
einer Plone-Site zu ändern.</td>
</tr>
<tr><td>Set own properties</td>
<td>Hiermit hat man das Recht, seine eigenen Eigenschaften
in einer Plone-Site zu ändern.</td>
</tr>
<tr><td>View</td>
<td>Hiermit dürfen Benutzer das fragliche Objekt anzeigen.
Dabei bedeutet <em>View</em> nicht nur Anzeigen als HTML,
sondern auch via FTP (File Transfer Protocol), WebDAV
und über andere Zugriffsformen.</td>
</tr>
</tbody>
</table>
<div class="section" id="rollen-hinzufugen">
<h4>Rollen hinzufügen</h4>
<p>Rollen an Benutzer zu vergeben bedeutet, dass Sie einen derart kompatiblen Satz von Rechten für jede Rolle finden müssen, dass die Gruppierung der Rechte Sinn macht. Das ist nicht immer möglich. Manchmal braucht ein bestimmter Benutzer etwas anderes als andere Benutzer.</p>
<p>Aus Entwicklersicht ist allerdings alles umso einfacher, je weniger und einfacher Sie Ihre Rollen halten können. Es ist nicht sehr kompliziert, aber es empfiehlt sich nicht, gleich für jede denkbare Sicherheitsoption eine Rolle anzulegen. Sie bringen sich damit selbst sehr schnell in ein großes Schlamassel. Ich möchte Sie stattdessen dazu bringen, die Zahl Ihrer Rollen klein und allgemein genug formuliert für die gesamte Site zu halten.</p>
<p>Um eine Rolle hinzuzufügen, gehen Sie zum Plone-Wurzelordner, klicken auf den <em>Security</em>-Reiter und scrollen zum unteren Seitenende (ein langer Weg). Unten ist ein einfaches Formular, mit dem man weitere Rollen hinzufügen oder Rollen löschen kann. Fügen Sie den Namen einer neuen Rolle hinzu, und klicken Sie auf <em>Add Role</em>.</p>
</div>
<div class="section" id="haufige-aufgaben-erledigen">
<h4>Häufige Aufgaben erledigen</h4>
<p>Einige Sicherheitseinstellungen können Sie schnell und einfach setzen, um häufige Aufgaben zu erledigen. Bevor Sie viele Änderungen an den Sicherheitseinstellungen vornehmen, empfehle ich Ihnen dringend, ein Backup Ihrer Plone-Site anzulegen. Wie Sie das machen, zeige ich Ihnen in Kapitel 14.</p>
<div class="section" id="verhindern-weiterer-site-mitgliedschaften">
<h5>Verhindern weiterer Site-Mitgliedschaften</h5>
<p>Um zu verhindern, dass weitere Benutzer Mitglied auf Ihrer Site werden, benutzen Sie das Recht <em>Add portal member</em> in Ihrer Zope-Wurzel für anonyme Benutzer. Dieses können Sie entweder dort für anonyme Benutzer deaktivieren, oder Sie gehen zu Ihrer Plone-Site und schalten die Einstellung <em>Acquire Permission</em> ab.</p>
</div>
<div class="section" id="blockieren-der-suche-auf-der-site">
<h5>Blockieren der Suche auf der Site</h5>
<p>Um zu verhindern, dass Benutzer auf Ihrer Site suchen können, benutzen Sie in der Wurzel einer Plone-Site das Recht <em>Search ZCatalog</em> für anonyme Benutzer. Ändern Sie also dort das Recht, indem Sie Anonymous oder einen anderen Benutzer deaktivieren.</p>
</div>
<div class="section" id="site-zugriff-fur-anonyme-benutzer-vollig-blockieren">
<h5>Site-Zugriff für anonyme Benutzer völlig blockieren</h5>
<p>Nun ja, anonyme Benutzer gänzlich von Ihrer Site fernzuhalten ist etwas trickreich, weil es ziemlich kompliziert ist, den anonymen Zugriff auf Ihre Site völlig zu blockieren. Die Benutzer müssen weiterhin auf Ihre Site zugreifen können, um sich anzumelden! Was Sie in dieser Situation wirklich möchten, ist den Zugriff auf Ihren Inhalt einzuschränken. Das können Sie mit einer Einschränkung der Rechte in Ihrem Workflow erreichen.</p>
</div>
</div>
<div class="section" id="sicherheit-und-workflow">
<h4>Sicherheit und Workflow</h4>
<p>Wie ich in Kapitel 7 dargestellt habe, wird im Workflow die Sicherheit aller Objekte verwaltet. Das geschieht, indem der Workflow die aktuellen Rechte eines Objekts ändert. Eben habe ich Ihnen gezeigt, wie Sie sich die Sicherheitseinstellungen aller Objekte ansehen können, d.h., Sie können nun sehen, dass sich die Sicherheitseinstellungen von Objekten in einem Zustand von denen in einem anderen Zustand unterscheiden können. Wenn Sie auf <em>portal_workflow</em> klicken, den <em>Contents</em>-Reiter wählen, auf <em>plone_workflow</em> und dann den <em>States</em>-Reiter klicken, werden Sie alle verfügbaren Zustände sehen. Klicken Sie auf einen Zustand, und wählen Sie dann <em>Permissions</em>, um die Rechte für diesen Zustand zu sehen (siehe Abbildung 9.13).</p>
<a class="reference external image-reference" href="img/09-13.png/image_view_fullscreen"><img alt="img/09-13.png" class="original" src="img/09-13.png" /></a>
<p>Abbildung 9.13. Rechte für den Zustand <em>Veröffentlicht</em></p>
<p>Wenn ein Objekt in den Zustand <em>Veröffentlicht</em> kommt, erhalten anonyme Benutzer, wie Sie sehen, die Rechte <em>Access contents information</em> und <em>View</em>. Das heißt, Leute können den Inhalt sehen. Sie werden bemerken, dass Mitglieder oder Besitzer ihren eigenen Inhalt nicht bearbeiten können, weil sie dieses Recht nicht haben. Die vom Workflow angewendeten Rechte werden im <em>Permissions</em>-Reiter gesetzt, wo Sie alle vom Workflow verwalteten Rechte setzen können.</p>
<p>Nachdem Sie die Sicherheitseinstellungen geändert haben, müssen Sie zum Werkzeug <em>plone_workflow</em> gehen und <em>Update security settings</em> klicken, sonst sind Sicherheitseinstellungen und Workflow des Objekts verschieden.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Weil sich die Rechte ändern, wenn das Objekt in einen anderen Zustand übergeht, werden andere Rechteänderungen am Objekt, die Sie evtl. über das ZMI veranlassen, entfernt, wenn diese Rechte vom Workflow verwaltet werden (und nur dann). Aus diesem Grund sollten Sie immer dem Drang widerstehen, kleine Änderungen im ZMI an der Sicherheit von Inhaltstypen vorzunehmen. Bleiben Sie dabei, das Plone-Site-Objekt und den Workflow zu ändern.</p>
</div>
<div class="section" id="wachter">
<h5>Wächter</h5>
<p>Zu allen Übergängen gibt es Wächter, mit denen der Administrator die erlaubten Rechte auswählen kann, bevor ein Benutzer den Übergang ausführen darf. Die Prüfung, ob ein Benutzer den Übergang ausführen darf, findet in dieser Reihenfolge statt: erst die Rechte, dann die Rollen und dann der Ausdruck. Sobald eine dieser Prüfungen positiv ist, wird der Übergang ausgeführt.</p>
<p>Es folgen die Einstellungen eines Wächters:</p>
<ul class="simple">
<li><strong>Permission</strong>: Eine Liste der erlaubten Rechte, mit Semikola (;) voneinander getrennt, z.B. <em>Review portal content; Modify portal content</em>.</li>
<li><strong>Roles</strong>: Eine Liste von erlaubten Rollen bei diesem Übergang, mit Semikola voneinander getrennt, z.B. <em>Manager; Reviewer</em>.</li>
<li><strong>Expression</strong>: Ein Workflow-TALES-Ausdruck (Template Attribute Language Expression Syntax), mit dem Sie eine bestimmte Bedingung ausdrücken können. Der folgende Übergang z.B. findet nur dann statt, wenn er im Ordner namens <tt class="docutils literal">Members</tt> ist. Das ist kein echtes Recht, aber es ist ein netter Trick:</li>
</ul>
<pre class="literal-block">
python: if 'Members' in state_object.getPhysicalPath()
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last"><tt class="docutils literal">getPhysicalPath</tt> ist eine Methode bei allen Objekten in Zope, die den Ort innerhalb der Zope-Objekthierarchie zurückgibt, wobei ein eventuelles virtuelles Hosting ignoriert wird.</p>
</div>
</div>
</div>
<div class="section" id="proxy-rollen">
<h4>Proxy-Rollen</h4>
<p>Im vorigen Kapitel habe ich einige nette Methoden vorgestellt, um Benutzer zu benachrichtigen und um Inhalte zu verschieben, die von einem Workflow erfasst sind. Wenn so etwas passiert, wird das Skript dann ausgeführt, wenn der Benutzer die Workflow-Transaktion ausführt. In diesem Fall könnte Ihr Skript etwas tun, wozu Ihr Benutzer ein Recht hat oder auch nicht. Vielleicht möchten Sie z.B. nicht, dass ein Benutzer etwas in einen Ordner namens <tt class="docutils literal">public</tt> hinzufügen darf, außer über den Workflow. Das stellt ein Problem dar, denn dann müssen Sie sicherstellen, dass das Skript mit einer übergeordneten Rolle ausgeführt werden kann.</p>
<p>Eine <em>Proxy-Rolle</em> ist etwas, womit Ihre Benutzer nicht in Berührung kommen, und wahrscheinlich werden sie auch nichts davon wissen, aber für Sie ist es eine Methode, dieses Problem zu umgehen. Angenommen, Sie möchten z.B., dass ein Benutzer einen anderen Benutzer aus der Liste aller Site-Benutzer finden kann. Sie möchten dem Benutzer nicht das Recht geben, alle Benutzer anzuzeigen, sondern nur dazu, die Benutzer in diesem bestimmten Kontext aufzulisten. Um das Skript auszuführen, braucht ein Benutzer das Recht <em>List portal members</em>, damit er eine Mitgliederliste bekommt, aber das möchten Sie nicht an anonyme Benutzer vergeben.</p>
<p>Das Skript, das diesen Befehl ausführt, muss eine übergeordnete Proxy-Rolle bekommen, wahrscheinlich <em>Member</em>. Gehen Sie dazu im ZMI zu dem Skript, klicken Sie auf den <em>Proxy</em>-Reiter und dann auf <em>Member</em>. Falls dieses Skript als Datei im Dateisystem vorliegt, kann diese Information in der Metadaten-Datei hinzugefügt werden. Die <tt class="docutils literal">.metadata</tt>-Datei hätte z.B. die folgende Zeile: <tt class="docutils literal">proxy = Member</tt>. Nun würde dieses Skript als Mitglied ausgeführt werden, wodurch Ihr Sicherheitsproblem gelöst ist!</p>
</div>
</div>
<div class="section" id="skripten-von-benutzern">
<h3>Skripten von Benutzern</h3>
<p>Jetzt haben Sie also eine Menge Benutzer in Ihrer Plone-Site, und daher benötigen Sie einige Skripten auf dieser Site, um Ihnen die Verwaltung der Benutzer zu erleichtern. Ab einigen hundert Benutzern kann es sehr schwer werden, Änderungen über das Web vorzunehmen, daher enthalten die folgenden Abschnitte ein paar Beispielskripten, die einige wichtige Aufgaben erledigen.</p>
<div class="section" id="benutzer-en-masse-registrieren">
<h4>Benutzer en masse registrieren</h4>
<p>Wenn Sie eine große Anzahl von Benutzern registrieren müssen, benötigen Sie ein Skript, um sie zu importieren. Diese Benutzer können aus einem beliebigen System stammen, das Sie mit Plone ersetzen. Wenn Sie die Benutzer allerdings schon in LDAP, einer relationalen Datenbank oder einer anderen externen Quelle haben, können Sie diese Benutzer von dort aus direkt integrieren.</p>
<p>Vorläufig nehmen Sie eine Anzahl von mit Kommata getrennten Benutzern in einer Datei mit folgendem Inhalt: Benutzername, vollständiger Name, E-Mail und Gruppen. In diesem Beispiel werden Sie durch diese Liste durchgehen, um alle Benutzer mit diesen Einstellungen hinzuzufügen und dann ihre Eigenschaften so zu verändern, dass sie die richtigen Einstellungen haben. Die <tt class="docutils literal">.csv</tt>-Datei sieht daher etwa wie folgt aus:</p>
<pre class="literal-block">
&quot;Benutzername&quot;, &quot;Vollständiger Name&quot;, &quot;E-Mail&quot;, &quot;Gruppen&quot;
&quot;Andy&quot;, &quot;Andy Mckay&quot;, &quot;andy&#64;enfoldsystems.com&quot;, &quot;Systeme,Vertrieb,Entwicklung&quot;
...
</pre>
<p>Eine <tt class="docutils literal">.csv</tt>-Datei enthält pro Zeile Werte, die mit Kommata voneinander getrennt sind, und kann mit den meisten Tabellenkalkulationsprogrammen erstellt und bearbeitet werden, darunter auch Microsoft Excel oder OpenOffice.org. Anschließend können Sie die Datei im CSV-Format exportieren und schließlich in Plone importieren. Weil dazu eine Menge von Methoden aufgerufen werden müssen, deren Nutzung nicht für alle Benutzer möglich ist, müssen Sie deswegen eine externe Methode daraus machen:</p>
<pre class="literal-block">
# An external method to import user
import csv

# the full path to your csv file
fileName = &quot;/var/zope.zeo/Extensions/test.csv&quot;

def importUsers(self):
    reader = csv.reader(open(fileName, &quot;r&quot;))
    pr = self.portal_registration
    pg = self.portal_groups
    out = []

    # if your csv file contains a header line that
    # explains the contents of each column
    ignoreLine = 1
</pre>
<p>Dies ist nur der Code zum Einstellen. Mit anderen Worten: Er stellt alle Variablen ein, die Sie in diesem Skript benutzen werden. Am Anfang importieren Sie das <tt class="docutils literal">csv</tt>-Modul, das in Python 2.3 enthalten ist und ein schnelles Parsen von <tt class="docutils literal">.csv</tt>-Dateien ermöglicht. Die <tt class="docutils literal">.csv</tt>-Datei steht in der Variablen <tt class="docutils literal">fileName</tt> und ist der vollständige absolute Pfad zur Datei. Wenn Sie einen relativen Pfad angeben, sucht Plone die Datei möglicherweise am falschen Ort. Wie Sie vorher gesehen haben, wird <tt class="docutils literal">self</tt> an die Methode übergeben, und von dort aus können Sie zu den zwei benötigten Werkzeugen gelangen: <tt class="docutils literal">portal_registration</tt> für den Zugriff auf die Registrierungs-API und <tt class="docutils literal">portal_groups</tt> für den Zugriff auf die Gruppen-API:</p>
<pre class="literal-block">
for row in reader:
    if ignoreLine:
        ignoreLine = 0
        continue

    # check we have exactly 4 items
    assert len(row) == 4
    id, name, email, groups = row
    groups = groups.split(',')

    # make a password
    password = pr.generatePassword()
</pre>
<p>Als Nächstes gehen Sie alle Zeilen durch und bekommen ID, Namen, E-Mail und Gruppen. Dann generieren Sie ein zufälliges Passwort, indem Sie  <tt class="docutils literal">generatePassword</tt> aufrufen. Dabei wird ein zufälliges Passwort aus sechs Zeichen (Klein- und Großbuchstaben und Zahlen) erzeugt. Wenn Sie die ID oder das Passwort von einer Information wie dem Benutzernamen oder der E-Mail abhängig machen möchten, können Sie das hier tun. In diesem Fall habe ich alle Gruppen mit Kommata getrennt ins gleiche Feld eingetragen (z.B. <tt class="docutils literal">&quot;Vertrieb,Marketing&quot;</tt>). Daher muss ich das z.B. wie folgt in eine Liste einzelner Namen trennen:</p>
<pre class="literal-block">
    try:
        # add in member
        pr.addMember(id = id,
            password = password,
            roles = [&quot;Member&quot;,],
            properties = {
                'fullname': name,
                'username': id,
                'email': email,
                }
            )
        # groups are separated by commas
        for groupId in groups:
            group = pg.getGroupById(groupId)
            group.addMember(id)

        out.append(&quot;Added user %s&quot; % id)

    except ValueError, msg:
        # if we skipped this user for a reason, tell the person
        out.append(&quot;Skipped %s, reason: %s&quot; % (id, msg))

# return something
return &quot;\n&quot;.join(out)
</pre>
<p>Da Sie nun alle für die Registrierung des Benutzers nötigen Angaben haben, können Sie die eigentliche Registrierung vornehmen. Das machen Sie, indem Sie die Funktion <tt class="docutils literal">addMember</tt> von <tt class="docutils literal">portal_registration</tt> aufrufen, die einen Benutzer registriert. Dieser Funktion wird ein Dictionary mit Schlüssel/Wert-Paaren wie E-Mail und Name übergeben. Dann rufen Sie für jede Gruppe <tt class="docutils literal">getGroupById</tt> auf, um die Gruppe zu bekommen und darauf <tt class="docutils literal">addMember</tt> aufzurufen. Wie der Name schon andeutet, wird dabei der Benutzer mit dieser Gruppe registriert. Wenn Sie fertig sind, müssen Sie nur noch etwas für die Person ausgeben, die den Import ausführt.</p>
<p>Um das auf Ihrer Site auszuführen, müssen Sie das Skript in das Verzeichnis  <tt class="docutils literal">Extensions</tt> Ihres Plone-Servers kopieren und es <tt class="docutils literal">import_users_with_groups.py</tt> nennen. Dann müssen Sie von Hand die Gruppen hinzufügen, die Sie auf Ihrer Site haben werden. Dieses Skript erzeugt nicht die Gruppen für Sie. Dann bereiten Sie die <tt class="docutils literal">.csv</tt>-Datei vor. Wenn Ihre Benutzer in einem anderen System gespeichert sind, müssen Sie einen Weg finden, sie in dieses Format zu übertragen. Ändern Sie den Dateinamen im Skript auf den Namen Ihrer neuen Datei. Fügen Sie dann eine externe Methode mit den folgenden Werten zu Ihrer Plone-Site hinzu:</p>
<ul class="simple">
<li><strong>ID</strong>: <tt class="docutils literal">import_users_with_groups</tt></li>
<li><strong>Module name</strong>: <tt class="docutils literal">import_users_with_groups</tt></li>
<li><strong>Function name</strong>: <tt class="docutils literal">importUsers</tt></li>
</ul>
<p>Nachdem Sie diese externe Methode hinzugefügt haben, klicken Sie auf <em>Test</em>, um die Methode auszuführen, und Sie werden das Ergebnis bekommen!</p>
</div>
<div class="section" id="benutzereinstellungen-andern">
<h4>Benutzereinstellungen ändern</h4>
<p>Wenn Sie ein neues Produkt installieren oder eine Einstellung neu vornehmen, kann es notwendig sein, Benutzer-Metadaten en masse zu verändern. Wenn Sie z.B. einen neuen WYSIWYG-Editor installieren und möchten, dass dieser zum Standardeditor für jeden Benutzer wird, müssen Sie zwei Dinge tun:</p>
<ul class="simple">
<li>Ändern Sie die Standardeinstellung für alle neuen Benutzer. Dazu klicken Sie auf <em>portal_metadata</em> und wählen den <em>Properties</em>-Reiter. Setzen Sie dort die Voreinstellung, und alle neuen Benutzer erhalten diesen Wert.</li>
<li>Ändern Sie die Einstellungen für alle vorhandenen Benutzer. Das kann nur mit der folgenden externen Methode durchgeführt werden:</li>
</ul>
<pre class="literal-block">
def fixUsers(self):
    pm = self.portal_membership
    members = pm.listMemberIds()

    out = []
    for member in members:
        # now get the actual member
        m = pm.getMemberById(member)
        # get the editor property for that member
        p = m.getProperty('wysiwyg_editor', None)

        out.append(&quot;%s %s&quot; % (p, member))
        if p is not None and p != 'Epoz':
            m.setMemberProperties({'wysiwyg_editor': 'Epoz',})
            out.append(&quot;Changed property for %s&quot; % member)
    return &quot;\n&quot;.join(out)
</pre>
<p>Kopieren Sie diesen Code in ein Python-Modul ins <tt class="docutils literal">Extensions</tt>-Verzeichnis Ihrer Plone-Instanz, und nennen Sie das Modul <tt class="docutils literal">fixUserScript.py</tt>. Fügen Sie dann im ZMI eine externe Methode mit den folgenden Parametern hinzu:</p>
<ul class="simple">
<li><strong>ID</strong>: <tt class="docutils literal">fixUsers</tt></li>
<li><strong>Module name</strong>: <tt class="docutils literal">fixUserScript</tt></li>
<li><strong>Function name</strong>: <tt class="docutils literal">fixUsers</tt></li>
</ul>
<p>Klicken Sie auf den <em>Test</em>-Reiter, um den Code auszuführen. Er läuft über alle Mitglieder Ihrer Site und setzt den Wert des WYSIWYG-Editors auf <tt class="docutils literal">&quot;Epoz&quot;</tt>. Dazu wird zuerst die Liste aller Mitglieder geholt, und zwar mit einer Methode in <tt class="docutils literal">portal_membership</tt> namens <tt class="docutils literal">listMemberIds</tt>, die das für Sie erledigt. Für jedes Mitglied wird die von Plone verwendete Eigenschaft untersucht, um den Editor zu bestimmen (in diesem Fall die Eigenschaft <tt class="docutils literal">wysiwyg_editor</tt>). Falls diese Eigenschaft verschieden von <tt class="docutils literal">&quot;Epoz&quot;</tt> ist, wird <tt class="docutils literal">setMemberProperties</tt> aufgerufen, um sie zu ändern.</p>
<p>Das ist eine hilfreiche Methode, um über all Ihre Mitglieder zu iterieren. Mit den Methoden <tt class="docutils literal">setMemberProperties</tt> und <tt class="docutils literal">getProperty</tt> können Sie eine beliebige  Eigenschaft eines Mitglieds untersuchen oder ändern.</p>
</div>
<div class="section" id="die-anderen-benutzer-einer-gruppe-bestimmen">
<h4>Die anderen Benutzer einer Gruppe bestimmen</h4>
<p>Ich habe bereits die Möglichkeit erörtert, eine E-Mail an alle Leute einer Arbeitsgruppe zu einem Objekt zu schicken. Das könnten Sie zum Workflow hinzufügen, aber zuerst brauchen Sie ein Skript, um das zu tun. Dieses Beispiel verwendet ein paar Funktionen, um an die Benutzer heranzukommen. Das folgende Skript <tt class="docutils literal">getGroupUsers</tt> nimmt ein Objekt und gibt eine Liste von Benutzern zurück:</p>
<pre class="literal-block">
##parameters=object=None
# object is the object to find all the members of the same group for
users = []
# get the creator
userName = object.Creator()
user = context.portal_membership.getMemberById(userName)
pg = context.portal_groups

# loop through the groups the user is in
for group in user.getGroups():
  group = pg.getGroupById(group)

   # loop through the users in each of those groups
  for user in group.getGroupUsers():
    if user not in users and user != userName:
      users.append(user)

return users
</pre>
<p>In diesem Skript erhalten Sie ein Objekt, dessen Erzeuger Sie mit der Methode <tt class="docutils literal">Creator</tt> finden müssen. Sobald Sie diesen Benutzer haben, können Sie <tt class="docutils literal">getGroups</tt> aufrufen, und eine Methode des Benutzerobjekts listet die Namen aller Gruppen auf, in denen der Benutzer Mitglied ist. Danach erhalten Sie all diese Gruppen, und aus dieser Liste erhalten Sie die Benutzernamen zu einer Gruppe. So haben Sie schließlich alle Benutzernamen. Nun möchten Sie darunter keine Duplikate und auch nicht die ursprüngliche Person, die das Objekt geändert hat. Die Benutzerliste enthält alle anderen Benutzer in den gleichen Gruppen wie die Person, die das Objekt besitzt.</p>
<p>Das sollten Sie in Ihr Workflow-E-Mail-Benachrichtigungsskript aus Kapitel 7 einsetzen, um es zu verbessern. Wenn Sie sich erinnern, dann haben Sie darin Folgendes gemacht:</p>
<pre class="literal-block">
for user in mship.listMembers():
    if &quot;Reviewer&quot; in mship.getMemberById(user.id).getRoles():
</pre>
<p>Das iteriert über alle Benutzer und prüft, ob sie die Mitgliederrolle haben. Das vorangegangene Skript hieß <tt class="docutils literal">getGroupUsers</tt> und befand sich im Ordner <tt class="docutils literal">portal_skins/custom</tt>, d.h., Sie können per Akquisition über den <tt class="docutils literal">context</tt>-Namespace darauf zugreifen. Kurz gesagt: mit <tt class="docutils literal">context.getGroupUsers(object)</tt> bekommen Sie die Benutzer:</p>
<pre class="literal-block">
users = context.getGroupUsers(object)
for id in users:
    user = mship.getMemberById(id)
</pre>
<p>Nun senden Sie eine E-Mail an alle in der Gruppe statt an alle Redakteure!</p>
</div>
<div class="section" id="benutzerangaben-in-page-templates">
<h4>Benutzerangaben in Page Templates</h4>
<p>In Kapitel 6 haben Sie ein Page Template erstellt, mit dem ein Benutzer Feedback über ein Formular an den Site-Administrator geben konnte. In diesem Formular konnte der Benutzer in einem Eingabefeld eine E-Mail-Adresse eingeben, die Sie dann validiert haben. Wenn ein Benutzer aber angemeldet ist und Sie seine E-Mail-Adresse kennen, wäre es nett, dieses Feld für den Benutzer automatisch auszufüllen.</p>
<p>Der vorhandene Code für das Eingabefeld lautet wie folgt:</p>
<pre class="literal-block">
&lt;input type=&quot;text&quot; name=&quot;email_address&quot;
       tal:attributes=&quot;tabindex tabindex/next;
                       value request/email_address|nothing&quot; /&gt;
</pre>
<p>Wenn nun im Anfrageformular schon ein Wert für diese E-Mail-Adresse aus einem vorangegangenen Ausfüllversuch existiert, sollten Sie ihn anzeigen. Wenn nicht, dann können Sie nachsehen, ob eine E-Mail-Adresse zum aktuellen Benutzer existiert. Die folgenden Änderungen am Formular sorgen dafür, dass die E-Mail-Adresse ausgefüllt ist:</p>
<pre class="literal-block">
&lt;input type=&quot;text&quot; name=&quot;email_address&quot;
tal:define=&quot;user context/portal_membership/getAuthenticatedMember;
            email user/email|nothing&quot;
       tal:attributes=&quot;tabindex tabindex/next;
                       value request/email_address|email|nothing&quot; /&gt;
</pre>
</div>
<div class="section" id="fehlersuche-und-hintergrunde-zur-sicherheit">
<h4>Fehlersuche und Hintergründe zur Sicherheit</h4>
<p>Ich habe festgestellt, dass Sicherheit nicht nur einer der am schwersten zu verstehenden Bereiche von Plone ist, sondern auch einer, bei dem die Fehlersuche und das Testen sehr schwierig sind. Da das Modell granular aufgebaut und kompliziert ist, kann es extrem schwierig sein herauszufinden, warum und wo ein Fehler auftritt. Manchmal ist die angegebene Fehlermeldung oder Information schwer zu entziffern, oder es ist schon schwer, überhaupt eine Information darüber zu bekommen.</p>
<p>Sicherheitstests sind ebenfalls ein schwieriges Unterfangen, da Sie in Sites mit vielen Rollen einen vollständigen Regressionstest für alle Rollen in allen Situationen durchführen sollten. Wegen der damit verbundenen Kosten werden diese umfangreichen Regressiontests aber oftmals nicht durchgeführt. Außerdem ist ein Sicherheitsfehler das Schlimmste, das auf einer Site passieren kann, wenn dadurch vertrauliche Informationen verloren gehen. Plone lässt Sie hier machen, was immer Sie wollen, und hält Sie nicht davon ab, sich selbst ins Knie zu schießen, also passen Sie auf!</p>
<div class="section" id="verbosesecurity">
<h5>VerboseSecurity</h5>
<p>VerboseSecurity ist ein Zusatzprodukt, das von den Installationsprogrammen gleich mitinstalliert wird. Sie können VerboseSecurity auch unter <a class="reference external" href="http://hathaway.freezope.org/Software/VerboseSecurity">http://hathaway.freezope.org/Software/VerboseSecurity</a> herunterladen. Wie der Name schon klarmacht, bietet es detaillierte Fehlermeldungen, wenn Sie etwas in Plone nicht tun dürfen, weil Sie keine Berechtigung dazu haben. Wenn Sie jedoch zu lasche Sicherheitseinstellungen benutzen, kann Ihnen dieses Produkt auch nicht helfen.</p>
<p>VerboseSecurity kann auf einem Plone-Server ohne Performance-Einbußen laufen, d.h., Sie können das Produkt problemlos auf Ihren Produktions- und Entwicklungsservern betreiben. Ein bisschen Leistung mag dann verloren gehen, wenn jemand kein Recht hat, etwas zu tun, ein Fehler ausgelöst wird und die neuen Sicherheitsmodule einspringen.</p>
<p>Da die Fehlermeldung aber sehr detailliert ist, möchten Sie diese sicher nicht den Benutzern vorsetzen. Sie enthält wesentlich mehr Informationen über Ihr System, als ein Benutzer jemals wissen sollte! Passwörter sind jedoch niemals enthalten, sondern nur Informationen über die Benutzer, Rollen und Rechte. Natürlich wird Ihr Produktionsserver immer perfekt laufen, d.h., Sie werden keinen Bedarf haben, VerboseSecurity auf Ihrem Produktionsserver zu installieren.</p>
<p>Die ursprüngliche Implementierung der Routinen, die die Rechte prüfen, war in Python geschrieben. Mit zunehmend stabilerem API und nachdem die Entwickler die Einsicht gewonnen hatten, welchen Overhead die Sicherheitsmaßnahmen mit sich brachten, wurde sie in C neu geschrieben. Standardmäßig läuft die schnellere C-Implementierung, was aber bedeutet, dass VerboseSecurity das Rechte-Modul nicht ändern kann, um es ausführlicher zu machen. Allerdings musste ich mich nur selten auf diese Detailstufe begeben. Normalerweise bekomme ich so schon genügend Informationen. Wenn Sie jedoch weitere Informationen benötigen, müssen Sie Plone mit der folgenden Umgebungsvariablen ausführen:</p>
<pre class="literal-block">
ZOPE_SECURITY_POLICY=PYTHON
</pre>
<p>Um VerboseSecurity zum Laufen zu bringen, müssen Sie lediglich sicherstellen, dass VerboseSecurity in Ihrem <tt class="docutils literal">Products</tt>-Verzeichnis liegt (weitere Details dazu finden Sie in Kapitel 10) und dann Plone neu starten. Gehen Sie zum <tt class="docutils literal">cookie_authentication</tt>-Objekt, das die Liste der Optionen für Ihre Site-Authentifikation enthält, und ändern Sie im Formular die Option für <tt class="docutils literal">login_page</tt> von <tt class="docutils literal">require_login</tt> auf einen leeren Wert, wie in Abbildung 9.14 zu sehen ist.</p>
<a class="reference external image-reference" href="img/09-14.png/image_view_fullscreen"><img alt="img/09-14.png" class="original" src="img/09-14.png" /></a>
<p>Abbildung 9.14. Ändern der Anmeldeeinstellungen für Ihre Site</p>
<p>Nun können Sie die Umstände neu wiederherstellen, unter denen der Fehler aufgetreten ist, den Sie suchen.  <em>Denken Sie daran, sich als der Benutzer anzumelden, der den Fehler bekam!</em> An dieser Stelle ist es sehr praktisch, zwei verschiedene Browser für den Zugriff auf Ihre Plone-Site zu haben: einen für die Verwaltung und einen zum Testen. Wenn ein Fehler auftritt, wird ein HTTP-Authentifikationsdialog auf dem Bildschirm angezeigt. An diesem Punkt klicken Sie auf <em>Abbrechen</em>, und nun sollten Sie eine detaillierte Fehlermeldung bekommen, wie sie in Abbildung 9.15 zu sehen ist.</p>
<a class="reference external image-reference" href="img/09-15.png/image_view_fullscreen"><img alt="img/09-15.png" class="original" src="img/09-15.png" /></a>
<p>Abbildung 9.15. Eine schön detaillierte Fehlermeldung</p>
<p>Diese Meldung ist recht lang und selbsterklärend. An dieser Stelle gehe ich normalerweise zu dem anderen Browser und untersuche die Rechteeinstellungen der beteiligten Objekte, um zu sehen, was die Ursache sein könnte.</p>
</div>
<div class="section" id="haufige-probleme">
<h5>Häufige Probleme</h5>
<p>Ein paar Probleme lassen sich leicht feststellen, wenn man es mit Plone zu tun hat. Das erste hat nichts speziell mit Plone zu tun, sollte hier aber dennoch wiederholt werden: Prüfen Sie, dass der Benutzer, der den Fehler erzeugen kann, wirklich der ist, den Sie annehmen. Ich habe oft genug von Leuten Dinge gehört wie &quot;Funktioniert in einem Browser, aber nicht in einem anderen.&quot; Das kommt normalerweise daher, das Sie beim Browser-Wechsel auch den Benutzer wechseln.</p>
<p>Um mit dem Offensichtlichen weiterzumachen, überprüfen Sie zweimal, dass Ihr Benutzer auch die erwartete Rolle hat! Das kann bedeuten, in <tt class="docutils literal">acl_users</tt> nachzusehen, welche Benutzerrolle er hat, und zu prüfen, dass die erwartete Rolle vorhanden ist. Als Nächstes denken Sie an mögliche Gruppen, in denen der Benutzer enthalten sein könnte. Wieder verschafft Ihnen ein Blick in <tt class="docutils literal">acl_users</tt> Klarheit, da Benutzer über eine Gruppe zu weiteren Rollen kommen können. Und denken Sie schließlich daran, dass die Rolle eines Benutzers auch von lokalen Rollen in Ordnern oder Objekten verändert werden kann. Das ist etwas schwieriger festzustellen, weil es keine einfache Möglichkeit gibt festzustellen, welcher Ordner oder welche Objekte lokale Rollen haben.</p>
<p>Sobald Sie sich sicher sind, wer der Benutzer ist und welche Rolle er an einem Objekt hat, können Sie herausfinden, welche Rechte an einem Objekt wirklich vorhanden sind. Wie Sie gesehen haben, können zwei ähnliche Objekte (z.B. zwei Dokumente) verschiedene Rechte und Rollen haben. Der Benutzer, der das Dokument erstellt, wird auf dem Dokument die Rolle des Besitzers haben, während ein anderer Benutzer nur die Mitgliederrolle daran haben wird. Die Rechte an einem Dokument werden auch vom Workflow verändert, während es durch die verschiedenen Workflow-Zustände geht.</p>
</div>
<div class="section" id="plone-zusperren">
<h5>Plone zusperren</h5>
<p>Plone kann man nicht so leicht zusperren, weil es kein Konzept einer &quot;gesperrten&quot; Site gibt. Das Grundprinzip ist aber, dass Benutzer das minimal Notwendige dessen tun können sollen, was sie tun müssen, und nicht mehr. Das heißt, Sie sollten die Standardeinstellungen überprüfen und die nicht benötigten Einstellungen entfernen.</p>
<p>Wirklich paranoide Zeitgenossen können auch Eigenschaften aus der Benutzerschnittstelle entfernen, um Benutzer vom Herumwandern abzuhalten, indem sie den CSS-Code verändern (Cascading Style Sheets). Aber denken Sie daran, dass allein das Entfernen eines Reiters zu einer Aktion oder die Verweigerung des Zugriffs an einem Page Template nicht ausreicht, wenn ein Benutzer z.B. weiterhin ein Dokument bearbeiten kann. Mit dem entsprechenden Wissen über Plone könnten Benutzer die Seite aus einem Skript oder über einen anderen bösartigen Mechanismus ausführen. Wenn Sie es nur intensiv genug versuchen, werden Sie sehen, dass Sie in Plone zur Bearbeiten-Seite eines Dokuments kommen, das Sie gerade anzeigen, indem Sie die entsprechende URL hacken. Allerdings wird es Ihnen nicht gelingen, die Seite wirklich zu bearbeiten; Sie können lediglich das Bearbeiten-Formular aufrufen.</p>
<p>Wenn Ihr Server ohne Zugangsbeschränkung in der Wildnis läuft, sollten Sie sicherstellen, dass Sie einen weiteren Webserver vor Zopes ZServer laufen lassen. Wie ich in Kapitel 10 beschreiben werde, ist der im Paket enthaltene ZServer eine einfache Implementierung ohne all die Überprüfungen und Sicherheiten, die ein echter Webserver braucht. Erwägen Sie nach Möglichkeit den Einsatz von Proxies für andere Zope-Services wie FTP und WebDAV, falls Sie Benutzern den Zugriff auf diese Dienste ermöglichen, denen Sie nicht trauen können (was normalerweise nicht der Fall ist).</p>
</div>
</div>
</div>
<div class="section" id="plone-mit-anderen-diensten-integrieren">
<h3>Plone mit anderen Diensten integrieren</h3>
<p>Die folgenden Abschnitte behandeln die Sicherheit außerhalb einer Plone-Instanz, z.B. alle Sicherheitseinstellungen, die Sie benötigen, um Plone auf einem Server zu betreiben. Anschließend behandle ich den Einsatz von Plone mit LDAP, damit Sie Benutzer von einem externen Server in Plone benutzen können.</p>
<div class="section" id="sicherheit-auf-ihrem-server">
<h4>Sicherheit auf Ihrem Server</h4>
<p>Die Sicherheit von Benutzern innerhalb eines Plone-Systems habe ich behandelt, aber es gibt einen weiteren wichtigen Aspekt: die Sicherheit und Einrichtung Ihres Plone-Servers in Ihrem Betriebssystem. Wie bei allen Webanwendungen ist es ein wichtiger Schritt, die Sicherheit Ihres Servers zu gewährleisten, bevor er für die ganze Welt freigeschaltet wird. Der Installationsvorgang für Zope 2.7 ist ziemlich gut und macht das meiste hiervon für Sie bereits richtig. Aber auf einige Dinge sollten Sie hingewiesen werden, was ich nun im Folgenden tun möchte.</p>
<div class="section" id="der-benutzer-der-zope-laufen-lasst">
<h5>Der Benutzer, der Zope laufen lässt</h5>
<p>Sie sollten sicherstellen, dass der Benutzer, der Zope laufen lässt, ein Minimum der notwendigen Sicherheitsrechte hat, um die Aufgabe zu erledigen. Der Benutzer, der Zope ausführt, benötigt Lese- und Schreibrechte an allen Zope-Verzeichnissen im Dateisystem. Der Benutzer muss in den Verzeichnissen schreiben, die die Protokolle und die Datenbank Ihrer Zope-Instanz enthalten. Das sind die Verzeichnisse <tt class="docutils literal">var</tt> und <tt class="docutils literal">log</tt> Ihrer Zope-Instanz.</p>
<p>Auf Linux macht man das am besten so, dass ein spezieller Benutzer, z.B. mit Namen <tt class="docutils literal">plone</tt>, erstellt wird, der das übernimmt. Dann können Sie die Zugriffsmöglichkeiten dieses Benutzers für den unwahrscheinlichen Fall beschränken, dass Plone gehackt wird.</p>
<p>Wenn Sie mit Plone unter Linux Ports mit kleinen Nummern (unter 1024) wie 21 oder 80 belegen möchten, müssen Sie Plone normalerweise als Root ausführen. Es belegt diese Ports dann als Root und wechselt anschließend zu einem anderen effektiven Benutzer. Dazu müssen Sie einen Wert für <tt class="docutils literal"><span class="pre">effective-user</span></tt> in der Konfigurationsdatei <tt class="docutils literal">zope.conf</tt> angeben. Dann belegt es die Ports und wechselt zu diesem Benutzer. Ein Beispiel dafür ist <tt class="docutils literal"><span class="pre">effective-user</span> zope</tt>. Die beste Alternative dazu ist nicht etwa die, das gar nicht zu machen, sondern Zope stattdessen auf einem höheren Port, z.B. 8080, auszuführen. Diesen Port können Sie dann in der Firewall schützen und Apache oder einen anderen Webserver benutzen, der auf Port 80 als Proxy zu Port 8080 läuft. Kapitel 10 enthält weitere Details zu diesem Thema.</p>
<p>Das Äquivalent dazu unter Windows ist der Benutzer, der den Dienst ausführt, standardmäßig das Konto <tt class="docutils literal">LocalSystem</tt>. Auch hier können Sie den Benutzer ändern, unter dem Plone läuft. Wenn Sie versuchen, Plone auf einem Windows-Rechner auszuführen, der keine Dienste hat (was ich nicht empfehlen oder unterstützen kann), läuft Plone lokal unter dem Benutzer, der den Server manuell gestartet hat.</p>
<p>Manche Produkte verlangen eventuell die Installation zusätzlicher Software, die z.B. Möglichkeiten der Bildverarbeitung, Dokumentkonvertierung usw. bietet. Wenn Sie solche Werkzeuge installiert haben, sollten Sie daran denken, dass eventuell ein wenig Arbeit damit verbunden ist, damit sie mit Ihrer Plone-Site erfolgreich zusammenarbeiten. Unter Windows habe ich z.B. <tt class="docutils literal">pdftohtml</tt> für die Konvertierung von PDF-Dokumenten (Portable Document Format) installiert, aber damit der Befehl ausgeführt werden kann, musste ich den Dienst unter einem Benutzer mit zusätzlichen Privilegien ausführen, damit Zope mit dieser Software interagieren kann. In diesem Fall war das kein Problem, weil der Server hinter einer Firewall lag.</p>
</div>
<div class="section" id="zugriff-in-notfallen">
<h5>Zugriff in Notfällen</h5>
<p>Wenn Sie eine Plone-Site haben, aber nicht auf das ZMI zugreifen können, weil Sie das Passwort nicht kennen oder vergessen haben, können Sie sich ein Notfall-Konto verschaffen. Dazu benötigen Sie den Zugriff per Dateisystem auf die Instanz Ihrer Plone-Site. Wenn Sie diesen nicht haben, müssen Sie zuerst einen Weg finden, diesen zu erhalten.</p>
<p>Gehen Sie dazu zu Ihrer Instanzwurzel, und starten Sie das Skript <tt class="docutils literal">zpasswd.py</tt>, das Sie in Ihrem Zope-Verzeichnis (in <tt class="docutils literal">ZOPE_HOME</tt>) finden. Auf meinem Rechner befindet sich das Skript <tt class="docutils literal">zpasswd.py</tt> in <tt class="docutils literal"><span class="pre">/opt/Zope-2.7/bin/zpasswd.py</span></tt>. Um also das Passwort zu erzeugen, machen Sie Folgendes:</p>
<pre class="literal-block">
$ cd /var/zope
$ python /opt/Zope-2.7/bin/zpasswd.py access

Username: emergency
Password:
Verify password:

Please choose a format from:

SHA - SHA-1 hashed password
CRYPT - UNIX-style crypt password
CLEARTEXT - no protection.

Encoding: SHA
Domain restrictions:
</pre>
<p>Damit wird eine Datei namens <tt class="docutils literal">access</tt> in Ihrer Zope-Instanz erzeugt. Starten Sie Zope nun erneut, und melden Sie sich im ZMI mit dem Benutzernamen und Passwort an, das in diesem Skript eingegeben wurde. Dieser Benutzer hat eine besondere Bedeutung in Plone und wird auch <em>Notfallbenutzer</em> genannt. Wenn Sie einmal als Notfallbenutzer angemeldet sind, können Sie keine Objekte erstellen, aber Sie können einen neuen Benutzer für sich selbst anlegen und sich als dieser Benutzer anmelden. Aus Sicherheitsgründen sollten Sie dann die Datei <tt class="docutils literal">access</tt> löschen.</p>
</div>
<div class="section" id="zugriff-in-notfallen-unter-windows">
<h5>Zugriff in Notfällen unter Windows</h5>
<p>Die Windows-Installation von Plone bietet eine grafische Benutzerschnittstelle, mit der man sehr leicht einen Notfallzugriff bekommt. Wählen Sie <em>Start - Programme - Plone - Plone</em>. und klicken Sie auf <em>Emergency User</em>. Damit können Sie einen neuen Benutzer erzeugen, das Passwort des aktuellen Notfallbenutzers ändern oder einen Notfallbenutzer löschen, wie Abbildung 9.16 zeigt.</p>
<a class="reference external image-reference" href="img/09-16.png/image_view_fullscreen"><img alt="img/09-16.png" class="original" src="img/09-16.png" /></a>
<p>Abbildung 9.16. Erstellen eines neuen Notfallbenutzers</p>
<p>Um einen neuen Benutzer zu erstellen, klicken Sie auf <em>Create User</em>. Geben Sie im dann erscheinenden Dialogfeld einen Benutzernamen und ein Passwort ein. Dann wird im Dateisystem eine Datei erzeugt, die den Benutzernamen und das Passwort enthält. Ein Klick auf <em>Change Password</em> ändert das Passwort des Benutzers. Nach dem Erstellen oder der Änderung eines Passworts müssen Sie Zope neu starten. Um Plone neu zu starten, gehen Sie zum <em>Control</em>-Reiter, klicken auf <em>Stop</em> und klicken dann auf <em>Start</em>. Dann klicken Sie auf <em>Manage Root</em> und geben den zuvor gewählten Benutzernamen und Ihr Passwort ein. Dann sind Sie als Notfallbenutzer angemeldet, d.h., Sie können keine Objekte erstellen, aber Sie können einen neuen Benutzer für sich selbst erstellen und sich als dieser Benutzer anmelden.</p>
</div>
</div>
<div class="section" id="externe-authentifizierungssysteme-verwenden">
<h4>Externe Authentifizierungssysteme verwenden</h4>
<p>Wie Sie in Kapitel 8 gesehen haben, speichert Plone all seine Benutzer in der Zope-Objektdatenbank in einer eigenen Benutzerliste. Das ist, wie alles, nicht perfekt, und ab einem gewissen Punkt möchten Sie vielleicht einen anderen Dienst verwenden, um Ihre Benutzer zu authentifizieren. Das am weitesten verbreitete Alternativsystem ist LDAP oder Microsofts Active Directory, das mit LDAP kommuniziert.</p>
<p>Möglicherweise möchten Sie aber mit einer anderen Anwendung zusammenarbeiten, die ihre Benutzer in einer relationalen Datenbank speichert. Während ich dieses Buch schrieb, verwendete die ASPN-Site von ActiveState Zope für alle Inhalte, aber dessen Benutzer können sich auch mit Microsofts Passport-System authentifizieren. Zusätzliche Schemata für die Benutzerauthentifizierung zu installieren ist dank der exzellenten Arbeit vieler Entwickler sogar sehr einfach. Bei der Einrichtung dieses Vorgangs empfand ich das Übersetzen der Software und die Integration der Systeme als den schwierigsten Teil.</p>
<div class="admonition-achtung admonition">
<p class="first admonition-title">Achtung</p>
<p class="last">Im nächsten Abschnitt werden Sie mit den <tt class="docutils literal">acl_users</tt>-Ordnern einer Plone-Site herumspielen. Sie sollten niemals den <tt class="docutils literal">acl_users</tt>-Ordner in der Wurzel Ihrer Zope-Instanz löschen oder ändern. Wenn Sie das tun und Ihr Benutzerordner geht aus irgendwelchen Gründen kaputt (z.B. wenn der Server herunterfährt), ist Ihre gesamte Site blockiert und Sie werden keinerlei Zugriff mehr darauf bekommen, nicht einmal als Administrator. Stellen Sie sicher, dass Sie nur den Benutzerordner der Plone-Site ändern!</p>
</div>
<div class="section" id="ldap-verwenden">
<h5>LDAP verwenden</h5>
<p>Zuerst müssen Sie einen LDAP-Server einrichten, oder etwas, das mit LDAP kommuniziert, z.B. Active Directory (obwohl Active Directory anscheinend einige Macken hat). In diesem Beispiel habe ich openLDAP auf meinem Red Hat-Server und auf meinem Windows-Server installiert. Für Windows finden Sie eine vorkompilierte Version unter <a class="reference external" href="http://www.zope.org/Members/volkerw/LdapWin32">http://www.zope.org/Members/volkerw/LdapWin32</a>. Diese habe ich mit Python 2.3 getestet.</p>
<p>Laden Sie die Datei herunter, packen Sie sie aus, und kopieren Sie den Inhalt nach <tt class="docutils literal"><span class="pre">C:\Programme\Plone\Python\lib\site-packages</span></tt>. Dann installieren Sie <em>LDAPUserFolder</em>.</p>
<p>Für Linux finden Sie die openLDAP-Downloads unter <a class="reference external" href="http://www.openldap.org">http://www.openldap.org</a>. Die getestete Version enthält die RPMs <tt class="docutils literal"><span class="pre">2.0.27-2.8.0</span></tt> und <tt class="docutils literal"><span class="pre">2.0.27-2.8.0</span></tt>. Nach dem Übersetzen mit den folgenden Anweisungen habe ich unter <a class="reference external" href="http://python-ldap.sourceforge.net">http://python-ldap.sourceforge.net</a> die passenden LDAP-Bibliotheken für Python heruntergeladen und übersetzt. In meinem Fall war die getestete Version <tt class="docutils literal"><span class="pre">2.0.0pre05-1.i386.rpm</span></tt>. Passen Sie auf, dass Sie denselben Python-Interpreter verwenden, mit dem auch Plone läuft.</p>
<p>Wenn Sie durch diese Schritte durch sind, müssen Sie sicherstellen, dass das Modul <tt class="docutils literal">_ldap.so</tt> von Python importiert werden kann. Am leichtesten prüft man das mit dem folgenden Befehl:</p>
<pre class="literal-block">
$ python -c &quot;import _ldap&quot;
</pre>
<p>Wenn Sie keine Fehlermeldungen erhalten, dann wurde es korrekt importiert. Wenn Sie doch Fehler bekommen, müssen Sie Ihre Schritte noch einmal durchgehen. Holen Sie sich dann <em>LDAPUserFolder</em> unter <a class="reference external" href="http://www.dataflake.org/software/ldapuserfolder">http://www.dataflake.org/software/ldapuserfolder</a>. Die hier getestete Version war 2.1 beta 2. Laden Sie die Datei herunter, packen Sie sie aus, und verschieben Sie sie ins Verzeichnis <tt class="docutils literal">Products</tt> Ihrer Zope-Installation. Beispiel:</p>
<pre class="literal-block">
$ tar -zxf LDAPUserFolder-2_1beta2.tgz
$ mv LDAPUserFolder /var/zope/Products
</pre>
<p>Nun starten Sie Plone neu, gehen zum Control Panel und vergewissern sich, dass das Produkt auf der Produktseite korrekt angezeigt wird. Weitere Details dazu gibt es in Kapitel 10.</p>
<p>Anschließend sollten Sie in Plone in der Lage sein, auf <tt class="docutils literal">acl_users</tt> und <em>Sources</em> zu klicken und dann nach unten bis zur Option <tt class="docutils literal">Users source #1</tt> zu scrollen. Wählen Sie dann <em>LDAPUserFolder</em>, und markieren Sie <em>I'm sure</em>, wie in Abbildung 9.17 gezeigt. Das erzeugt einen neuen Benutzerordner und ersetzt den vorhandenen. Passen Sie also auf, dass Sie nichts Wichtiges verlieren. Tatsächlich ist jetzt ein guter Zeitpunkt für ein Backup. Anschließend klicken Sie auf OK.</p>
<a class="reference external image-reference" href="img/09-17.png/image_view_fullscreen"><img alt="img/09-17.png" class="original" src="img/09-17.png" /></a>
<p>Abbildung 9.17. Hinzufügen eines <em>LDAPUserFolder</em></p>
<p>Nehmen Sie in den Einstellungen zu <em>LDAPUserFolder</em> die Änderungen vor, die Ihren vorhandenen LDAP-Einstellungen entsprechen. Nun sollten Sie auf den <em>Users</em>-Reiter klicken und nach Benutzern suchen können, die in Ihrem LDAP-Verzeichnis bereits existieren.</p>
</div>
<div class="section" id="relationale-und-andere-datenbanken">
<h5>Relationale und andere Datenbanken</h5>
<p>Ein hervorragender Ersatz für den Benutzerordner heißt <em>exUserFolder</em>, was für <em>extensible user folder</em> steht. Er ist sehr einfach zu installieren. Laden Sie ihn einfach unter <a class="reference external" href="http://prdownloads.sourceforge.net/exuserfolder/exUserFolder-0_20_0.tgz">http://prdownloads.sourceforge.net/exuserfolder/exUserFolder-0_20_0.tgz</a> herunter, packen Sie ihn wie üblich aus, und kopieren Sie ihn in Ihr <tt class="docutils literal">Products</tt>-Verzeichnis. Nach einem Plone-Neustart sollten Sie <em>acl_users</em> anklicken können, <em>Sources</em> wählen und zur Option <em>Users source #1</em> scrollen. Dann wählen Sie <em>exUserFolder</em> und markieren <em>I'm sure</em>.</p>
<p>Tatsächlich führt <em>exUserFolder</em> eine Authentifizierung auf folgende Arten durch:</p>
<ul class="simple">
<li>Radius</li>
<li>SMB</li>
<li>LDAP</li>
<li>Relationale Datenbanken</li>
</ul>
<p>Um das machen zu können, müssen Sie die spezifischen Datenbankadapter für die relationale Datenbank installieren. Glücklicherweise sind Adapter für alle wichtigen Datenbanken vorhanden. Weitere Informationen zu fast allen Themen finden Sie in den <tt class="docutils literal">exUserFolder</tt>-Verzeichnissen in den <tt class="docutils literal">ReadMe</tt>-Dateien. Das Zope-Buch behandelt die Einrichtung des Zugriffs auf relationale Datenbanken unter <a class="reference external" href="http://zope.org/Documentation/Books/ZopeBook/2_6Edition/RelationalDatabases.stx">http://zope.org/Documentation/Books/ZopeBook/2_6Edition/RelationalDatabases.stx</a>.</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch10.rst">
    <title>10. Integration mit anderen Systemen</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch10.rst</link>
    <description>Integration ist ein großes Problem in allen Firmen, die bereits viele verschiedene Systeme verwenden. Da Plone ein Open Source-Projekt ist, existieren dafür eine Menge Produkte, Zusätze, Skins und Werkzeuge, die gratis zusätzliche Funktionalität bieten. Sie haben richtig gelesen, diese Produkte werden oftmals einfach so an alle abgegeben, die sie haben wollen. Dazu kommt noch, dass Python als Open Source-Sprache über eine Menge hervorragender Produkte verfügt (oft Pakete genannt). Die meisten dieser Produkte betreffen Plone allerdings nicht direkt. Mit anderen Worten, sie fügen keine Funktionalität von sich aus zu Plone hinzu. Das ist Aufgabe von Plone-Produkten. Allerdings fragen die Leute oft "Kann Plone dies oder jenes machen?" Die Antwort lautet oftmals: "Wenn Python es kann, ja!"</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Integration mit anderen Systemen</h2>
<p>Integration ist ein großes Problem in allen Firmen, die bereits viele verschiedene Systeme verwenden. Da Plone ein Open Source-Projekt ist, existieren dafür eine Menge Produkte, Zusätze, Skins und Werkzeuge, die gratis zusätzliche Funktionalität bieten. Sie haben richtig gelesen, diese Produkte werden oftmals einfach so an alle abgegeben, die sie haben wollen. Dazu kommt noch, dass Python als Open Source-Sprache über eine Menge hervorragender Produkte verfügt (oft <em>Pakete</em> genannt). Die meisten dieser Produkte betreffen Plone allerdings nicht direkt. Mit anderen Worten, sie fügen keine Funktionalität von sich aus zu Plone hinzu. Das ist Aufgabe von Plone-Produkten. Allerdings fragen die Leute oft &quot;Kann Plone dies oder jenes machen?&quot; Die Antwort lautet oftmals: &quot;Wenn Python es kann, ja!&quot;</p>
<p>Hier ist eine Liste der beliebtesten Python-Produkte:</p>
<ul class="simple">
<li><strong>Python Imaging Library (PIL)</strong>: Damit können Sie Bilder manipulieren, konvertieren und analysieren (<a class="reference external" href="http://www.pythonware.com/products/pil/">http://www.pythonware.com/products/pil/</a>).</li>
<li><strong>ReportLab</strong>: Hiermit können Sie dynamisch PDF-Dateien (Portable Document Format) erzeugen, die Bilder, Grafiken und andere nette Dinge enthalten (<a class="reference external" href="http://www.reportlab.org/">http://www.reportlab.org/</a>).</li>
<li><strong>Windows-Erweiterungen</strong>: Damit haben Sie eine Schnittstelle zu allen Windows-APIs (Application Programming Interfaces), z.B. können Sie damit COM-Objekte (Component Object Model) benutzen (<a class="reference external" href="http://sourceforge.net/projects/pywin32/">http://sourceforge.net/projects/pywin32/</a>).</li>
<li><strong>Pygame</strong>: Das ist ein Framework, mit dem man Spiele in Python entwickeln kann. Einige Leute haben es in Plone benutzt, um an die Schnittstelle zu seinen Medienschichten für die Erzeugung von Bildern oder Sounds zu gelangen (<a class="reference external" href="http://www.pygame.org/">http://www.pygame.org/</a>).</li>
<li><strong>OpenOffice.org-Anbindung</strong>: Mit dieser Anbindung können Sie fast alles mit OpenOffice.org-Dokumenten machen, z.B. sogar Microsoft Office-Dokumente parsen, wie Sie in Kapitel 13 sehen werden (<a class="reference external" href="http://udk.openoffice.org/python/python-bridge.html">http://udk.openoffice.org/python/python-bridge.html</a>).</li>
<li><strong>mxTidy</strong>: Dieses Paket sucht und korrigiert Probleme in HTML-Dateien (Hypertext Markup Language), inklusive Page Templates (<a class="reference external" href="http://www.egenix.com/files/python/mxTidy.html">http://www.egenix.com/files/python/mxTidy.html</a>).</li>
</ul>
<p>Diese hervorragenden Zusatzprodukte verfügen normalerweise über grafische Installationsprogramme für Windows, mit denen Sie eine Installation schrittweise durchführen können. Wenn Sie kein Windows-Benutzer sind, dann bietet das Python-Modul <em>distutils</em> eine einfache Schnittstelle für die Installation dieser Produkte von der Kommandozeile. Wie immer gilt auch hier, dass man vor der Installation die Anweisungen im heruntergeladenen Paket lesen sollte, damit nichts schief gehen kann. Dieses Kapitel konzentriert sich auf die Installation von Produkten, die eine zusätzliche Funktionalität für Plone bieten. Ein Verzeichnis von Python-Paketen finden Sie unter <a class="reference external" href="http://www.python.org/pypi">http://www.python.org/pypi</a>.</p>
<div class="sidebar">
<p class="first sidebar-title">Open Source-Lizenzen</p>
<!-- Most open-source packages are released with a particular license that describes how the package may be used. Before you use any third-party code, you should check the license to see if it's compatible with your needs. -->
<p>Die meisten Open Source-Pakete werden unter einer bestimmten Lizenz herausgebracht, die beschreibt, wie das Paket benutzt werden darf. Bevor Sie Code von Dritten benutzen, sollten Sie sich die Lizenz ansehen, um zu prüfen, ob sie zu der Art des geplanten Einsatzes bei Ihnen passt.</p>
<!-- Plone is licensed under the General Public License (GPL), which you can find at http://www.gnu.org/copyleft/gpl.html and inside the *LICENSE.txt* file of your Plone installation. If you're the developer of a Plone Web site, then you can happily create and develop Web sites without any issue. Users of your Web site will never have to worry about the license for the code; they just use the Web site normally. As with most licenses, the license limits the redistribution and sale of other people's code. -->
<p>Plone wird unter der General Public License (GPL) lizenziert, die Sie unter  <a class="reference external" href="http://www.gnu.org/copyleft/gpl.html">http://www.gnu.org/copyleft/gpl.html</a> und in der Datei <tt class="docutils literal">LICENSE.txt</tt> in Ihrer Plone-Installation finden. Als Entwickler einer Plone-Website dürfen Sie Websites problemlos fröhlich drauflosentwickeln. Die Benutzer Ihrer Website werden sich nie Gedanken über die Lizenz des Codes machen müssen, sondern sie benutzen die Website ganz normal. Wie bei den meisten Lizenzen gilt auch hier, dass die Weiterverbreitung und der Verkauf von anderer Leute Code eingeschränkt werden soll.</p>
<!-- Usually licenses are easy to read and understand, but if you're unsure, you should probably have a qualified legal professional look at the license. I'll limit myself to describing the main licenses that exist in the Zope world and point you to where you can find more information: -->
<p>Normalerweise sind Lizenzen leicht zu lesen und zu verstehen, aber wenn Sie sich dabei nicht wohlfühlen, sollten Sie die Lizenz lieber einem ausgebildeten Profi vorlegen. Ich beschränke mich hier darauf, die wichtigsten in der Zope-Welt gebräuchlichen Lizenzen zu beschreiben und Links zu weiteren Angaben dazu anzugeben:</p>
<!-- - **Zope Public License** http://zope.org/Resources/ZPL -->
<!-- - **Python License** http://http://www.python.org/2.3/license.html -->
<!-- - **GPL** http://www.gnu.org/copyleft/gpl.html -->
<!-- - **Lesser GPL** http://www.gnu.org/copyleft/lesser.html -->
<ul class="last simple">
<li><strong>Zope Public License</strong>  Dies ist die Lizenz des Zope Application Servers sowie die Lizenz der Wahl für viele Zusatzprodukte, die für Zope geschrieben wurden. Eine Kopie dieser Lizenz finden Sie unter <a class="reference external" href="http://zope.org/Resources/ZPL">http://zope.org/Resources/ZPL</a>.</li>
<li><strong>Python License</strong>  Das ist die Python-Lizenz, von der Sie eine Kopie unter <a class="reference external" href="http://www.python.org/2.3/license.html">http://www.python.org/2.3/license.html</a> finden.</li>
<li><strong>GPL</strong> Das ist die Lizenz von Plone, zu finden unter <a class="reference external" href="http://www.gnu.org/copyleft/gpl.html">http://www.gnu.org/copyleft/gpl.html</a>.</li>
<li><strong>Lesser GPL</strong>  Und schließlich die Lesser GPL, die unter  <a class="reference external" href="http://www.gnu.org/copyleft/lesser.html">http://www.gnu.org/copyleft/lesser.html</a> zu finden ist.</li>
</ul>
</div>
<div class="section" id="plone-produkte-installieren">
<h3>Plone-Produkte installieren</h3>
<p>Ein <em>Produkt</em> ist ein Modul, das in Plone installiert wird, um weitere Funktionalität für Plone bereitzustellen. Auch wenn der Name <em>Produkt</em> an Kosten denken lässt, ist das nicht richtig, denn die meisten Produkte sind frei verfügbar und Open Source. Der Begriff <em>Produkt</em> beschreibt tatsächlich etwas, was im Dateisystem vorhanden ist und an andere Plone-Sites weitergegeben und dort verwendet werden kann.</p>
<p>Die Installation eines Produkts erfolgt normalerweise in den folgenden zwei Schritten:</p>
<ol class="arabic simple">
<li>Installation zu seiner Registrierung innerhalb von Zope</li>
<li>Installation in jeder Plone-Instanz, die es benutzen soll</li>
</ol>
<p>Wegen der großen Vielzahl an verfügbaren Zusätzen kann man nur sehr schwer allgemeinen Grundsätze angeben, was man genau zu deren Installation tun muss. Wie ich in diesem Kapitel mehrfach darstellen werde, sollten Sie immer die Installationsanweisungen der Produkte lesen, die normalerweise erklären, wie man das Produkt installiert. Wenn Sie weitere Hilfestellung brauchen, wenden Sie sich an eine Mailing-Liste oder an den Autor des Produkts, um weitere Informationen zu erhalten. Aber lesen Sie unbedingt zuerst die Anweisungen!</p>
<p>Denken Sie bei der Installation von Produkten daran, dass Sie Code installieren, der unvollständig sein kann und keinerlei Qualitätsgarantien macht. Es liegt in der Natur von Open Source, dass Leute Produkte schreiben und sie dann liegen lassen, wenn sie mit dem nächsten Projekt weitermachen. In einer idealen Welt würde eine Person Ihres Vertrauens sich den Code Zeile für Zeile anschauen, bevor Sie irgendetwas installieren. In der Realität ist das nicht machbar. Dennoch gilt, dass die meisten Produkte ziemlich gut sind. Aber auf jeden Fall sollten Sie Produkte testen, bevor Sie diese auf Ihrer Millionen Euro schweren Website installieren.</p>
<div class="section" id="produkte-finden">
<h4>Produkte finden</h4>
<p>Das richtige Produkt für Ihre Zwecke zu finden ist vermutlich der schwierigste Teil der Integration. Die Website Zope.org enthält viele Produkte, die von Benutzern erstellt und hochgeladen wurden. Diese Produkte finden Sie vor allem unter <a class="reference external" href="http://www.zope.org/Products">http://www.zope.org/Products</a>, aber wenn Sie sich die Homepage von Zope.org anschauen, werden Sie Produktankündigungen im rechten Teil der Seite sehen. Manche dieser Produkte sind für Plone gedacht, andere für Zope, das CMF (Content Management Framework) oder Python.</p>
<p>Der andere wichtige Bereich zum Auffinden von Produkten ist das Collective-Projekt auf SourceForge (<a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>). Die dortigen Produkte befinden sich im CVS (Concurrent Versioning System) von SourceForge. Produkte werden zwar oftmals in Archivdateien verbreitet, aber CVS ist die beste Methode, auf diese Produkte hier zuzugreifen.</p>
<p>Zurzeit gibt es kein umfassendes Produktverzeichnis für Plone-Produkte oder deren Entwicklungszustand (aber ich hoffe, eines wird unter Plone.org online verfügbar sein, wenn dieses Buch erscheint). Bei der Veröffentlichung von Produkten stellen Leute ihre Pakete oftmals auf die <em>Dateien</em>-Seite, aber am sichersten ist es, ins CVS zu schauen. Eine visuelle Ansicht aller verfügbaren Dateien finden Sie unter <a class="reference external" href="http://cvs.sourceforge.net/viewcvs.py/collective/">http://cvs.sourceforge.net/viewcvs.py/collective/</a>.</p>
<p>Ein letztes CVS-Repository mit nützlichem Code ist das von Zope Corporation. Fast jeder öffentlich gemachte Code wird in dieses CVS-Repository gestellt. Auch wenn Sie die Quellen zu Zope 2 suchen, ist das der beste Zielort für Sie. Das <tt class="docutils literal">Products</tt>-Verzeichnis enthält alle Produkte (<a class="reference external" href="http://cvs.zope.org/Products/">http://cvs.zope.org/Products/</a>). Weitere Informationen darüber, wie Sie Code auschecken können, finden Sie unter <a class="reference external" href="http://dev.zope.org/CVS/ReadOnlyAccess">http://dev.zope.org/CVS/ReadOnlyAccess</a>.</p>
<div class="sidebar">
<p class="first sidebar-title">Was ist CVS?</p>
<!-- CVS is a system for maintaining control over source code. Most development happens in a source code control system, such as CVS or one of its many similar competing products, such as Subversion, Perforce, BitKeeper, and so on. -->
<p>CVS ist ein System zur Versionskontrolle von Quellcode. Meistens findet die Entwicklung unter einem Versionskontrollsystem wie CVS oder einem seiner vielen ähnlichen Konkurenzprodukte statt, z.B. <em>Subversion</em>, <em>Perforce</em>, <em>BitKeeper</em> usw.</p>
<!-- Checking files out of CVS is simple, and most Unix and Linux users will be familiar with using CVS from the command line. To check out all the products in the collective to your computer (this may take a while), do the following:: -->
<p>Das Auschecken von Dateien aus CVS ist einfach, und die meisten Unix- und Linux-Benutzer werden schon damit vertraut sein, CVS von der Kommandozeile aus zu benutzen. Machen Sie Folgendes, um alle Produkte aus dem Collective-Projekt auf Ihren Rechner auszuchecken (das kann eine Weile dauern):</p>
<pre class="literal-block">
cvs -d:pserver:anonymous&#64;cvs.sourceforge.net:/cvsroot/collective login
</pre>
<!-- Supply a blank password and continue with the following command:: -->
<p>Geben Sie ein leeres Passwort ein, und fahren Sie mit folgendem Befehl fort:</p>
<pre class="literal-block">
cvs -z3 -d:pserver:anonymous&#64;cvs.sourceforge.net:/cvsroot/collective co *.*
</pre>
<!-- Most of the Plone development team using Windows uses TortoiseCVS, which hooks directly into the Windows Explorer shell - m Explorer you can right-click to check in and check out code. For more information on TortoiseCVS, visit http://www.tortoisecvs.org. -->
<p class="last">Die meisten Windows-Benutzer im Plone-Entwicklerteam verwenden <em>TortoiseCVS</em>, das sich direkt in die Windows Explorer-Shell einklinkt, d.h., Sie können Code im Explorer mit einem Rechtsklick ein- und auschecken. Weitere Informationen zu TortoiseCVS finden Sie unter <a class="reference external" href="http://www.tortoisecvs.org">http://www.tortoisecvs.org</a>.</p>
</div>
</div>
<div class="section" id="installation-in-zope">
<h4>Installation in Zope</h4>
<p>Nachdem Sie ein passendes Produkt gefunden und heruntergeladen haben, müssen Sie es installieren. Zuerst müssen Sie es in Zope installieren, damit es von Zope als neues Produkt erkannt wird. Dazu müssen Sie den Bereich finden, in dem sich alle vorhandenen Produkte befinden. Um die Verzeichnisse zu finden, gehen Sie im ZMI (Zope Management Interface) zum Control Panel. Dort sehen Sie eine Liste der Verzeichnisse Ihrer Plone-Instanz. Wenn Sie einen Wert für <tt class="docutils literal">INSTANCE_HOME</tt> haben, dann befindet sich Ihr <tt class="docutils literal">Products</tt>-Verzeichnis in diesem Verzeichnis, sonst finden Sie das <tt class="docutils literal">Products</tt>-Verzeichnis in <tt class="docutils literal">SOFTWARE_HOME</tt>. Es sollte angemerkt werden, dass bei fast allen Installationsmethoden von Plone die Variable <tt class="docutils literal">INSTANCE_HOME</tt> für Sie gesetzt wird. Wie Sie in Abbildung 10.1 sehen, ist der Wert für mein <tt class="docutils literal">INSTANCE_HOME</tt> gleich <tt class="docutils literal">/var/book</tt>, d.h., mein <tt class="docutils literal">Products</tt>-Verzeichnis ist <tt class="docutils literal">/var/book/Products</tt>.</p>
<a class="reference external image-reference" href="img/10-01.png/image_view_fullscreen"><img alt="img/10-01.png" class="original" src="img/10-01.png" /></a>
<p>Abbildung 10.1. Suche nach Ihrem <tt class="docutils literal">Products</tt>-Verzeichnis</p>
<p>Um den Zope-Anteil der Installation durchzuführen, packen Sie das heruntergeladene Produkt aus und kopieren es in das <tt class="docutils literal">Products</tt>-Verzeichnis Ihres Servers. Tatsächlich ist das ein wenig trickreich und hängt sehr stark davon ab, wie das Produkt verpackt wurde. Um das ein wenig detaillierter zu zeigen, erklärt der folgende Abschnitt, wie man CMFExternalFile als Beispielprodukt installiert. CMFExternalFile wird seinerseits im Abschnitt &quot;Eine Datei in Plone verwalten&quot; behandelt.</p>
<p>CMFExternalFile besitzt die nette Eigenschaft, dass es aus zwei Teilen besteht, die separat heruntergeladen werden. Zuerst haben Sie einen Zope-spezifischen Codeteil namens ExternalFile. Sollten Sie dieses Produkt jemals außerhalb von Plone, im normalen Zope, verwenden wollen, dann könnten Sie das tun. Und zweitens haben Sie einen Plone- und CMF-spezifischen Codeteil namens CMFExternalFile. Die meisten Produkte benötigen jedoch keine zwei Installationen, sondern bestehen aus einem einzigen Teil.</p>
<div class="section" id="eine-beispielinstallation-unter-windows-durchfuhren">
<h5>Eine Beispielinstallation unter Windows durchführen</h5>
<p>Zuerst müssen Sie das Produkt von Zope.org unter <a class="reference external" href="http://zope.org/Members/arielpartners/ExternalFile/1.2.0/ExternalFile-1-2-0.zip">http://zope.org/Members/arielpartners/ExternalFile/1.2.0/ExternalFile-1-2-0.zip</a> herunterladen und auf Ihrem Rechner speichern.</p>
<p>Dann packen Sie die Datei aus. Dazu könnten Sie WinZip benutzen, das Sie heutzutage auf den meisten Windows-Rechnern finden (ich ziehe 7-Zip vor, das Sie unter <a class="reference external" href="http://www.7-zip.org">http://www.7-zip.org</a> finden).</p>
<p>Nach dem Auspacken erhalten Sie ein Verzeichnis namens <tt class="docutils literal">ExternalFile</tt>. Darin befindet sich das Produktverzeichnis (siehe Abbildung 10.2). Sie erkennen es daran, dass sich in diesem Verzeichnis eine Menge Python- und Textdateien befinden, darunter auch <tt class="docutils literal">INSTALL.txt</tt> und <tt class="docutils literal">README.txt</tt> mit weiteren Informationen zur Installation.</p>
<a class="reference external image-reference" href="img/10-02.png/image_view_fullscreen"><img alt="img/10-02.png" class="original" src="img/10-02.png" /></a>
<p>Abbildung 10.2. Der Inhalt des <tt class="docutils literal">ExternalFile</tt>-Verzeichnisses</p>
<p>Als Nächstes verschieben Sie den <tt class="docutils literal">ExternalFile</tt>-Ordner (nicht seinen Inhalt) in Ihr <tt class="docutils literal">Products</tt>-Verzeichnis. Unter Windows befindet sich dieses Verzeichnis unter <tt class="docutils literal"><span class="pre">C:\Programme\Plone</span> 2\Data\Products</tt>. In diesem Verzeichnis sehen Sie eine Reihe anderer Verzeichnisse, darunter <tt class="docutils literal">CMFPlone</tt>, <tt class="docutils literal">CMFCore</tt> usw. Das Verzeichnis <tt class="docutils literal">ExternalFile</tt> sollte nun eines davon sein. Nun können Sie zum Abschnitt über das Testen der Installation auf dem Server springen.</p>
</div>
<div class="section" id="eine-beispielinstallation-unter-unix-durchfuhren">
<h5>Eine Beispielinstallation unter Unix durchführen</h5>
<p>Zuerst müssen Sie das Produkt von Zope.org unter <a class="reference external" href="http://zope.org/Members/arielpartners/ExternalFile/1.2.0/ExternalFile-1-2-0.zip">http://zope.org/Members/arielpartners/ExternalFile/1.2.0/ExternalFile-1-2-0.zip</a> herunterladen und auf Ihrem Rechner speichern. Packen Sie dann die Datei aus. Auf den meisten Unix-Systemen ist solch ein unzip-Programm bereits installiert. Geben Sie dann die folgenden Befehle ein:</p>
<pre class="literal-block">
$ unzip ExternalFile-1-2-0.zip
Archive:  ExternalFile-1-2-0.zip
   creating: ExternalFile/CVS/
...
</pre>
<p>Nach dem Auspacken erhalten Sie ein Verzeichnis namens <tt class="docutils literal">ExternalFile</tt>. Darin befindet sich das Produktverzeichnis. Sie erkennen es daran, dass sich in diesem Verzeichnis eine Menge Python- und Textdateien befinden, darunter auch <tt class="docutils literal">INSTALL.txt</tt> und <tt class="docutils literal">README.txt</tt> mit weiteren Informationen zur Installation.</p>
<p>Als Nächstes verschieben Sie den <tt class="docutils literal">ExternalFile</tt>-Ordner (nicht seinen Inhalt) in Ihr <tt class="docutils literal">Products</tt>-Verzeichnis. Dieser Befehl hängt von der Konfiguration Ihres Servers ab. In meinem Fall lautet er wie folgt:</p>
<pre class="literal-block">
$ mv ExternalFile /var/zope/Products
</pre>
</div>
<div class="section" id="die-installation-auf-dem-server-testen">
<h5>Die Installation auf dem Server testen</h5>
<p>Nach der Installation eines Produkts müssen Sie Plone neu starten, damit das neue Produkt in Plone registriert wird. Nach dem Neustart Ihres Servers gehen Sie ins ZMI und zeigen den <em>Product Management</em>-Schirm des Zope-Control Panels an. Dieser Schirm listet alle auf dem Server installierten Produkte auf. Wenn Sie das Produkt erfolgreich installiert haben, wird es hier aufgeführt, wie Sie in Abbildung 10.3 sehen können.</p>
<a class="reference external image-reference" href="img/10-03.png/image_view_fullscreen"><img alt="img/10-03.png" class="original" src="img/10-03.png" /></a>
<p>Abbildung 10.3. Korrekt installierte Produkte</p>
<p>Gelegentlich kann an dieser Stelle eines von drei Dingen schief gehen. Sollte zum einen im ZMI nichts auftauchen, dann haben Sie das Verzeichnis an den falschen Ort kopiert. Korrigieren Sie das, indem Sie die Installationsanweisungen und den Ort Ihres <tt class="docutils literal">Products</tt>-Verzeichnisses noch einmal überprüfen, wie zuvor erklärt wurde.</p>
<p>Zweitens könnten Sie ein &quot;defektes&quot; Icon in der Produktliste sehen, was bedeutet, dass versucht wurde, das Produkt in Zope zu registrieren, wobei aber ein Fehler aufgetreten ist. Klicken Sie auf das defekte Icon, um einen Traceback zu erhalten, der Ihnen den Fehler nennen und eine Möglichkeit bieten sollte, ihn zu beheben.</p>
<p>Und sollten Sie schließlich nach dem Neustart nicht mehr auf das ZMI zugreifen können, dann haben Sie womöglich ein ernsteres Problem. Zope konnte dann nicht gestartet werden, weil Plone einen ernsten Fehler gefunden hat. Um herauszufinden, was das Problem ist, starten Sie Plone von der Kommandozeile im Debug-Modus. Sie erhalten auf dem Bildschirm einen Traceback.</p>
</div>
</div>
<div class="section" id="installation-in-plone">
<h4>Installation in Plone</h4>
<p>Nach der korrekten Installation in Zope ist der nächste Schritt einfach. Um CMFExternalFile vollständig zu installieren, müssen Sie das Produkt CMFExternalFile (<a class="reference external" href="http://prdownloads.sourceforge.net/collective/CMFExternalFile.0.5.zip?download">http://prdownloads.sourceforge.net/collective/CMFExternalFile.0.5.zip?download</a>) nun auf dieselbe Weise installieren, wie Sie ExternalFile installiert haben. Dann müssen Sie Plone wieder neu starten.</p>
<p>Sie müssen CMFExternalFile in <em>allen</em> Plone-Instanzen installieren. Nicht alle, aber die meisten Plone-Produkte verlangen das. Wie man das macht, weiß man nur dann genau, wenn man die Installationsanweisungen anschaut. Wenn Sie etwas lesen wie &quot;auf die normale CMF-Art installieren&quot; oder &quot;erstellen Sie in Ihrer Plone-Instanz eine externe Methode&quot;, dann müssen Sie diesen Schritt ausführen.</p>
<p>Glücklicherweise können Sie die Anweisung, eine externe Methode zu erstellen, sogar ignorieren, da Plone über einen viel einfacheren Weg dafür verfügt. Klicken Sie in Plone auf <em>Plone Konfiguration</em> und dann auf <em>Produkte hinzufügen/löschen</em>. Sie erhalten eine Liste von Produkten, die auf Ihrem Server installiert sind und in Plone konfiguriert werden müssen. Klicken Sie einfach auf das Kontrollkästchen neben dem Produkt (in diesem Fall CMFExternalFile), und klicken Sie dann auf <em>installieren</em>, wie in Abbildung 10.4 zu sehen ist.</p>
<a class="reference external image-reference" href="img/10-04.png/image_view_fullscreen"><img alt="img/10-04.png" class="original" src="img/10-04.png" /></a>
<p>Abbildung 10.4. Eine Liste für den Benutzer verfügbarer Produkte</p>
<p>Damit wird das Produkt installiert - jedenfalls, wenn es keinen Fehler dabei gibt. Sonst erscheint es nicht in dieser Liste der installierten Produkte. Dann können Sie das Problem eventuell dadurch lösen, dass Sie die Protokolldatei lesen. Klicken Sie also auf den Link neben dem Produktnamen, um das Protokoll zu erhalten. Diese Installation ist ein Dienst, der vom Werkzeug <tt class="docutils literal">portal_quickinstaller</tt> in Zope bereitgestellt wird. Um zu sehen, was dieses Produkt tatsächlich tut, springen Sie zum Abschnitt &quot;Integration von Plone mit dem Dateisystem&quot;.</p>
</div>
</div>
<div class="section" id="einen-anderen-webserver-verwenden">
<h3>Einen anderen Webserver verwenden</h3>
<p>Wenn Sie Teil einer Organisation sind, die bereits Websites betreibt, dann verwenden Sie sehr wahrscheinlich eine bestimmte Plattform für den Webserver. Unter <em>Virtual Hosting</em> versteht man die Möglichkeit, mehrere Websites auf einem Server zu betreiben, wobei die Sites über ihre IP- (Internet Protocol) Adressen oder Namen unterschieden werden. Damit kann ein erster Server, z.B. Apache, Anfragen an eine oder mehrere Plone-Instanzen weiterreichen.</p>
<p>Virtual Hosting wird normalerweise mit Hilfe von <em>Proxies</em> erreicht, auch wenn der Einsatz eines Proxy-Servers mit Plone ganz unabhängig von der Anzahl der gehosteten Sites ein wünschenswerter Ansatz ist. Ein Proxy-Server sitzt zwischen einem Client und einem Server und leitet Anfragen von Client und Server weiter. Ein Proxy-Server sollte für den Benutzer transparent sein. In Kapitel 14 werde ich Ihnen zeigen, wie Sie Proxy-Server benutzen können, um die Performance von Plone dramatisch zu steigern.</p>
<p>Obwohl Plone den Webserver benutzt, der Zope zugrunde liegt, funktioniert dieser ZServer sehr gut. Es ist aber kein vollständiger, industrietauglicher Webserver, auf den man die Welt loslassen sollte. Der Server hat einige Probleme, was mögliche Denial-of-Service-(DOS-)Attacken angeht, allerdings sind das in ZServer obskure und schwer zu findende Probleme. Es sind keine Attacken gegen ZServer bekannt, die diese Probleme ausnutzen, was aber vielleicht an deren relativ obskurem Charakter in der realen Welt liegt. ZServer ist nicht speziell als industrietauglicher Server entworfen worden, und da seine Eigenschaften ausreichend vollständig sind, wird er nicht weiterentwickelt. Durch die Aktualisierung eines Servers wie Apache können Sie garantieren, dass Ihre Besucher einen robusten, sicheren Server zu Gesicht bekommen. Wenn Sie natürlich eine Intranet- oder andere Anwendung für vertrauenswürdige Benutzer entwickeln, ist das möglicherweise kein Problem.</p>
<p>Abbildung 10.5 zeigt, wie eine solche Einstellung aussehen könnte. Sie zeigt keine echten Rechner, sondern nur Dienste. Eine Anfrage käme normalerweise aus dem Internet zur Firewall, um dann an Apache und Plone zu gelangen. All diese Dienste könnten auf verschiedenen Rechnern laufen. Der wichtige Punkt ist der, dass nicht vertrauenswürdige Benutzer keinen Zugriff auf Plone erhalten sollten, außer über einen Proxy.</p>
<a class="reference external image-reference" href="img/10-05.png/image_view_fullscreen"><img alt="img/10-05.png" class="original" src="img/10-05.png" /></a>
<p>Abbildung 10.5. Funktionsweise des Virtual Hosting</p>
<p>Das Vorschalten eines Webservers wie Apache vor Plone bringt eine Reihe nützlicher Dienste mit sich, die ZServer nicht hat. Apache bietet z.B. Folgendes: Rewriting von URLs (Uniform Resource Locator), Unterstützung von SSL (Secure Sockets Layer), Caching, Inhaltsdekomprimierung, Virtual Hosting, Proxy-Dienste auf andere Webservices, Prüfung eingehender Anfragen usw. Die am häufigsten gestellte Frage ist die, wie man eine URL wie <a class="reference external" href="http://localhost:8080/Plone">http://localhost:8080/Plone</a> in etwas Freundlicheres wie <a class="reference external" href="http://ihresite.com">http://ihresite.com</a> ändert. Das bezeichnet man als <em>URL-Rewriting</em>. Ein Proxy-Server ist dafür zwar nicht notwendig, aber mit einem solchen ist das viel einfacher.</p>
<p>Eine beliebte Methode für die Proxy-Weiterleitung zu Plone besteht im Einsatz eines HTTP-Proxys (Hypertext Transfer Protocol). Apache bewerkstelligt das mit Hilfe des Moduls <tt class="docutils literal">mod_proxy</tt>. Wenn ein Proxy-Server eine Anfrage nach einer Seite erhält, führt er verschiedene Funktionen aus. Dann wird eine neue Anfrage erzeugt und an den ZServer geschickt. Dessen Antwort wird an den Server und dann an den Client zurückgegeben. Natürlich ist das für den Client alles transparent. Er stellt ganz normale Anfragen an einen Server.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Die alte Methode, Apache mit Plone zu verbinden, ist die über Fast CGI oder Persistent CGI. Diese sind schwieriger zu konfigurieren und sogar langsamer im Betrieb. Zwar existiert eine Menge alter Dokumentation zu diesen Themen, aber heute gibt es effizientere Lösungen. Daher kann ich diese alten Methoden <em>nicht</em> empfehlen.</p>
</div>
<div class="section" id="plone-konfigurieren">
<h4>Plone konfigurieren</h4>
<p>Bevor Sie Ihren Proxy-Webserver konfigurieren, müssen Sie zuerst Plone konfigurieren. Da jeweils nur ein Server einen Port belegen kann, ändern Sie Plone so, dass es einen Port mit einer höheren Nummer belegt. Normalerweise wäre das ein Port, der von der Firewall blockiert wird und von außen nicht erreichbar ist. Beispiel-Ports sind 8080, 9090, 9673 usw. Kapitel 2 enthält Informationen darüber, wie Sie die Ports ändern können, auf denen Ihr Plone-Server läuft.</p>
<p>Als Nächstes möchten Sie wahrscheinlich mit Hilfe von URL-Rewriting die URL Ihrer Site ändern. Da das Plone-Objekt in der Zope-Objektdatenbank (ZODB) liegt und eine ID hat, greift man darauf zu, indem diese ID in die URL gesetzt wird, wie in <a class="reference external" href="http://localhost:8080/Plone">http://localhost:8080/Plone</a>. Um das etwas freundlicher zu machen, müssen Sie die Anfrage an den Webserver von <a class="reference external" href="http://ihresite.com">http://ihresite.com</a> in eine Anfrage an das richtige Objekt in Zope übersetzen. Dafür haben Sie je nach Bedarf zwei leicht unterschiedliche Möglichkeiten. Wenn Sie einen Proxy-Webserver benutzen oder Ihre Sites auf Domain-Namen basieren, können Sie ein Virtual Host Monster (VHM) benutzen. Dies ist ein benutzerfreundliches und mächtiges Objekt, das Ihnen das Leben sehr viel leichter machen wird, daher empfehle ich es wärmstens. Sie benötigen in der Wurzel einer Zope-Instanz nur ein VHM. Das VHM-Objekt sitzt in der Wurzel einer Zope-Site und fängt alle eingehenden Anfragen ab. Dann ändert es die Anfragen, damit sie an den gewünschten Teil von Zope gehen.</p>
<p>Um ein VHM zu erstellen, gehen Sie im ZMI zur Zope-Wurzel und wählen <em>Virtual Host Monster</em> im Dropdown-Menü aus. Im dann erscheinenden Formular geben Sie eine ID ein, z.B. <strong>vhm</strong> (wie die ID genau aussieht, spielt keine Rolle).</p>
<p>Wenn Sie einen Proxy-Webserver vor Plone benutzen, machen Sie an diesem Punkt mit der Konfiguration dieses Webservers im Abschnitt &quot;Konfigurieren des Proxy-Servers&quot; weiter.</p>
<p>Der nächste Schritt ist nur dann notwendig, wenn Sie <em>keinen</em> Proxy-Webserver benutzen. Klicken Sie auf das VHM-Objekt, das Sie im ZMI erstellt haben, und wählen Sie dann den <em>Mappings</em>-Reiter, der eine Liste der verfügbaren Abbildungen von Hosts auf dieses Objekt anzeigt. Eine Abbildung nimmt eine ankommende Anfrage und bildet sie mit der folgenden Syntax auf Plone ab:</p>
<pre class="literal-block">
host/pfad
</pre>
<p>wobei <tt class="docutils literal">host</tt> der abgebildete Hostname und <tt class="docutils literal">pfad</tt> der eigentliche Pfad zu dem Objekt in Zope ist. Beispiel:</p>
<pre class="literal-block">
http://www.einesite.com/Plone
</pre>
<p>Um zu garantieren, dass alle Namensvarianten auf den Pfad abgebildet werden, können Sie in der Abbildung Joker benutzen. Im folgenden Beispiel werden alle Unterdomains von <tt class="docutils literal">einesite.com</tt> abgebildet:</p>
<pre class="literal-block">
*.einesite.com/Plone
</pre>
<p>Um diese Abbildung hinzuzufügen, gehen Sie zum <em>Mapping</em>-Reiter, geben eine Abbildung pro Zeile ein und klicken auf <em>Save</em>. Das bedeutet, Sie können mit den abgebildeten Adressen nicht mehr auf die Wurzel Ihrer Zope-Site zugreifen. Glücklicherweise können Sie aber mit einer IP-Adresse weiterhin auf die Wurzel Ihres Zope-Servers zugreifen. Das funktioniert weiterhin, weil numerische Adressen von der Abbildung nicht betroffen sind. Abbildung 10.6 zeigt, wie sich Abbildung 10.5 ändert, wenn Sie das Rewriting umgehen und direkt über die IP auf den Server zugreifen.</p>
<a class="reference external image-reference" href="img/10-06.png/image_view_fullscreen"><img alt="img/10-06.png" class="original" src="img/10-06.png" /></a>
<p>Abbildung 10.6. Virtual Hosting mit Root-Zugriff</p>
<p>Nun haben Sie eine benannte Domain wie <tt class="docutils literal">einesite.com</tt> auf eine bestimmte Plone-Instanz abgebildet. Wenn eine Anfrage nach diesem Site-Namen ankommt, wird sie zu der Plone-Instanz weitergeleitet.</p>
</div>
<div class="section" id="konfigurieren-des-proxy-servers">
<h4>Konfigurieren des Proxy-Servers</h4>
<p>Nachdem Sie Ihr VHM in Plone hinzugefügt haben, ist es Zeit, den Proxy-Server zu  konfigurieren. Aber die Konfiguration von Proxy-Servern ist vom tatsächlich eingesetzten Server abhängig. Die folgenden Abschnitte behandeln die Eigenarten jedes Servers. Um das Virtual Hosting zum Laufen zu bekommen, müssen Sie allerdings eine URL an Plone weiterleiten, die das VHM-Objekt versteht.</p>
<p>Es gibt einen weiteren Vorteil des Virtual Hosting mit einem Proxy-Server, der erwähnt werden sollte. Die gesamte Konfiguration der Domains nehmen Sie im Proxy-Server, also außerhalb von Plone, vor. Das bedeutet, dass Ihr Systemadministrator ein vertrautes Werkzeug verwalten und benutzen kann, ohne sich um Plone kümmern zu müssen.</p>
<p>Die Weiterleitung funktioniert so, dass eine ankommende Anfrage so manipuliert wird, dass eine Anfrage mit einer speziellen URL an Plone gesendet wird. Diese Anfrage ist manipuliert und enthält alle Informationen, die Plone benötigt, um eine Antwort zu produzieren. Wenn die Antwort produziert und an die fragende Person zurückgeschickt wird, müssen alle URLs korrekt auf Ihre Site zeigen. Das garantiert, dass alle Links innerhalb Ihrer Seite korrekt sind.</p>
<p>Eine URL besteht aus den folgenden drei Hauptkomponenten:</p>
<ul class="simple">
<li>Aus einer IP-Adresse oder einem Hostnamen und Port für den Server, auf dem Plone läuft.</li>
<li>Aus einer IP-Adresse oder einem Hostname dafür, wo Plone laufen soll, damit alle Links in den entstehenden Dokumenten die korrekte URL haben.</li>
<li>Aus dem eigentlichen Objekt in Zope, das erreicht werden soll, und aus der ihm übergebenen URL.</li>
</ul>
<p>Diese Information wird an Plone übergeben, indem die URL in eine lange, komplizierte URL in folgendem Format übersetzt wird (zur Verdeutlichung wurden hier Zeilenenden hinzugefügt):</p>
<pre class="literal-block">
http://[URL to server]:[port]
/VirtualHostBase/[protocol]/[URL]:[port]
/[path to virtual host root]
/VirtualHostRoot/[actual URL]
</pre>
<p>Betrachten Sie folgendes Beispiel:</p>
<ul class="simple">
<li>Plone läuft auf einem Rechner mit der IP-Adresse 192.168.2.1 auf Port 8080. Beachten Sie, dass die IP-Adresse eine ist, auf die der Proxy-Server zugreifen kann. Es ist nicht die IP-Adresse für den Rest der Welt - um diese kümmert sich der Proxy-Server.</li>
<li>Plone sollte unter <tt class="docutils literal">www.meinesite.com</tt> auf Port 80 erscheinen.</li>
<li>Das eigentliche Plone-Objekt findet man unter <tt class="docutils literal">/Plone</tt>.</li>
<li>Die ankommende Anfrage lautet <tt class="docutils literal">/Members/andym</tt>.</li>
</ul>
<p>Daraus wird die folgende lange URL erzeugt (eine Zeile):</p>
<pre class="literal-block">
http://192.168.2.1:8080
/VirtualHostBase/http/www.meinesite.com:80/Plone
/VirtualHostRoot/Members/andym
</pre>
<p>Das macht man deswegen, weil das VHM-Objekt genau weiß, was es mit dieser URL machen soll, wenn es sie sieht. Es verändert sie und sendet die Anfrage an das Plone-Objekt. Ganz offensichtlich wird das Seitenfragment (<tt class="docutils literal">/Members/andym</tt>) für jede Anfrage verschieden sein und muss berechnet werden. Aber wenn Sie wissen, was Sie vorhaben, können Sie Ihren Server nun konfigurieren.</p>
<div class="section" id="apache-konfigurieren">
<h5>Apache konfigurieren</h5>
<p>Apache ist wahrscheinlich der beliebteste Server, der vor Plone gesetzt wird, und ist für alle Linux-, Unix- und Windows-Plattformen verfügbar (<a class="reference external" href="http://httpd.apache.org/">http://httpd.apache.org/</a>). Nach der Installation von Apache müssen Sie Anfragen mit HTTP-Proxies an Plone weitergeben.</p>
<p>Um Apache zu konfigurieren, benötigen Sie den Zugriff auf Apaches Konfigurationsdateien, deren Ort von Ihrer Apache-Installation abhängt. Wenden Sie sich dazu an die Apache-Dokumentation. Unter Windows ist die Konfiguration vom <em>Start</em>-Menü aus erreichbar. Und unter Linux finden Sie die Apache-Konfiguration im Verzeichnis <tt class="docutils literal">/etc</tt> unter <tt class="docutils literal">/etc/apache/httpd.conf</tt> oder <tt class="docutils literal">/etc/apache2/httpd.conf</tt>. Um diese Dateien verändern zu dürfen, müssen Sie normalerweise Root oder ein privilegierter Benutzer sein.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">In diesem Beispiel wird Apache 2 verwendet, aber all diese Befehle sind mit früheren Versionen wie Apache 1.3.2 rückwärtskompatibel. Einige ältere Apache-Versionen (vor 1.3.2) sind allerdings für Probleme mit Cookies bekannt.</p>
</div>
<p>Die einfachste Art eines URL-Rewrite in Apache ist die Verwendung der eingebauten Rewrite- und Proxy-Module. Das bedeutet, dass Apaches Module <tt class="docutils literal">mod_rewrite</tt> und <tt class="docutils literal">mod_proxy</tt> aktiviert werden müssen. In Apache ist jede Site normalerweise in einem Virtual Host-Verzeichnis enthalten, dessen Beschreibung mit dem folgenden Code anfängt:</p>
<pre class="literal-block">
&lt;VirtualHost *:80&gt;
    ServerName ihresite.com
    # other configuration options
</pre>
<p>Sie müssen lediglich Rewrites aktivieren und die Rewrite-Regel (eine Zeile) hinzufügen, etwa so:</p>
<pre class="literal-block">
    RewriteEngine On
    RewriteRule ^/(.*)  http://192.168.2.1:8080
/VirtualHostBase/http/www.meinesite.com:80/Plone
/VirtualHostRoot/$1 [L,P] ~CCC
&lt;/VirtualHost&gt;
</pre>
<p>Die Rewrite-Regel, um die es hier geht, nimmt einen beliebigen ihr übergebenen Anfrage-String und fügt ihn ans Ende Ihrer festcodierten Rewrite-Regel an. Das <tt class="docutils literal">[L,P]</tt> sagt Apache, dass das die letzte Rewrite-Regel ist und sie zum angegebenen Proxy weiterleiten soll. Danach müssen Sie Apache neu starten, um die Konfiguration zu aktualisieren. Weitere Informationen über das Rewriting finden Sie in der Dokumentation zu <tt class="docutils literal">mod_rewrite</tt> unter <a class="reference external" href="http://httpd.apache.org/docs-2.0/misc/rewriteguide.html">http://httpd.apache.org/docs-2.0/misc/rewriteguide.html</a>. Beachten Sie, dass Sie in diesem Fall die Information zur Rewrite-Regel in einer Virtual Host-Direktive gesetzt haben. Sie könnten in Apache auch mehrere solche Virtual Hosts haben, so dass PHP-, Perl- und Java-Sites alle nebeneinander auf einem Server liegen könnten.</p>
</div>
<div class="section" id="squid">
<h5>Squid</h5>
<p>Squid ist wegen seiner mächtigen Caching- und Konfigurationsmöglichkeiten bei Benutzern sehr beliebt. Es ist für Unix verfügbar, und es gibt mit Cygwin übersetzte Pakete für Windows. Ich habe die Windows-Version nicht speziell getestet, aber es heißt, sie funktioniere gut. Sie finden die Dateien zum Herunterladen unter <a class="reference external" href="http://www.squid-cache.org">http://www.squid-cache.org</a>. Die folgenden Bemerkungen gelten für die letzte beim Schreiben dieses Buchs aktuelle stabile Version 2.5.</p>
<p>Die Installation von Squid aus seinem Quellcode ist recht einfach. Geben Sie nach dem Herunterladen folgende Befehle ein:</p>
<pre class="literal-block">
$ tar -xvf squid-2.5.STABLE3.tar.gz
$ cd  squid-2.5.STABLE3
$ ./configure --prefix=/usr/local/squid
...
$ make all
...
$ make install
...
</pre>
<p>Leider kennt Squid keine Rewrite-Regeln, mit denen Sie ankommende Anfragen vor ihrer Weiterleitung ändern können. Aber <em>Squid Guard</em> (<a class="reference external" href="http://www.squidguard.org">http://www.squidguard.org</a>) kann das. Ich habe die Version 1.2.0 getestet. Nach dem Herunterladen geben Sie folgende Befehle zur Installation ein:</p>
<pre class="literal-block">
$ tar -zxvf squidGuard-1.2.0.tar.gz
$ cd squidGuard-1.2.0
$ ./configure
...
$ make
...
$ make install
...
</pre>
<p>Nun sind sowohl Squid als auch SquidGuard bereit, aber es müssen noch beide Konfigurationsdateien eingerichtet werden. Die Konfigurationsdatei für Squid finden Sie unter <tt class="docutils literal">/etc/squid.conf</tt>. Es ist eine lange Datei, in der zum Glück alle Optionen detailliert erklärt werden. Das Folgende sind die wesentlichen Optionen, die man einstellen muss:</p>
<pre class="literal-block">
http_port 80
httpd_accel_host virtual
httpd_accel_port 0

http_access allow all
http_access allow localhost
</pre>
<p>Diese letzten beiden Zeilen sind Sicherheitsregeln, die den Zugriff über einen Browser erlauben. Das sind lasche Regeln, weil sie hinter einer Firewall getestet wurde. Wenn Sie Squid extern betreiben, sollten Sie sich über Zugriffsregeln im Detail informieren. Am leichtesten sichert man diese Regeln ab, indem man <tt class="docutils literal">http_access allow all</tt> in <tt class="docutils literal">http_access deny all</tt> ändert. Fügen Sie abschließend folgende Zeile zur Konfigurationsdatei hinzu:</p>
<pre class="literal-block">
redirect_program /usr/bin/squidGuard -c /etc/squid/squidGuard.conf
</pre>
<p>Das richtet einen Redirect über Squid Guard mit der Konfigurationsdatei unter <tt class="docutils literal">/etc/squid/squidGuard.conf</tt> ein. SquidGuard enthält von sich aus keine Konfigurationsdatei, aber eine Standarddatei, die die Virtual Host-Konfiguration enthält, sieht wie folgt aus:</p>
<pre class="literal-block">
dbhome /var/lib/squidguard/db
logdir /var/log/squid
acl {
    default {
            redirect http://192.168.2.1:8080
/VirtualHostBase/http/www.agmweb.ca:80
/Plone/VirtualHostRoot/%p
  }
 }
</pre>
<p>Schließlich hat Squid die benötigte Konfiguration, um den Datenverkehr so umzuleiten, dass das Host-Monster ihn versteht. Ankommende Anfragen werden dann von Squid behandelt und an Plone weitergegeben.</p>
</div>
<div class="section" id="microsoft-internet-information-services">
<h5>Microsoft Internet Information Services</h5>
<p>Der MS-IIS (Internet Information Services) ist nicht gerade mein bevorzugter Server, aber weil viele Firmen ihn verwenden, habe ich hier einen Abschnitt über IIS aufgenommen. Leider kann IIS keine Weiterleitung auf die gleiche Weise wie Squid und Apache vornehmen, sondern Sie brauchen ein separates Plug-In dafür. Kurz vor der Veröffentlichung dieses Buches wurde ein freier Proxy namens IIS2Zope geschrieben, der genau diese Funktionalität bietet. Allerdings konnte ich ihn noch nicht auf einer Site testen, die performant genug wäre. Weitere Informationen finden Sie unter <a class="reference external" href="http://zope.org/Members/freshlogic/index_html">http://zope.org/Members/freshlogic/index_html</a>.</p>
<p>Stattdessen möchte ich eine freie und einfach einzurichtende Lösung vorstellen. Früher haben Zope-Benutzer PCGI empfohlen, aber mit der Zeit wurde es langsam und schwer zu installieren. Durch die Verwendung der Sprache ASP von Microsoft und einiger Eigenschaften von IIS bekommen Sie eine schnellere Lösung. Sie heißt ASP 404 und führt die Weiterleitung mit Hilfe der Programmiersprache ASP aus.</p>
<p>Laden Sie unter <a class="reference external" href="http://www.zope.org/Members/hiperlogica/ASP404">http://www.zope.org/Members/hiperlogica/ASP404</a> die letzte Version herunter. Ich habe <tt class="docutils literal"><span class="pre">ASP404_1-0-b2.zip</span></tt> getestet. Wenn Sie die geladene Datei auspacken, werden Sie darin eine Datei namens <tt class="docutils literal">default.asp</tt> finden. Nehmen Sie diese Datei, und kopieren Sie sie an die Wurzel der Site, die Sie weiterleiten möchten. Auf meinem Server ist das <tt class="docutils literal"><span class="pre">C:\inetpub\wwwroot</span></tt>.</p>
<p>Als Nächstes müssen Sie das Skript mit der passenden Angabe zum Ort Ihrer Plone-Site konfigurieren. Sie müssen das Skript in einem einfachen Texteditor öffnen und zwei Zeilen ändern, die die Variablen für die Site-Konfiguration enthalten. Konkret: Ändern Sie Zeile 18 von</p>
<pre class="literal-block">
zopeAddress = &quot;http://127.0.0.1:8080&quot;
</pre>
<p>in die Adresse des Zielservers. In diesem Beispiel ist das:</p>
<pre class="literal-block">
zopeAddress = &quot;http://192.168.2.1:8080&quot;
</pre>
<p>Ändern Sie dann Zeile 27 von</p>
<pre class="literal-block">
zopePath = &quot;/&quot;
</pre>
<p>wie folgt in die ID des Plone-Objekts:</p>
<pre class="literal-block">
zopePath = &quot;/Plone&quot;
</pre>
<p>Speichern Sie die Datei, und schließen Sie den Editor. Und schließlich müssen Sie IIS noch beibringen, dass er mit Plone sprechen soll. An dieser Stelle müssen Sie ein bisschen tricksen. Öffnen Sie den Internet Services Manager, den Sie normalerweise irgendwo in der Windows Systemsteuerung finden. Suchen Sie die weiterzuleitende Site, und greifen Sie auf die Site-Eigenschaften zu, wie in Abbildung 10.7 zu sehen ist.</p>
<a class="reference external image-reference" href="img/10-07.png/image_view_fullscreen"><img alt="img/10-07.png" class="original" src="img/10-07.png" /></a>
<p>Abbildung 10.7. Zugriff auf die Eigenschaften der Site</p>
<p>Wählen Sie in den Eigenschaften den Reiter <em>Custom Errors</em>, und scrollen Sie bis zum Fehler für 404 nach unten. Doppelklicken Sie auf den Fehler 404, und ändern Sie ihn wie folgt:</p>
<ul class="simple">
<li><strong>Message Type</strong>: URL</li>
<li><strong>URL</strong>: /default.asp</li>
</ul>
<p>Abbildung 10.8 zeigt die Einstellungen.</p>
<a class="reference external image-reference" href="img/10-08.png/image_view_fullscreen"><img alt="img/10-08.png" class="original" src="img/10-08.png" /></a>
<p>Abbildung 10.8. Die Weiterleitung für 404 einstellen</p>
<p>Speichern Sie die Änderungen mit einem Klick auf OK. Nun sollte Ihre Fehlerliste wie die in Abbildung 10.9 aussehen. Wenn das der Fall ist, sollte es richtig eingestellt sein. Greifen Sie über den Browser auf IIS zu, und Bingo - Sie sehen Plone!</p>
<a class="reference external image-reference" href="img/10-09.png/image_view_fullscreen"><img alt="img/10-09.png" class="original" src="img/10-09.png" /></a>
<p>Abbildung 10.9. Die Fehlerliste</p>
<p>Hier ist Folgendes passiert: Sie fangen den Fehler dafür ab, dass ein Element in IIS nicht gefunden werden kann. Das ASP-Skript, das Sie installiert haben, liest dann die Anfrage und leitet sie an Plone weiter. Dann nimmt es die Antwort entgegen und reicht Sie an IIS und schließlich an den Browser zurück. Das heißt, Sie haben ein einfaches Proxy-Programm zu IIS hinzugefügt.</p>
<p>Hierbei müssen Sie allerdings einige Punkte beachten. Der erste ist der, dass eine Seite nicht gefunden werden darf, wenn die Weiterleitung erfolgen soll, sonst wird das Skript nicht ausgeführt. Das hat Vor- und Nachteile. Sie können zu IIS Ordner und Bilder hinzufügen, die dem Benutzer anstelle derer aus Plone angezeigt werden, falls die Namen auf die Anfrage vom Browser passen. Zweitens wird die ankommende Anfrage geparst und weitergereicht, was in manchen Situationen mit all den möglichen HTTP-Anfragekonfigurationen verwirrend wird. Außerdem werden Sie feststellen, dass all Ihre Plone-Anfragen von IIS als 404-Fehler protokolliert werden, was Analysewerkzeuge für solche Dateien verwirren kann.</p>
<p>Im Großen und Ganzen hat diese Einstellung für die meisten Leute funktioniert, die sie verwendet haben, aber dass es eine Enterprise-Lösung sein kann, die mit jeder Situation fertig wird, ist eher unwahrscheinlich. Auf jeden Fall ist es eine gute Ausgangsbasis für diejenigen, die damit arbeiten und entwickeln möchten.</p>
</div>
<div class="section" id="fehlersuche-bei-proxy-servern">
<h5>Fehlersuche bei Proxy-Servern</h5>
<p>Nachdem Sie den Server eingerichtet und alles neu gestartet haben, werden Sie den Server testen wollen, indem Sie mit Ihrem Browser die Site besuchen. Nachdem Sie das ein paarmal gemacht haben, können Sie folgende Tipps verwenden, falls etwas nicht ganz so funktioniert, wie es sollte:</p>
<ul class="simple">
<li><strong>Testen der Site</strong>: Die goldene Regel bei der Fehlersuche in Proxy-Servern ist die, die Site <em>immer</em> zu testen, indem Sie sich anmelden und Ihren Proxy-Server benutzen. Das können Sie tun, indem Sie direkt auf die IP-Adresse und den Port Ihres Plone-Servers zugreifen. Im vorigen Beispiel können Sie auf die Site zugreifen, indem Sie auf <a class="reference external" href="http://192.168.2.1:8080/Plone">http://192.168.2.1:8080/Plone</a> gehen, und schon haben Sie den Proxy-Server vollständig umgangen. Wenn Sie keine Probleme haben, auf diese Weise auf Plone zuzugreifen und sich darin anzumelden, aber sehr wohl Probleme bekommen, wenn Sie es über den Proxy-Server versuchen, ist es wahrscheinlich, dass etwaige Fehler auf Seiten des Proxy-Servers liegen. Einige ältere Versionen von Apache 1.3 machen bei der Anmeldung Probleme mit Cookies, d.h., Sie sollten auf die neueste Version von 1.3 aktualisieren.</li>
<li><strong>Prüfen der URL</strong>: Überprüfen Sie doppelt, ob Ihr Proxy-Server die richtige URL benutzt, die ziemlich lang und kompliziert sein kann. Unterteilen Sie sie an den Schrägstrichen, um jeden Teil zu untersuchen. Bedenken Sie, dass Sie Plone die richtigen Werte übergeben müssen, damit es die korrekte URL zurückgeben kann. Das heißt, Sie müssen sicherstellen, dass der Abschnitt <tt class="docutils literal"><span class="pre">/[protocol]/[URL]:[port]</span></tt> korrekt ist. Falls Ihre Site z.B. SSL verwendet, stellen Sie sicher, dass der Protokollabschnitt <tt class="docutils literal">https</tt> und nicht <tt class="docutils literal">http</tt> lautet.</li>
</ul>
</div>
</div>
</div>
<div class="section" id="integration-von-plone-mit-dem-dateisystem">
<h3>Integration von Plone mit dem Dateisystem</h3>
<p>Eine Integration von Plone mit dem Dateisystem mag etwas merkwürdig klingen, aber hiermit meine ich, dass man in Plone Inhalte benutzen kann, die im Dateisystem liegen. Natürlich besteht Plone schon aus einer Reihe von Dateien, die im Dateisystem installiert sind und von dort ausgeführt werden. Allerdings werden in Plone alle Inhalte in der ZODB gespeichert, wenngleich viele Leute mir sagen, sie möchten ihre Inhalte im Dateisystem speichern und von dort ausgeben.</p>
<p>Tatsächlich ist es so, dass viele Leute sich Zope und Plone anschauen, kleine Icons für Ordner sehen und annehmen, dass diese direkt den Ordnern und Elementen im Dateisystem entsprechen. Allerdings ist das überhaupt nicht der Fall. Angenommen, Sie würden eine relationale Datenbank benutzen, wie es die meisten Content-Management-Systeme (CMS) tun. Würden Sie das dann immer noch so sehen? Viele Leute denken automatisch, dass das ein Problem sei, aber hier ist eine Liste von Gründen, warum Sie das vielleicht haben möchten:</p>
<ul class="simple">
<li><strong>Sie haben viele sehr große Inhaltselemente</strong>: Plone kann mit sehr großen Dateien ohne große Probleme umgehen. Datenbanken mit mehr als 10 Gigabyte sind nicht so selten und funktionieren einwandfrei. Wenn Sie es mit wirklich großen Inhalten zu tun haben (ein Kunde von mir benutzt Plone, um seine DVDs zu verwalten, d.h. die eigentlichen Inhalte der DVDs), sollten Sie sich CMFExternalFile und Apache anschauen. Bei wirklich großen Sachen ist die Verwendung von Apache oder eines anderen Dienstes zur Ausgabe Ihres Inhalts ein guter Ansatz.</li>
<li><strong>Sie möchten Inhalte mit Programmen verwalten, die vom Dateisystem lesen, z.B. Microsoft Word</strong>: Mit External Editor können Sie Inhalte mit Ihren lokalen Programmen bearbeiten, die in einer Plone-Site gespeichert sind. Wenn Sie Microsoft Word installiert haben, können Sie ein Microsoft Word-Dokument hochladen und es auf Ihrem Rechner mit Microsoft Word bearbeiten.</li>
<li><strong>Sie sind es leid, Code über das Web in kleinen Textbereichen zu bearbeiten</strong>: Schauen Sie sich wieder zuerst External Editor an. Zweitens, warum arbeiten Sie überhaupt über das Web? Wie ich in Kapitel 7 demonstriert habe, können Sie alle Skins und CSS-Templates im Dateisystem schreiben.</li>
<li><strong>Sie können meine Dateisystemwerkzeuge auf den Inhalt anwenden</strong>: Nun, Sie können Plone über FTP (File Transfer Protocol) und WebDAV mounten. Beide bieten dateisystemähnliche Schnittstellen, die mit Plone zusammenarbeiten.</li>
<li><strong>Sie möchten leicht Backups von Ihrem Inhalt anfertigen</strong>: In Kapitel 14 zeige ich, wie man Plone administriert und Backups davon macht und wie man einfache, inkrementelle Backups anlegt. Ein alternativer Speicher namens Directory Storage kann das ebenfalls bewerkstelligen (siehe <a class="reference external" href="http://dirstorage.sf.net">http://dirstorage.sf.net</a>).</li>
<li><strong>Sie möchten CVS/Subversion/BitKeeper oder ein anderes System zur Versionsverwaltung der Inhalte benutzen</strong>: Ja, das macht Sinn, ist aber leider noch nicht vollständig integriert. In zukünftigen Versionen, von denen eine provisorisch Plone 3 heißt, wird das wahrscheinlich integriert sein.</li>
</ul>
<p>Mit diesen Punkten im Hinterkopf werden Sie nun verschiedene Methoden kennen lernen, um Inhalte aus Plone auszugeben, die im Dateisystem existieren.</p>
<div class="section" id="den-proxy-webserver-benutzen">
<h4>Den Proxy-Webserver benutzen</h4>
<p>Nun haben Sie also den Webserver so eingerichtet, wie es zuvor in diesem Kapitel beschrieben wurde. Auf die Gefahr hin, dass ich mich wiederhole: Dieser Webserver kann einfache Inhalte weit besser ausgeben, als Plone das jemals können wird. Wenn Sie eine hohe Anzahl von herunterzuladenden Dateien haben, sollten Sie diese in ein Verzeichnis auf Ihrem Server platzieren, das nicht zu Plone weitergeleitet wird, und von Plone aus Links darauf setzen. Die Benutzer werden einfach auf einen Link klicken und die Dateien ganz normal herunterladen.</p>
<p>In IIS kann man das leicht machen, da IIS automatisch zuerst prüft, ob die Datei existiert, bevor es einen Fehler 404 ausgibt. Apache benötigt dafür nur zwei zusätzliche Zeilen in der Konfiguration (im folgenden Code <tt class="docutils literal">DocumentRoot</tt> und die erste <tt class="docutils literal">RewriteRule</tt>):</p>
<pre class="literal-block">
&lt;VirtualHost *:80&gt;
    ServerName ihresite.com
    # other configuration options
    DocumentRoot /var/downloads
    RewriteEngine On
    RewriteRule ^/download(.*) - [L]
    RewriteRule ^/(.*)  http://192.168.2.1:8080
/VirtualHostBase/http/www.meinesite.com:80
/Plone/VirtualHostRoot/$1 [L,P]
&lt;/VirtualHost&gt;
</pre>
<p>In diesem Beispiel platzieren Sie den Inhalt in <tt class="docutils literal">/var/downloads</tt>, und die URLs zu den über Apache herunterladbaren Inhalten fangen alle mit <tt class="docutils literal">/download</tt> an.</p>
<p>Der Rewrite-Mechanismus sieht dann, dass die URL mit <tt class="docutils literal">/download</tt> anfängt, und wird daher keinerlei Änderungen daran vornehmen, was mit dem Bindestrich (<tt class="docutils literal">-</tt>) ausgedrückt wird. Durch die Angabe des <tt class="docutils literal">[L]</tt> am Zeilenende werden keine weiteren Rewrite-Regeln angewendet, d.h., es findet keine Weiterleitung statt, und Apache macht ganz normal weiter, indem es die Datei ausgibt.</p>
<p>Dieser Trick ist hilfreich, wenn Sie andere Dienste auf dem gleichen Virtual Host anbieten möchten. Auf einer Site habe ich Mailman laufen, einen Mailinglisten-Manager. Alle Mailman-URLs fangen mit <tt class="docutils literal">/mailman</tt> oder  <tt class="docutils literal">/pipermail</tt> an. Nachdem Mailman korrekt eingerichtet und konfiguriert ist, habe ich folgende zwei Zeilen zur Konfiguration hinzugefügt, damit es schön funktioniert:</p>
<pre class="literal-block">
RewriteRule ^/mailman(.*) - [L]
RewriteRule ^/pipermail(.*) - [L]
</pre>
<p>Der einzige Haken hierbei ist, dass Sie keine Objekte zu Plone hinzufügen können, deren Namen mit Ihren Regeln kollidieren, z.B. Ordner mit ähnlichen Namen. In diesem Beispiel wären etwa <tt class="docutils literal">mailman</tt>, <tt class="docutils literal">pipermail</tt> oder <tt class="docutils literal">download</tt> verboten, weil die Benutzer diese Objekte nie sehen würden. Mit dieser Methode könnten Sie den Zugriff auf bestimmte Teile Ihrer Site einschränken, aber ich rate Ihnen, dafür lieber die Sicherheitsmechanismen von Plone zu verwenden. Sonst verwaltet Plone den Inhalt nicht wirklich, d.h., es gibt keine Sicherheit, keinen Workflow und keine Metadaten dazu. Der Inhalt liegt völlig außerhalb von Plone. Eventuell kann das aber auch eine gute Lösung sein.</p>
</div>
<div class="section" id="eine-datei-in-plone-verwalten">
<h4>Eine Datei in Plone verwalten</h4>
<p>CMFExternalFile ist ein Produkt, mit dem Sie Inhalte aus Plone heraus verwalten können, obwohl die Kerninhalte im Dateisystem liegen. Falls Sie ExternalFile und CMFExternal im oberen Teil dieses Kapitel schon installiert haben, dann sind Sie bereit. Wenn nicht, gehen Sie noch einmals zum Abschnitt &quot;Plone-Produkte installieren&quot; zurück.</p>
<p>Nach deren Installation gehen Sie zur Plone-Schnittstelle zurück. Wenn Sie zu Plone gehen, werden Sie bemerken, dass Sie nun einen neuen Inhaltstyp namens <em>External File</em> hinzufügen können. Den Typ External File fügen Sie genauso hinzu wie eine normale Datei, was ich in Kapitel 3 beschrieben habe. Wenn Sie in den Programmcode schauen, werden Sie sogar festellen, dass die gleichen Templates verwendet werden.</p>
<p>Es gibt hierbei allerdings einen kleinen Unterschied. Die Datei wurde tatsächlich im Dateisystem hinzugefügt. Sie wird in ein neues Verzeichnis im <tt class="docutils literal">var</tt>-Verzeichnis Ihrer Plone-Installation platziert. Wenn Sie nicht genau wissen, wo das ist, gehen Sie ins Control Panel, und suchen Sie nach dem Verzeichnis, das von der Instanzwurzel aufgelistet wird. Mein <tt class="docutils literal">var</tt>-Verzeichnis befindet sich in <tt class="docutils literal">/var/zope/var</tt>. Darin ist ein Verzeichnis namens <tt class="docutils literal">externalfiles</tt>. In diesem werden alle Dateien erzeugt, die Sie in Plone hochladen. Wenn Sie sich das Verzeichnis anschauen, sollten Sie die Datei finden, die Sie hochgeladen haben.</p>
<p>Was Sie nun haben, ist eine hybride Speicherlösung, die die Datei im Dateisystem speichert und die Metadaten zu dem Objekt (Beschreibung, Stichwörter etc.) in Plone. Das ist besser als eine Lösung mit nur einem Webserver, weil die Inhalte über Sicherheit, Metadaten usw. verfügen. Wenn Sie wirklich wollten, könnten Sie durch die korrekte Konfiguration Ihres Webservers erreichen, dass der Inhalt von Apache ausgegeben wird, indem Sie das Verzeichnis aus dem <tt class="docutils literal">externalfiles</tt>-Verzeichnis lesen.</p>
</div>
<div class="section" id="ftp-zugriff-auf-plone">
<h4>FTP-Zugriff auf Plone</h4>
<p>FTP ist eine gute Möglichkeit, Inhalte hoch- und herunterzuladen, um sie ohne einen Browser zu bearbeiten. Um FTP in Plone zu aktivieren, müssen Sie sicherstellen, dass es auf dem Server aktiviert ist. Gehen Sie noch einmal zu Kapitel 2, um zu sehen, wie Sie solche Dienste hinzufügen und bearbeiten können. Kurz gesagt: Vergewissern Sie sich, dass Ihre Zope-Konfigurationsdatei, <tt class="docutils literal">zope.conf</tt>, Folgendes enthält:</p>
<pre class="literal-block">
&lt;ftp-server&gt;
    address 21
&lt;/ftp-server&gt;
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Wenn Ihr Server den Port 21 benutzt, müssen Sie sicherstellen, dass kein anderes Programm diesen Port belegt. Außerdem gilt für die meisten Unix-Systeme, dass Sie Ihren Dienst unter Root starten müssen, damit er das Recht hat, einen Port mit einer kleinen Nummer zu belegen. Dazu müssen Sie den effektiven Benutzer in Ihrer Zope-Konfigurationsdatei einstellen. Lesen Sie dazu den Abschnitt &quot;Sicherheit auf Ihrem Server&quot; in Kapitel 9.</p>
</div>
<p>Als Nächstes benötigen Sie einen FTP-Client, um auf den Server zuzugreifen. Unter Windows können Sie einfach den Internet Explorer benutzen, indem Sie die Adresse des Servers in der Adresszeile eingeben. Setzen Sie die Adresse auf den Pfad Ihres Zope-Servers (z.B. <a class="reference external" href="ftp://localhost:8021/">ftp://localhost:8021/</a>), und Sie bekommen Zugriff auf die Objekte in Ihrer Site, wie in Abbildung 10.10 zu sehen ist.</p>
<a class="reference external image-reference" href="img/10-10.png/image_view_fullscreen"><img alt="img/10-10.png" class="original" src="img/10-10.png" /></a>
<p>Abbildung 10.10. FTP-Zugriff im Internet Explorer</p>
<p>Wenn Sie einen Benutzernamen und ein Passwort benötigen, um auf den Server zuzugreifen, dann müssen Sie diese in folgendem Format zur URL hinzufügen: <a class="reference external" href="ftp://benutzer:passwort&#64;localhost:8021/">ftp://benutzer:passwort&#64;localhost:8021/</a>. Es gibt viele andere FTP-Clients, bei denen Sie eine ausgefeiltere Schnittstelle haben können, wenn Sie möchten. Unter Linux sind ebenfalls viele FTP-Clients verfügbar, ob auf der Kommandozeile oder mit einer GUI, z.B. <em>gFTP</em> und <em>Konqueror</em>.</p>
</div>
<div class="section" id="webdav-zugriff-auf-plone">
<h4>WebDAV-Zugriff auf Plone</h4>
<p>WebDAV ist ein System zur Erstellung von Inhalten mit Systemen wie Plone, die HTTP benutzen. Damit können Sie einen Plone-Server auf ein Dateisystem abbilden. Um das zu aktivieren, müssen Sie die Zope-Konfigurationsdatei so bearbeiten, wie ich in Kapitel 2 beschrieben habe, damit die Datei <tt class="docutils literal">zope.conf</tt> Folgendes enthält:</p>
<pre class="literal-block">
&lt;webdav-source-server&gt;
    address 1980
&lt;/webdav-source-server&gt;
</pre>
<p>Für Windows gibt es das Programm  <em>WebDrive</em> unter <a class="reference external" href="http://www.webdrive.com/">http://www.webdrive.com/</a>, samt einer freien Testversion, die Sie ausprobieren können. Nach der Installation von WebDrive fügen Sie eine Verbindung zum Plone-Server hinzu, und dann müssen Sie nur noch aus dem Dateisystem direkt auf Ihr Plone zugreifen, indem Sie zum Windows Explorer gehen, wie in Abbildung 10.11 gezeigt wird.</p>
<a class="reference external image-reference" href="img/10-11.png/image_view_fullscreen"><img alt="img/10-11.png" class="original" src="img/10-11.png" /></a>
<p>Abbildung 10.11. Zugriff auf Ihre Plone-Inhalte mit WebDrive</p>
<p>Unter Unix können Sie <em>Cadaver</em> (<a class="reference external" href="http://www.webdav.org/cadaver">http://www.webdav.org/cadaver</a>) verwenden, einen Client auf der Kommandozeile mit vielen Funktionen. Nach der Installation von Cadaver können Sie eine Verbindung zur Plone-Site von der Kommandozeile aus herstellen. Beispiel:</p>
<pre class="literal-block">
cadaver http://192.168.2.1:8080/Members/Plone
</pre>
</div>
<div class="section" id="inhalte-mit-erweiterten-editoren-bearbeiten">
<h4>Inhalte mit erweiterten Editoren bearbeiten</h4>
<p>Wie ich schon mehrfach betont habe, ist es keine gute Idee, Ihre Benutzer dazu zu zwingen, Inhalte in einem HTML-Textbereich schreiben und bearbeiten zu müssen. Manche Editoren haben dafür eine Lösung parat.</p>
<p>Einer davon ist <em>Epoz</em>, mit dem die Benutzer Dokumente direkt im Browser bearbeiten und ändern können, ohne etwas über HTML wissen zu müssen. Wenn Sie es mit vielen Benutzern zu tun haben, die Inhalte eingeben, können diese nach der Installation von Epoz HTML-Inhalte ändern, ohne dass sie HTML verstehen müssen. Für eine noch ausgefeiltere Bearbeitung können Sie <em>External Editor</em> verwenden, mit dem Sie Inhalte in einem lokalen Programm wie Microsoft Word bearbeiten können.</p>
<div class="section" id="wysiwyg-editor-im-browser">
<h5>WYSIWYG-Editor im Browser</h5>
<p>Die getestete Version von Epoz 0.7.4 finden Sie unter <a class="reference external" href="http://zope.org/Members/mjablonski/Epoz/0.7.4">http://zope.org/Members/mjablonski/Epoz/0.7.4</a>. Epoz verlangt einen modernen Browser, den die meisten Plone-Benutzer aber sowieso benötigen. Die erforderlichen Browser sind Internet Explorer 5.5+, Mozilla 1.3.1+ und Netscape 7.1+.</p>
<p>Laden Sie Epoz herunter, und installieren Sie es wie üblich. Danach müssen Sie Ihre persönlichen Plone-Voreinstellungen so ändern, dass Sie Epoz benutzen können. Melden Sie sich bei Plone an, klicken Sie auf <em>Meine Einstellungen</em>, und wählen Sie dort wieder <em>Meine Einstellungen</em>. Öffnen Sie auf der <em>Meine Einstellungen</em>-Seite das <em>Content Editor</em>-Dropdown-Menü, wählen Sie die Option <em>Epoz</em>, und klicken Sie dann auf <em>Speichern</em>, um Ihre Änderungen zu bestätigen.</p>
<p>Nun haben Sie Ihren Editor gewählt und können zu einem beliebigen Dokument gehen, um dann auf den <em>Bearbeiten</em>-Reiter zu klicken. Sie werden bemerken, dass das Feld <em>Haupttext</em> sich erheblich zu einem erweiterten Editor verändert hat. Der Editor sollte ziemlich selbsterklärend für Sie sein und vertraute Buttons wie <em>B</em> für fett (bold), <em>I</em> für kursiv (italics) usw. enthalten (siehe Abbildung 10.12).</p>
<a class="reference external image-reference" href="img/10-12.png/image_view_fullscreen"><img alt="img/10-12.png" class="original" src="img/10-12.png" /></a>
<p>Abbildung 10.12. Bearbeiten eines Dokuments in Epoz</p>
</div>
<div class="section" id="external-editor">
<h5>External Editor</h5>
<p>External Editor ist ein Werkzeug, das Sie bei allen möglichen Plone-Inhalten, Templates und Code benutzen können. Damit können Sie in einer Plone-Site gespeicherte Objekte mit lokalen Programmen Ihrer Wahl bearbeiten. Sie können etwa ein in Ihrer Plone-Site gespeichertes Microsoft Word-Dokument lokal mit Microsoft Word bearbeiten. Sobald Sie das Dokument speichern, wird es automatisch an Plone gesendet.</p>
<p>External Editor ist in den Plone-Installationspaketen enthalten und wird auf dem Server automatisch eingerichtet. Die Anwendung ist insofern ungewöhnlich, als sie aus zwei Komponenten besteht: einer für den Server und einer für <em>jeden</em> Client, der das Produkt benutzen möchte.</p>
<p><strong>Installation des Server-Produkts</strong></p>
<p>Es ist nicht notwendig, das Server-Produkt zu installieren, falls Sie bei der Installation Ihrer Plone-Site ein Installationsprogramm verwendet haben. Wenn das nicht der Fall ist, dann finden Sie das serverseitige Produkt unter <a class="reference external" href="http://zope.org/Members/Caseman/ExternalEditor">http://zope.org/Members/Caseman/ExternalEditor</a>. Installieren Sie das Produkt auf die übliche Weise, die ich am Anfang dieses Kapitels beschrieben habe, und starten Sie dann Ihr Zope neu.</p>
<p>Melden Sie sich dann in Plone als Administrator an, klicken Sie auf <em>Plone Konfiguration</em>, und wählen Sie dann <em>Portal Einstellungen</em>. Wählen Sie die Option <em>Externe Editoren ermöglichen</em>, um sicherzugehen, dass Sie Objekte mit diesem Werkzeug bearbeiten können.</p>
<p><strong>Installation des Client-Produkts</strong></p>
<p>Dieses Produkt müssen Sie auf jedem Client-Rechner installieren, der auf die Plone-Site zugreift. Genauso wie Sie in Ihrem Browser Flash oder QuickTime installieren würden, installieren Sie den clientseitigen Code von External Editor. In einem Intranet oder auf dem eigenen Rechner kann man das leicht machen, aber auf öffentlichen Rechnern kann das schon schwieriger sein.</p>
<p>Für Windows 2000 und XP laden Sie das ausführbare Windows-Installationsprogramm namens <tt class="docutils literal"><span class="pre">zopeedit-win32-0.7.1.exe</span></tt> herunter. Wenn Sie auf die Datei doppelklicken, geht es mit der grafischen Oberfläche weiter. Sie müssen nur alle Voreinstellungen auswählen. Dabei werden die Optionen für Internet Explorer ausgewählt. Um zu überprüfen, ob das auch funktioniert hat, machen Sie Folgendes:</p>
<ol class="arabic simple">
<li>Im Fenster <em>Arbeitsplatz</em> wählen Sie <em>Extras - Ordneroptionen</em>.</li>
<li>Im Fenster <em>Dateitypen</em> scrollen Sie bis nach unten, wo Sie die Zope-Erweiterung sehen sollten (siehe Abbildung 10.13).</li>
</ol>
<a class="reference external image-reference" href="img/10-13.png/image_view_fullscreen"><img alt="img/10-13.png" class="original" src="img/10-13.png" /></a>
<p>Abbildung 10.13. Dateityp-Konfiguration unter Windows</p>
<p>Unter Unix laden Sie das Archiv namens <tt class="docutils literal"><span class="pre">zopeedit-0.7-src.tar.gz</span></tt> herunter. Dieses müssen Sie dann auspacken und einrichten, wie in den Unix-Installationsanweisungen unter <a class="reference external" href="http://zope.org/Members/Caseman/ExternalEditor/install-unix">http://zope.org/Members/Caseman/ExternalEditor/install-unix</a> beschrieben ist. Folgendes ist ein Beispiel mit der Version 0.7:</p>
<pre class="literal-block">
$ tar -zxf zopeedit-0.7.1-src.tgz
$ cd zopeedit-0.7.1-src
$ python setup.py install
...
</pre>
<p>Nachdem Sie den Client installiert haben, müssen Sie alle Browser konfigurieren, die Sie benutzen möchten. Anweisungen für Konqueror, Galeon und andere Browser sind online unter Zope.org verfügbar. Die folgende schrittweise Konfiguration gilt für Mozilla:</p>
<ol class="arabic simple">
<li>Wählen Sie <em>Edit - Preferences</em>.</li>
<li>Unter <em>Navigator</em> wählen Sie <em>Helper Applications</em>.</li>
<li>Klicken Sie auf den <em>New Type</em>-Button.</li>
<li>Geben Sie eine Beschreibung ein, z.B. <strong>Zope Editor</strong>.</li>
<li>Als <em>MIME-Typ</em> geben Sie <strong>appliation/x-zope-edit</strong> ein.</li>
<li>Als <em>Application</em> wählen Sie die Hilfsanwendung <em>Python-Datei</em>.</li>
<li>Klicken Sie auf <em>OK</em>, und schließen Sie dann die Voreinstellungen.</li>
</ol>
<p>External Editor öffnet einen Editor. Welcher Editor erscheint, hängt vom Inhalt einer Konfigurationsdatei ab. Um den Editor Ihrer Wahl aufzurufen, ändern Sie diese Datei. Sie können sie unter verschiedenen Namen - je nach Ihren Einstellungen - an folgenden Orten finden:</p>
<ul class="simple">
<li>Wenn Sie Plone unter Windows mit einem Installationsprogramm installiert haben, finden Sie die Datei unter <tt class="docutils literal"><span class="pre">C:\Programme\Plone\Zope\pwi\zopeedit.ini</span></tt>.</li>
<li>Wenn Sie unter Windows das separate Installationsprogramm für External Editor verwendet haben, finden Sie die Datei in dem Verzeichnis, wo Sie External Editor installiert haben. Standardmäßig ist das <tt class="docutils literal"><span class="pre">C:\Programme\ZopeExternalEditor\zopeedit.ini</span></tt>.</li>
<li>Unter Unix heißt die Datei <tt class="docutils literal"><span class="pre">.zope-external-edit</span></tt> und befindet sich im Home-Verzeichnis des Benutzers, der das Programm ausführt, z.B. <tt class="docutils literal"><span class="pre">/home/andy/.zope-external-edit</span></tt>. Es ist deswegen im Home-Verzeichnis des Benutzers, weil jeder Benutzer eventuell eigene Einstellungen hat.</li>
</ul>
<p>Diese Datei enthält eine Abbildung von Erweiterungen auf den gestarteten Editor. Um z.B. den Editor für Page Templates zu ändern, finden Sie die folgenden Zeilen mit <tt class="docutils literal"><span class="pre">meta-type:Page-Template</span></tt>:</p>
<pre class="literal-block">
[meta-type:Page Template]
extension=.pt
</pre>
<p>Sie könnten z.B. <em>Scite</em> benutzen, einen freien Texteditor. Um ihn für Page Templates zu verwenden, müssen Sie die Datei wie folgt ändern:</p>
<pre class="literal-block">
[meta-type:Page Template]
extension=.pt
editor=scite
</pre>
<p>Damit External Editor funktioniert, muss jeder Aufruf des Editors einen eigenen Prozess starten. Das heißt, dass das External Editor-Client-Programm diesen Prozess überwachen kann, um zu sehen, wann er beendet wird. Für manche Editoren,  die versuchen, mehrere Dateien im gleichen Prozess zu öffnen, ist das ein Problem. Um z.B. VIM in KDE zu laden, müssen Sie eine separate Shell wie folgt ausführen:</p>
<pre class="literal-block">
editor=konsole -e vim
</pre>
</div>
<div class="section" id="ein-word-dokument-bearbeiten">
<h5>Ein Word-Dokument bearbeiten</h5>
<p>Die Bearbeitung eines Microsoft Word-Dokuments kann man tatsächlich ziemlich einfach einrichten. Sie brauchen auf Ihrem lokalen Rechner nur ein installiertes Microsoft Word. Laden Sie Ihr Microsoft Word-Dokument in Plone als Standarddatei hoch, und zeigen Sie die Datei dann in Plone an. Klicken Sie auf das Icon mit dem kleinen Bleistift in der oberen rechten Ecke Ihrer Seite. Dann wird auf Ihrem Rechner Microsoft Word gestartet, und das Dokument vom Server wird angezeigt. Nun können Sie den Inhalt nach Belieben bearbeiten, und bei einem Klick auf <em>Speichern</em> wird die Datei automatisch in Plone gespeichert.</p>
</div>
<div class="section" id="page-templates-uber-external-editor-bearbeiten">
<h5>Page Templates über External Editor bearbeiten</h5>
<p>Um ein Page Template zu erstellen, verwenden Sie das ZMI. Wenn Sie den Ordner mit dem Inhalt des Page Templates anzeigen, sehen Sie rechts vom Objekt bestimmt ein zusätzliches Icon mit einem Bleistift. Ein Klick auf diesen Bleistift aktiviert External Editor und öffnet das Page Template im gewählten Editor. Sie müssen nur einen guten Editor finden, mit dem Sie in Page Templates schreiben können. Da Page Templates lediglich aus XHTML (Extensible HTML) bestehen, benutze ich einen einfachen Editor, der XML (Extensible Markup Language) unterstützt. Die folgenden Abschnitte behandeln zwei Beispiel-Editoren: <em>Dreamweaver</em> und <em>HTML-Kit</em>.</p>
</div>
<div class="section" id="dreamweaver-mx">
<h5>Dreamweaver MX</h5>
<p>Ändern Sie den Teil <tt class="docutils literal"><span class="pre">[meta-type:Page</span> Template]</tt> der Konfigurationsdatei so, dass er auf Dreamweaver zeigt. In meiner Installation sieht das wie folgt aus:</p>
<pre class="literal-block">
[meta-type:Page Template]
extension=.pt
editor=C:\Programme\Macromedia\Dreamweaver MX\Dreamweaver.exe
</pre>
<p>Ein Klick auf das Bleistift-Icon zur Bearbeitung in External Editor öffnet es nun direkt in Dreamweaver, wie in Abbildung 10.14 zu sehen ist. Leider öffnet Dreamweaver nicht jede Datei in einer separaten Instanz, d.h., Sie können nicht mehr als eine Datei gleichzeitig bearbeiten.</p>
<a class="reference external image-reference" href="img/10-14.png/image_view_fullscreen"><img alt="img/10-14.png" class="original" src="img/10-14.png" /></a>
<p>Abbildung 10.14. Page Templates mit Dreamweaver bearbeiten</p>
</div>
<div class="section" id="html-kit">
<h5>HTML-Kit</h5>
<p>HTML-Kit ist ein freier und mächtiger HTML-Editor und die bevorzugte Wahl vieler Plone-Entwickler. Um HTML-Kit mit External Editor zu benutzen, ändern Sie Ihre Konfigurationsdatei so, dass sie auf HTML-Kit zeigt. In meiner Installation sieht das wie folgt aus:</p>
<pre class="literal-block">
[meta-type:Page Template]
extension=.pt
editor=C:\Programme\Chami\HTML-Kit\Bin\HTMLKit.exe
</pre>
<p>Beim Klick auf das Bleistift-Icon zur Bearbeitung in External Editor wird nun direkt HTML-Kit gestartet. Sie können auch eine Einstellung ändern, mit der alle Dateien in einem separaten Prozess geöffnet werden. Wählen Sie <em>Edit - Preferences - Startup</em>, und aktivieren Sie <em>Limit to a single HTML-Kit instance</em>. Nun werden alle Dateien in einem neuen Prozess geöffnet.</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch11.rst">
    <title>11. Inhaltstypen manipulieren und kategorisieren</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch11.rst</link>
    <description>In diesem Buch habe ich Ihnen gezeigt, wie Sie Inhalte zu Ihrer Site hinzufügen können, und ich habe die in Plone enthaltenen Inhaltstypen wie Dokumente, Bilder  usw. beschrieben. Bisher waren Sie allerdings auf genau diese Inhaltstypen eingeschränkt und auf solche, die Sie in Produkten aus dem Internet gefunden haben. Der mächtigste Teil von Plone, die Manipulation dieser Inhaltstypen, bildet das Hauptthema dieses Kapitels.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Inhaltstypen manipulieren und kategorisieren</h2>
<p>In diesem Buch habe ich Ihnen gezeigt, wie Sie Inhalte zu Ihrer Site hinzufügen können, und ich habe die in Plone enthaltenen Inhaltstypen wie Dokumente, Bilder  usw. beschrieben. Bisher waren Sie allerdings auf genau diese Inhaltstypen eingeschränkt und auf solche, die Sie in Produkten aus dem Internet gefunden haben. Der mächtigste Teil von Plone, die Manipulation dieser Inhaltstypen, bildet das Hauptthema dieses Kapitels.</p>
<p>In diesem Kapitel vergleiche ich die verschiedenen Objekttypen in Plone miteinander. Dadurch erhalten Sie einen Einblick in manche Entwicklungstaktiken für Ihre eigenen Projekte. Dann behandle ich Inhaltstypen und zeige, wie sie in Plone registriert werden. Diese Registrierung bildet die Basis für die Anpassung der Typen an das von Ihnen gewünschte Format. Anschließend fahre ich mit der Kategorisierung von bzw. der Suche in Inhalten fort - Aufgaben, von denen Sie sicher wissen möchten, wie man sie ausführt. Mit diesem Wissen werden Sie wichtige Entscheidungen darüber treffen können, wie Sie Ihre Site entwickeln und neue Inhaltstypen erstellen können.</p>
<p>Lassen Sie mich jetzt also Ihren Appetit auf die Manipulation von Inhaltstypen wecken. Sobald Sie einen völlig neuen Inhaltstyp anpassen können, können Ihre Benutzer fast alles erstellen und bearbeiten, was Sie nur möchten! Zum Beispiel:</p>
<ul class="simple">
<li>Benutzer können ein Bild einer Zellkultur hochladen, das mit gewissen Bildbearbeitungsbibliotheken zerschnitten und manipuliert wird und dann dem Benutzer in einem bestimmten Format präsentiert wird.</li>
<li>Benutzer können eine MP3-Audiodatei hochladen, daraus den Titel und Künstlernamen extrahieren und sie in Plone platzieren.</li>
<li>Sie können einen vollständigen E-Commerce-Shop erstellen, in dem Plone-Benutzer Elemente wie z.B. Kleidungsstücke zum Verkauf anbieten, mit Informationen zu Versandkosten, Größe und Garantie.</li>
<li>Benutzer können ein Microsoft Word-Dokument hochladen und dann so manipulieren, dass gewisse Teile davon ausradiert werden. Benutzer mit einer niedrigen Sicherheitsstufe können dann nur die Dokumente mit ausradierten Passagen sehen.</li>
</ul>
<p>All diese Möglichkeiten und noch mehr stehen Ihnen in Plone zur Verfügung! Es gibt wirklich nur wenige Einschränkungen. Aus diesem Grund ist Plone wahrscheinlich eines der erweiterbarsten und flexibelsten Frameworks, die verfügbar sind. Die einzige wirkliche Einschränkung liegt in Ihrer Fähigkeit, in Python zu programmieren (oder darin, sich jemanden zu leisten, der es in Ihrem Auftrag macht).</p>
<p>In diesem Kapitel behandle ich daher Inhaltstypen im Detail, und dazu zählt auch, wie man sie über das Web registriert und manipuliert. Für die folgenden Abschnitte wird zwar kein Wissen über Python explizit verausgesetzt, aber ich empfehle Ihnen dennoch, sich wenigstens damit vertraut zu machen. Dieses Kapitel enthält auch Informationen über Formulare und darüber, wie man sie validiert.</p>
<p>Wenn Sie eigene Inhaltstypen entwickeln möchten, sollten Sie dieses Kapitel unbedingt lesen, unabhängig davon, ob Sie über das Web oder in Python entwickeln, denn dieses Kapitel behandelt die Bereiche, die man kennen muss, um die Registrierung von Inhaltstypen zu verstehen.</p>
<p>Das nächste Kapitel setzt diese Reise dann fort und bringt Ihnen die wirklich blutigen Details bei der Entwicklung von Inhaltstypen mit Python näher. Danach werden Sie <em>Archetypes</em> verwenden, um das Gleiche mit einem Zehntel an Aufwand zu machen. Mit Archetypes werden Sie dann einige wirklich coole und abgefahrene Sachen machen. Aber jetzt geht es zuerst einmal direkt zu Inhaltstypen!</p>
<div class="section" id="ubersicht-zu-inhaltstypen">
<h3>Übersicht zu Inhaltstypen</h3>
<p>Ich muss gestehen, ich habe in diesem Buch gewisse Begriffe verwendet, die ich nur oberflächlich erklärt habe. Daher wird es jetzt Zeit, das nachzuholen, damit Sie diese besser verstehen können. Folgende Konzepte sind von Bedeutung:</p>
<ul class="simple">
<li><strong>Inhaltstyp (Content Type)</strong>: Das ist eine Art von Inhalt, der in einer Plone-Site registriert ist. Normalerweise, wenn auch nicht immer, ist ein Inhaltstyp etwas, das mit Hilfe der Plone-Schnittstelle von Benutzern einer bestimmten Sicherheitsstufe hinzugefügt und bearbeitet werden kann. Es wird empfohlen, in Plone Inhaltstypen wie Dokumente, Dateien und Bilder voneinander zu trennen. Diese Trennung von Inhalten in verschiedene Typen ist ein grundlegendes Konzept in Plone.</li>
<li><strong>Element und Objekt</strong>: Diese Begriffe beziehen sich auf die eigentliche Instanz von irgendetwas. Es sind sehr überladene Begriffe, d.h., ihre Definition ist normalerweise stark kontextabhängig. Bisher habe ich in diesem Buch diese Begriffe bei einer bestimmten Instanz eines Inhaltstyps benutzt, z.B. bei einem bestimmten Dokument oder Bild. Von nun an werde ich den spezifischen Begriff, nämlich <em>Inhaltstyp</em> verwenden, wenn es um Inhaltstypen geht.</li>
<li><strong>Werkzeug (Tool)</strong>: Ein Werkzeug ist ein Dienst, der in einer Plone-Site vorhanden ist. Auf jeder Plone-Site kann es nur eine Instanz eines Werkzeugs geben. Ein Werkzeug macht von sich aus gar nichts, und externe Benutzer der Anwendung werden nie mitbekommen, wie viele Werkzeuge vorhanden sind oder was sie machen. Aber Inhaltstypen oder Anfragen von Benutzern interagieren mit den Werkzeugen. Einige wichtige Werkzeuge haben Sie bereits gesehen, z.B. <tt class="docutils literal">portal_workflow</tt>, <tt class="docutils literal">portal_skins</tt> und <tt class="docutils literal">portal_actions</tt>.</li>
<li><strong>Zope-Objekt</strong>: Das ist ein Objekt, das in Zope lebt. Es bietet den Benutzern eine bestimmte Funktionalität, und man kann über das ZMI (Zope Management Interface) darauf zugreifen. Aber es ist nichts, worauf Plone selbst zugreifen oder was es steuern würde. Wenn Sie zu einer Plone-Site gehen und auf das ZMI zugreifen, werden Sie eine große Anzahl von Objekten im ZMI sehen. Ein Werkzeug ist ein solches Objekt, die Plone-Site ist ein anderes, und die Cache-Manager sind weitere. Zwischen diesen Objekten gibt es eine Menge an Überlappungen. Plone enthält z.B. einen Inhaltstyp für Bilder und Zope verfügt über ein Bildobjekt. Beide haben ähnliche Aufgaben und funktionieren auf ähnliche Weise, aber Plone kann nur auf eines zugreifen.</li>
</ul>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Obwohl alles in einer Plone-Site in der Zope-Objektdatenbank (ZODB) ein <em>Zope-Objekt</em> ist, benutze ich diesen Begriff, um Objekte zu beschreiben, die keine Werkzeuge oder Instanzen eines Inhaltstyps sind.</p>
</div>
<div class="section" id="wann-man-inhaltstypen-erstellen-sollte">
<h4>Wann man Inhaltstypen erstellen sollte</h4>
<p>Also gut, Sie bauen in Plone Ihre Superanwendung, die Ihnen Ruhm und Ehre und noch dazu ein sicheres Einkommen einbringen wird. Wie werden Sie sie aber strukturieren, und was bauen Sie überhaupt? Nun, das hängt davon ab, was Sie machen wollen, und davon, wie Sie ihre Entwicklung bisher eingeteilt haben. Die folgenden Fragen helfen Ihnen möglicherweise bei der Entscheidung:</p>
<ul class="simple">
<li><strong>Ändern Sie einfach nur Skins und einfache Verhalten, z.B. Portlets?</strong> Sie können in einer Skin fast alles machen, was Sie wollen, außer ein Werkzeug oder einen Inhaltstyp schreiben. Wenn Sie wirklich wollen, können Sie alle Cascading Stylesheets (CSS), Templates und Skripten ändern, die in einer Plone-Site enthalten sind.</li>
<li><strong>Werden Mitglieder Ihrer Site mehrere Kopien eines Elements hinzufügen?</strong> Wenn ja, dann möchten Sie wahrscheinlich einen Inhaltstyp dafür schreiben.</li>
<li><strong>Ist das ein Dienst, den andere Inhaltstypen benutzen könnten?</strong> Wenn ja, dann möchten Sie wahrscheinlich ein Werkzeug schreiben.</li>
<li><strong>Möchten Sie mehrere Kopien von etwas, wollen aber nicht, dass es Mitglieder Ihrer Site erstellen und bearbeiten können?</strong> Wenn ja, dann möchten Sie wahrscheinlich ein Zope-Objekt. Aber Sie sollten noch einmal genau darüber nachdenken, was Sie machen.</li>
</ul>
<p>Normalerweise wird eine Anwendung in mehrere Teile aufgeteilt: ein bis zwei Werkzeuge und ein oder zwei Inhaltstypen. Kapitel 12 behandelt die Erstellung eines Inhaltstyps, der einen Source Code, z.B. einen Python-Schnipsel, nimmt und die Syntax des Codes hervorhebt. Falls Sie diese Syntax-Hervorhebung an anderen Stellen brauchen können, dann könnten Sie sie in ein Werkzeug verwandeln, das von mehreren Inhaltstypen verwendet werden kann. Werkzeuge sind, kurz gesagt, die beste Art, um Funktionalität zu einer Site statt zu einem bestimmten Inhaltstyp hinzuzufügen.</p>
<p>Die Definition der Erstellung eines Inhaltstyps wird normalerweise von den Anforderungen der Benutzer diktiert, die diese Objekte hinzufügen, bearbeiten und steuern müssen. Es kann verlockend sein, mit der Erstellung eines Inhaltstyps für jede Art von Objekt zu beginnen, aber wie bei jeder Art von Entwicklung müssen Sie vorsichtig sein. Wäre es möglich, einen statt zwei Inhaltstypen mit minimalen Unterschieden zu benutzen? Das Wissen darüber, wie man so etwas konfiguriert, kommt mit zunehmender Erfahrung, aber die folgenden paar Kapitel werden bestimmt eine Hilfe sein.</p>
</div>
<div class="section" id="inhaltstypen-konfigurieren">
<h4>Inhaltstypen konfigurieren</h4>
<p>Nun enthält Ihre Plone-Site also Inhaltstypen, aber woher weiß die Plone-Site, wie diese konfiguriert werden? Die Antwort lautet, dass Attribute, Methoden, Sicherheit und Skins für alle Inhaltstypen im Dateisystem in Python und im entsprechenden Code definiert werden. Diese Information genügt Plone, um zu wissen, wie das Produkt benutzt wird. Wie Sie gesehen haben, ist die einzige Ausnahme hiervon der Workflow, der normalerweise außerhalb des Inhaltstyps definiert wird. Manche Produkte haben ihren eigenen Workflow, der als Verhalten zum Inhaltstyp hinzugefügt wird.</p>
<p>In Kapitel 10 habe ich gezeigt, wie Inhaltstypen in zwei Schritten in Plone installiert werden: Zuerst wird das Produkt in Zope installiert, dann wird der Inhaltstyp in <em>jeder</em> Plone-Instanz installiert. Beim zweiten Schritt werden Angaben über den Inhaltstyp installiert, die aus dem Dateisystem stammen und dann in Ihrer Plone-Site installiert werden.</p>
<p>Warum besteht dieser Vorgang aus zwei Phasen? In der zweiten Phase wird eine lokale Kopie des Produkts in Ihrer Plone-Site angelegt, und nun können Sie ändern, wie sich der Inhalt bei Ihnen verhält. Möchten Sie, dass ein Dokumentobjekt andere Reiter oben hat? Oder soll ein Dokumentobjekt anders manipuliert werden, anders aussehen oder sogar völlig anders benannt werden? Kein Problem! Nun können Sie Ihre Plone-Instanz über das Web ändern.</p>
<p>Dieser Ansatz ist der gleiche wie bei <tt class="docutils literal">portal_skins</tt>, in dem Sie eine Skin in Ihrer lokalen Instanz anpassen können. Wenn es im Produkt zu Änderungen kommt und Sie eine neue Version von Plone installieren, betreffen diese Änderungen das Dateisystem. Aber nun können Sie diese Änderungen herunterladen und installieren. Weil Sie die Angaben in Ihrer Datenbank gemacht haben, behalten Sie diese angepasste Version.</p>
<p>Jeder Inhaltstyp in Plone verfügt über eine Einstellung im Werkzeug <tt class="docutils literal">portal_types</tt>. Auch wenn jeder Inhaltstyp nur eine Einstellung im Werkzeug <tt class="docutils literal">portal_types</tt> hat, kann es zu diesem Typ eine unbeschränkte Anzahl von Objekten in Ihrer Datenbank geben. Die Konfiguration wird bei Bedarf zu Rate gezogen, d.h., wenn Sie die Konfiguration ändern, aktualisieren Sie alle Objekte dieses Typs in der Datenbank.</p>
</div>
<div class="section" id="registrierung-von-inhaltstypen-im-werkzeug-portal-types">
<h4>Registrierung von Inhaltstypen im Werkzeug portal_types</h4>
<p>Gehen Sie im ZMI zum Werkzeug <tt class="docutils literal">portal_types</tt>, um auf die Registrierungsinformation zuzugreifen. Dort erhalten Sie eine Liste aller in dieser Plone-Site registrierten Inhaltstypen. Die meisten davon sind als etwas zu erkennen, das Sie mit wenigen Ausnahmen über die Plone-Schnittstelle hinzufügen könnten, z.B. Plone Site, TempFolder usw.</p>
<p>Jedes dieser Objekte ist eine Instanz mit Angaben zum Factory-Typ, was der Name eines bestimmten Konfigurationstyps ist. Klicken Sie auf ein beliebiges dieser Objekte, um auf die Typinformation zuzugreifen. Wenn Sie z.B. ein Ereignis anklicken, erhalten Sie eine lokale Kopie der Information zu dem Inhaltstyp. Über das Web können Sie Ihre Konfiguration ändern. In diesem Formular gibt es folgende Einträge:</p>
<ul class="simple">
<li><strong>Title</strong>: Der Titel des Inhaltstyps.</li>
<li><strong>Description</strong>: Die Beschreibung, die zu diesem Inhaltstyp erscheint. Diese wird benutzt, wenn Sie zu den Ordnerinhalten gehen und <em>Neuen Artikel hinzufügen</em> anklicken, ohne einen Inhaltstyp anzugeben. Dann erscheint eine Liste aller Inhaltstypen und ihrer Beschreibungen.</li>
<li><strong>Icon</strong>: Die ID des Icons, das für diesen Inhaltstyp benutzt wird.</li>
<li><strong>Product metatype</strong>: Der Metatyp für diesen Inhaltstyp. Dieser bildet den Plone-Inhaltstyp auf den Zope-Metatyp ab.</li>
<li><strong>Product name</strong>: Der Name des Produkts, in dem dieser Metatyp definiert ist.</li>
<li><strong>Product factory method</strong>: Die Methode, die von der Produkt-Factory aufgerufen wird, um diesen Inhalt zu erstellen.</li>
<li><strong>Initial view name</strong>: Wird in Plone nicht benutzt.</li>
<li><strong>Implicitly addable</strong>: Gibt an, ob dieser Inhalt zu Plone hinzugefügt werden kann. Wenn dieser Eintrag ausgewählt ist, kann der Inhalt hinzugefügt werden, außer es ist explizit etwas anderes angegeben.</li>
<li><strong>Filter content types</strong>: Falls dieser Inhaltstyp ein Ordner ist, aktivieren Sie das, um die Inhaltstypen zu filtern, die von Benutzern zu diesem Objekt hinzugefügt werden können.</li>
<li><strong>Allowed content types</strong>: Falls dieser Inhaltstyp andere Elemente enthalten kann und <em>Filter content types</em> aktiviert ist, sind nur die in dieser Liste angegebenen Inhaltstypen erlaubt.</li>
<li><strong>Allow discussion</strong>: Setzt den voreingestellten Status für Diskussionen bei allen Inhaltstypen. Falls dieser Eintrag aktiviert ist, können Benutzer den Inhalt diskutieren. Welche Benutzer das tun können, hängt von dem Recht <em>Discuss content</em> ab.</li>
</ul>
<p>Nun werden Sie sich einige Aspekte dieser Registrierungsangaben detaillierter anschauen, wobei ich auch einige Beispiele zeige.</p>
<div class="section" id="wie-andert-man-das-icon-eines-inhaltsyps">
<h5>Wie ändert man das Icon eines Inhaltsyps?</h5>
<p>Wenn Sie z.B. das Icon nicht mögen, das bei einem Inhaltstyp erscheint, müssen Sie lediglich ein neues Bild hochladen und sicherstellen, dass der Wert für das Icon im zuvor beschriebenen Formular gesetzt ist. Icons funktionieren am besten, wenn sie einen transparenten Hintergrund haben und 16 mal 16 Pixel groß sind.</p>
<p>Klicken Sie auf <em>portal_skins</em> und <em>custom</em>, und fügen Sie dann ein neues Bild hinzu. Im Werkzeug <tt class="docutils literal">portal_types</tt> setzen Sie dann den Wert für das Icon auf den Wert der ID des hochgeladenen Objekts. Um zu testen, ob das Icon sich verändert hat, gehen Sie zur Plone-Schnittstelle und schauen sich dort um, wo das Objekt erscheinen könnte. Führen Sie z.B. eine Suche durch, oder sehen Sie im Formular zur Erstellung von Inhalten nach.</p>
</div>
<div class="section" id="aktionen">
<h5>Aktionen</h5>
<p>Wenn Sie sich die Konfiguration von Inhaltstypen in <tt class="docutils literal">portal_types</tt> anschauen, sehen Sie einen <em>Actions</em>-Reiter. Diese Aktionen können auf den Inhaltstyp angewendet werden. Aktionen haben Sie schon kurz in Kapitel 4 gesehen, das eine detaillierte Liste dessen enthält, was unter diesem <em>Actions</em>-Reiter vorkommt.</p>
<p>Erinnern Sie sich daran, dass eine <em>Aktion</em> die Möglichkeit bietet, eine Liste von Angaben zu speichern, die leicht bearbeitet und auf die dann unter verschiedenen Bedingungen zugegriffen werden kann. Im Plone-Portal werden Aktionen oben auf den Seiten mit blauen Reitern dargestellt. Zu jedem Inhaltstyp erscheinen die Aktionen als grüne Reiter in der Seitenmitte.</p>
<p>Wie Sie gesehen haben, werden Aktionen in Werkzeugobjekten gespeichert. Viele Werkzeuge enthalten Aktionen, aber es gibt keine gute Möglichkeit, den Ort einer Aktion in Erfahrung zu bringen. Wenn Sie auf Ihrer Plone-Site eine bestimmte Aktion ändern möchten, müssen Sie zuerst das entsprechende Werkzeug finden, in dem sie gespeichert ist.</p>
<p>Sobald Sie diese Aktion gefunden haben, können Sie sie nach Belieben anpassen. Wenn Sie z.B. eine neue Aktion als grünen Reiter zu einem Dokument hinzufügen möchten, müssen Sie zunächst den richtigen Ort finden. Zu Glück sind die folgenden Tipps recht hilfreich beim Finden von Aktionen:</p>
<ul class="simple">
<li>Wenn Sie eine Aktion zu einem Stück Inhalt suchen, z.B. Anzeigen oder Bearbeiten, dann befindet sie sich im entsprechenden Inhaltstyp im Werkzeug <tt class="docutils literal">portal_types</tt>.</li>
<li>Wenn Sie nach einer Site-Aktion suchen, finden Sie sie im Werkzeug <tt class="docutils literal">portal_action</tt>.</li>
<li>Sollten Sie die Aktion immer noch nicht finden, sehen Sie in einem verwandten Werkzeug nach. So befinden sich z.B. Registrierung und Anmeldung in <tt class="docutils literal">portal_membership</tt>.</li>
<li>Wenn Sie nach allen vorherigen Tipps die Aktion weiterhin nicht finden können, gehen Sie zu <tt class="docutils literal">portal_actions</tt>, um die Liste von Werkzeugen zu sehen, und sehen Sie dort bei allen Anbietern von Aktionen nach.</li>
</ul>
<p>Plone sucht auf folgende Art und Weise nach Aktionen:</p>
<ul class="simple">
<li>Bei einem Objekt werden alle Aktionen abgefragt.</li>
<li>Bei jeder Aktion werden die Eigenschaften <tt class="docutils literal">conditions</tt>, <tt class="docutils literal">permissions</tt> und <tt class="docutils literal">visible</tt> geprüft. Wenn sie durchgehen, wird die Aktion zurückgegeben.</li>
<li>Jede Aktion wird in der Benutzerschnittstelle angezeigt, normalerweise in Form von Reitern am Anfang des Inhalts oder oben auf der Seite.</li>
<li>Die URL dieser Aktion ist die URL des Objekts, bei der die eigentliche <em>Aktion</em> am Ende angefügt wird.</li>
</ul>
<p>Bei einem Dokument unter <a class="reference external" href="http://localhost.com/Plone/Document123">http://localhost.com/Plone/Document123</a> wäre z.B. die URL zum Bearbeiten <a class="reference external" href="http://localhost.com/Plone/Document123/document_edit_form">http://localhost.com/Plone/Document123/document_edit_form</a>. Hierbei sollte Ihnen ein wichtiges Sicherheitsproblem auffallen: Die Werte der Eigenschaften <tt class="docutils literal">conditions</tt>, <tt class="docutils literal">permissions</tt> und <tt class="docutils literal">visible</tt> beziehen sich auf die Anzeige der Aktion in der Liste der Aktionen. Mit anderen Worten: Wenn ein Benutzer wirklich will, kann er die URL ändern und  <a class="reference external" href="http://localhost.com/Plone/Document123/document_edit_form">http://localhost.com/Plone/Document123/document_edit_form</a> eingeben, selbst dann, wenn die Rechte an der Aktion das gar nicht erlauben. Aus diesem Grund sollten Sie immer mit Rechten an den Aktionen arbeiten, die ausgeführt werden. Als Benutzer, der ein Objekt anzeigen, aber nicht bearbeiten kann, können Sie trotzdem die URL ändern, um zum <em>Bearbeiten</em>-Formular zu gelangen. Bis hierher ist noch kein echter Schaden entstanden, da nach dem Abschicken des Formulars die Sicherheit neu überprüft und Ihnen dann die Erlaubnis verweigert wird.</p>
<p>Normalerweise werden Aktionen in Plone als Reiter angezeigt. Da sie aber durch ein Programm aufgerufen werden können, können sie auf beliebige Weise genutzt werden. Um eine Aktion durch ein Programm zu benutzen, rufen Sie die Methode <tt class="docutils literal">listFilteredActionsFor</tt> im Werkzeug <tt class="docutils literal">portal_actions</tt> auf. Bei einem gegebenen Objekt erhalten Sie damit für alle Aktionen eines Objekts ein Python-Dictionary mit Kategorien als Schlüssel:</p>
<pre class="literal-block">
actions = context.portal_actions.listFilteredActionsFor(object)
</pre>
<p>Damit erhalten Sie Folgendes:</p>
<pre class="literal-block">
{'site_actions': [
  {'category': 'site_actions', 'name': 'Small Text',
  'url': &quot;javascript:setActiveStyleSheet('Small Text', 1);&quot;,
  'visible': 1, 'id': 'small_text',
  'permissions': ('View',)
   },
... und so weiter
</pre>
<p>Die grünen Reiter am oberen Rand sind eine Kombination zweier Kategorien: <tt class="docutils literal">object</tt> und <tt class="docutils literal">object_tabs</tt>. Die von der Methode zurückgegebenen Aktionen sind ein Python-Dictionary, dessen Schlüssel die Gruppe der Kategorie für diese Aktion sind. Um also nur an das Aktionsobjekt für eine Kategorie zu kommen, z.B. alle Aktionen in der Kategorie <tt class="docutils literal">object</tt>, können Sie auf nur diesen Schlüssel des Dictionarys zugreifen. So erhalten Sie z.B. mit <tt class="docutils literal"><span class="pre">actions[&quot;object&quot;]</span></tt> eine Liste aller dieser Aktionen:</p>
<pre class="literal-block">
{'category': 'object',
'name': 'Contents',
'url': ' http://localhost:8080/Plone/folder_contents',
'visible': 1,
'id': 'folderContents',
'permissions': ('List folder contents',)},
... und so weiter
</pre>
<p>Sie werden bemerken, dass, solange Sie das zu untersuchende Objekt angeben, das Werkzeug <tt class="docutils literal">portal_types</tt> verwendet wird, um alle Aktionen für Ihren bestimmten <tt class="docutils literal">portal_type</tt> ebenso wie andere relevante Aktionen zu finden.</p>
<p>Wenn Sie einen neuen Reiter zu einem Inhaltstyp hinzufügen möchten, müssen Sie lediglich zu <tt class="docutils literal">portal_types</tt> gehen, dort den Inhaltstyp anklicken und den <em>Actions</em>-Reiter wählen. Dann fügen Sie Ihre Aktion hinzu. Wenn die Aktion für den Inhaltstyp als grüner Reiter erscheinen soll, dann müssen Sie sicherstellen, dass die Kategorie <tt class="docutils literal">object_tabs</tt> lautet.</p>
</div>
<div class="section" id="andere-objekte-im-werkzeug-portal-types">
<h5>Andere Objekte im Werkzeug portal_types</h5>
<p>Wenn Sie sich das Werkzeug <tt class="docutils literal">portal_types</tt> anschauen, werden Sie wahrscheinlich bemerken, dass Sie zu dem Ordner andere Objekttypen hinzufügen können, z.B. <em>DTML Method</em>, <em>External Method</em>, <em>Script (Python)</em> und <em>Scriptable Type Information</em>. Die ersten drei dieser Optionen sollen Unterstützung für die letzte Option in der Liste, <em>Scriptable Type Information</em>, bieten.</p>
<p>Mit <em>Scriptable Type Information</em> können Sie einen Typ mit eigenen Erzeugungsrechten und einem eigenen Erzeugungsskript über das Web definieren, anstatt dass diese für Sie definiert werden. Das kommt eventuell dann in Frage, wenn die vorgegebenen Rechte bei einem Inhaltstyp nicht ausreichen. Diese Option scheint zwar sehr nützlich, aber dennoch habe ich noch nie einen guten Anwendungsfall für <em>Scriptable Type Information</em> gegenüber der normalen Factory-basierten Typinformation gesehen, denken Sie also nicht zu viel darüber nach.</p>
</div>
</div>
<div class="section" id="speichern-von-inhaltstypinformationen-im-dateisystem">
<h4>Speichern von Inhaltstypinformationen im Dateisystem</h4>
<p>Nun haben Sie gesehen, wie diese Information in Zope gespeichert wird, aber natürlich kommt sie irgendwoher aus dem Dateisystem. Diese Information wird normalerweise im Produkt in einem Dictionary gespeichert, das üblicherweise <tt class="docutils literal">factory_type_information</tt> heißt. Listing 11.1 zeigt diese Factory-Information zu dem Produkt <em>Folder</em>, einem Produkt, das Ordner in Plone anzeigt. Sie stammt aus der Datei <tt class="docutils literal">PloneFolder.py</tt> im Verzeichnis <tt class="docutils literal">CMFPlone</tt>.</p>
<p>Listing 11.1. Factory-basierte Typinformation</p>
<pre class="literal-block">
factory_type_information = {
    'id':'Folder',    'meta_type':'Plone Folder',
    'description':&quot;&quot;&quot;\
Plone folders can define custom 'view' actions,\
 or will behave like directory listings without one defined.&quot;&quot;&quot;,
    'icon':'folder_icon.gif',
    'product':'CMFPlone',    'factory':'addPloneFolder',
    'filter_content_types':0,
    'immediate_view':'folder_listing',    'actions':
      ( {
           'id':'view',           'name':'View',
           'action':'string:${folder_url}/',           'permissions':
(CMFCorePermissions.View,),           'category':'folder',         }
    ...
      )
    }
</pre>
<p>Das Python-Dictionary bildet die Formulare ab, die Sie in der Plone-Schnittstelle gesehen haben. So ist z.B. <tt class="docutils literal">'meta_type': 'Plone Folder'</tt> der <tt class="docutils literal">meta_type</tt> des Produkts und erscheint in diesem Feld. Die Aktionen erscheinen als Liste von Dictionaries zu jeder Aktion, und es sind wiederum einfache Schlüssel/Wert-Paare für alle Eigenschaften einer Aktion. Hier habe ich nur die erste Aktion, <em>View</em>, angegeben, aber mittlerweile sollte Ihnen diese Art von Angaben vertraut vorkommen.</p>
</div>
<div class="section" id="einen-neuen-inhaltstyp-aus-einem-vorhandenen-typ-erstellen">
<h4>Einen neuen Inhaltstyp aus einem vorhandenen Typ erstellen</h4>
<p>Unter <em>Umwidmen</em> versteht man die Erstellung mehrerer, leicht verschiedener Kopien des gleichen Typs, ausgehend von der Information eines vorhandenen Inhaltstyps. Eine Umwidmung kann dann eine schnelle und einfache Lösung sein, wenn Sie z.B. einen Typ erstellen möchten, der fast, aber nicht ganz, mit einem Nachrichtenelement identisch ist.</p>
<p>Der große Nachteil dieses Ansatzes ist der, dass Sie außer den Aktionen, Skins und einigen Inhaltstyp-Einstellungen nicht wirklich viel ändern können. Bevor Sie also auf diesem Weg weitermachen, machen Sie sich bitte klar, dass Sie auf diese Punkte beschränkt sind, d.h., Sie können z.B. keine neuen Felder oder Attribute hinzufügen. Auf der Mailing-Liste habe ich viele E-Mails gesehen, in denen ungefähr Folgendes steht: &quot;Ich habe schon so viel gemacht, aber nun will ich die Attribute meiner Pressemitteilung ändern.&quot; Nehmen Sie das als Warnung: Das geht nicht! Wenn Sie mehr machen möchten, sehen Sie sich die nächsten beiden Kapitel an, in denen steht, wie Sie eigene Inhaltstypen schreiben können.</p>
<p>Angenommen, Sie möchten einen Typ Pressemitteilung haben, der einer Nachricht ähnelt, aber auch noch Folgendes macht:</p>
<ul class="simple">
<li>Er hat den Namen <em>Pressemitteilung</em> in der Dropdown-Liste.</li>
<li>Er hat ein anderes Icon.</li>
<li>Er hat einen anderen Workflow als Nachrichtenelemente.</li>
<li>Er wird anders angezeigt.</li>
<li>Er hat die gleichen Datenstrukturen wie ein Nachrichtenelement.</li>
<li>Er behält den Typ eines Nachrichtenelements.</li>
</ul>
<p>Nun, in diesem Fall ist die Umwidmung eines Inhaltstyps ideal. Nehmen Sie in diesem Beispiel die Factory-basierte Typinformation eines Nachrichtenelements, laden Sie sie in das Werkzeug <tt class="docutils literal">portal_types</tt>, und nennen Sie sie dann <em>Pressemitteilung</em>. Dadurch können Sie den gesamten vorhandenen Code und die Information wiederbenutzen, während Sie auch neue Möglichkeiten haben. Gehen Sie im ZMI zu <tt class="docutils literal">portal_types</tt>, und führen Sie folgende Schritte aus:</p>
<ol class="arabic simple">
<li>Wählen Sie <em>Factory-based Type Information</em>.</li>
<li>Geben Sie als ID <strong>Pressemitteilung</strong> ein, und wählen Sie <em>CMF Default: News Item</em> im Feld <em>Use default type information</em>.</li>
<li>Klicken Sie auf <em>Add</em>, um dieses Formular zu beenden.</li>
</ol>
<p>Das ist nun eine Instanz der Konfiguration eines Nachrichtenelements, heißt aber <em>Pressemitteilung</em>. Welchen Vorteil haben Sie davon? Nun, Sie haben jetzt einen weiteren Objekttyp, den Benutzer über das Web hinzufügen können. Damit haben die Benutzer Ihrer Site eine wirklich einfache Möglichkeit, zwischen einer Nachricht und einer Pressemitteilung zu unterscheiden, ohne sich mit Stichwörtern oder Metadaten herumschlagen zu müssen. Diese Objekte erscheinen nun auch bei Suchvorgängen und an allen anderen Stellen als Pressemitteilungen. Nun können Sie die Konfiguration der Pressemitteilung ändern, ohne dass es einen Einfluss auf die Konfiguration von Nachrichten hätte.</p>
<p>Änderungen am Icon wurden in diesem Kapitel bereits angesprochen: Laden Sie das Bild einfach in Ihr Verzeichnis, und ändern Sie dann für eine Pressemitteilung die Icon-Eigenschaft auf der Seite <tt class="docutils literal">portal_types</tt>. Wenn Sie zum <tt class="docutils literal">portal_workflow</tt> gehen, sehen Sie, dass alle Inhaltstypen einen eigenen Workflow haben. Da Sie nun einen neuen Inhaltstyp haben, können Sie gezielt den Workflow von Pressemitteilungen ändern. Vielleicht benötigen Pressemitteilungen eine zusätzliche Überprüfung, oder sie senden nach ihrer Veröffentlichung E-Mails an bestimmte Benutzer. Nun können Sie einen neuen Workflow erstellen, wie ich es in Kapitel 8 gezeigt habe, und ihn an Ihre Pressemitteilungen zuweisen.</p>
<p>Eine neue Anzeige hinzuzufügen bedeutet, das Page Template <tt class="docutils literal">newsitem_view</tt> anzupassen und in etwas Sinnvolles umzubenennen, z.B. in <tt class="docutils literal">pressrelease_view</tt>. Vielleicht möchten Sie diese Datei ändern, um am Seitenende einige Angaben über die Firma zu machen. Beispiel:</p>
<pre class="literal-block">
&lt;h2&gt;About ACME Widget Company&lt;/h2&gt;
&lt;p&gt;Our company is the prime maker of widgets in the world. Founded
in 1980 we've been providing excellent widgets to all parts of the
globe. For more marketing information, please contact: Joe Bloggs,
marketing director.&lt;/p&gt;
</pre>
<p>Nachdem Sie Ihre Änderungen an Ihrem neuen Page Template gespeichert haben, gehen Sie zu den Einstellungen der Pressemitteilung in <tt class="docutils literal">portal_types</tt> zurück und gehen dort auf die <em>Actions</em>-Seite. Ändern Sie die Aktion für die Anzeige einer Pressemitteilung von <tt class="docutils literal">newsitem_view</tt> in <tt class="docutils literal">pressrelease_view</tt>. Immer, wenn Sie nun eine Pressemitteilung anzeigen, wird diese Anzeigeseite angezeigt, wie auch in Abbildung 11.1 zu sehen ist.</p>
<a class="reference external image-reference" href="img/11-01.png/image_view_fullscreen"><img alt="img/11-01.png" class="original" src="img/11-01.png" /></a>
<p>Abbildung 11.1. Ein in Plone geladenes Beispiel-Python-Skript</p>
<p>In diesem Fall habe ich ein Pressemitteilungsobjekt hinzugefügt, und die Fußzeile über ACME Company befindet sich im Template, d.h., die Benutzer müssen sie nicht immer eingeben.</p>
</div>
<div class="section" id="ein-skripting-objekt-erstellen">
<h4>Ein Skripting-Objekt erstellen</h4>
<p>Sobald ein Objekt im Werkzeug <tt class="docutils literal">portal_types</tt> registriert ist, können Sie dann Objektinstanzen davon in Ihrer Plone-Site erzeugen. Die Erzeugung solcher Objekte können Sie auch programmgesteuert mit Skripten durchführen. Das ist beim Erstellen von Objekten nützlich, die auf bestimmten anderen Faktoren basieren oder wenn Objekte en masse erzeugt werden. Plone hat zu diesem Zweck zwei nützliche Script (Python)-Objekte:</p>
<ul class="simple">
<li><strong>generateUniqueId</strong>: Dies erzeugt eine neue eindeutige ID für diesen Objekttyp, z.B. <tt class="docutils literal"><span class="pre">Folder.2003-12-19.7359814582</span></tt>. Sie ist nur in dem Ordner eindeutig, in dem sie erzeugt wird. Wenn Sie schnell viele Objekte erzeugen, kann es sein, dass diese nicht eindeutig sind. Aber im Normalfall ist das gut genug.</li>
<li><strong>invokeFactory</strong>: Dies erwartet eine ID und einen Typnamen. Es erzeugt ein Objekt des angegebenen Typs und weist ihm die angegebene ID zu.</li>
</ul>
<p>Nun werden Sie ein Beispielskript erstellen, das einen Ordner und eine Standardseite darin erzeugt, und in dieser Standardseite setzen Sie einen bestimmten Inhalt. Wenn Ihnen das bekannt vorkommt, dann vielleicht deswegen, weil das immer dann passiert, wenn Sie Mitglied auf einer Site werden und für Sie ein Home-Verzeichnis angelegt wird. Die Typnamen entsprechen der Registrierung im Werkzeug <tt class="docutils literal">portal_types</tt>. Das heißt, wenn Sie einen Ordner erzeugen und ein Dokument darin platzieren möchten, dann müssen Sie die Parameter <tt class="docutils literal">Folder</tt> und <tt class="docutils literal">Document</tt> an das Skript <tt class="docutils literal">invokeFactory</tt> übergeben.</p>
<p>Listing 11.2 zeigt ein Skript, das sich eine eindeutige ID holt und einen Ordner auf Basis dieser ID erstellt. Dann erstellt es in diesem Ordner ein neues Dokument.</p>
<p>Listing 11.2. Holen einer ID und Erstellen eines Ordners</p>
<pre class="literal-block">
##title=Create
##parameters=
# create with a random id
newId = context.generateUniqueId('Folder')

# create a object of type Folder
context.invokeFactory(id=newId, type_name='Folder')
newFolder = getattr(context, newId)

# create a new Document type
newFolder.invokeFactory(id='index.html', type_name='Document')

# get the new page
newPage = getattr(newFolder, 'index.html')
newPage.edit('html', '&lt;p&gt;This is the default page.&lt;/p&gt;')

# return something back to the calling script
return &quot;Done&quot;
</pre>
<p>Wenn Sie das als Script (Python)-Objekt hinzufügen und es im <em>Test</em>-Reiter testen, wird ein Ordner für Sie erzeugt. Interessanterweise werden der Ordner und das Dokument im aktuellen Kontext erzeugt, was auch immer das Kontextobjekt sein mag.</p>
</div>
<div class="section" id="das-inhaltstyp-register">
<h4>Das Inhaltstyp-Register</h4>
<p>Ich habe Ihnen eine Vielzahl verschiedener Zugriffsarten auf Plone gezeigt, z.B. FTP und WebDAV. Wenn Plone Inhalte über einen dieser Zugangswege empfängt, muss es mit diesen Inhalten in der passenden Weise umgehen. Dafür sorgt das Inhaltstyp-Register, das Sie im ZMI unter dem Werkzeug <tt class="docutils literal">content_type_registry</tt> sehen können. Wenn Sie dieses Werkzeug in Zope aufsuchen, werden Sie wahrscheinlich von einem weiteren schlecht entworfenen Formular im ZMI geblendet, aber lassen Sie sich davon nicht entmutigen!</p>
<p>Wenn ein Inhalt über FTP oder WebDAV in Plone hinzugefügt wird, werden die Regeln im Register von oben nach unten ausgeführt, bis eine Übereinstimmung erfolgt. Diese basiert auf den Kriterien dieser Regel, und bei einer Übereinstimmung wird der entsprechende Inhaltstyp für diese Regel erzeugt. Folgendes sind die vier verschiedenen Arten von Kriterien:</p>
<ul class="simple">
<li><strong>major_minor</strong>: Nimmt die zwei Teile (links und rechts vom Schrägstrich) des MIME-Typs (Multipurpose Internet Mail Extensions) einer empfangenen Datei und führt darauf den Vergleich aus. Wenn Sie einen Teil leer lassen, passt dieser auf alles. Ein <tt class="docutils literal">major_minor</tt> mit dem Wert <tt class="docutils literal">image `` (beachten Sie das eine Leerzeichen rechts) passt z.B. auf ``image/jpeg</tt>, <tt class="docutils literal">image/gif</tt>, <tt class="docutils literal">image/png</tt> usw.</li>
<li><strong>extension</strong>: Passt zur Dateinamenserweiterung. Alle Erweiterungen werden mit einem Leerzeichen voneinander getrennt. Das heißt, <tt class="docutils literal">doc pdf</tt> passt auf <tt class="docutils literal">rechnungen.doc</tt> und <tt class="docutils literal">bericht.pdf</tt>.</li>
<li><strong>mimetype_regex</strong>: Führt einen Vergleich mit einem regulären Ausdruck auf dem MIME-Typ durch. Das heißt, <tt class="docutils literal"><span class="pre">*,^j</span></tt> passt auf <tt class="docutils literal">image/jpeg</tt>, <tt class="docutils literal">image/jpg</tt>, <tt class="docutils literal">application/java</tt> usw.</li>
<li><strong>name_regex</strong>: Führt einen Vergleich mit einem regulären Ausdruck auf dem Dateinamen aus. Zum Beispiel passt <tt class="docutils literal">^Rechnung</tt> auf <tt class="docutils literal"><span class="pre">Rechnung-123.pdf</span></tt>, aber nicht auf <tt class="docutils literal"><span class="pre">Keine_Rechnung-123.pdf</span></tt>.</li>
</ul>
<p>Um einen Typ hinzuzufügen, geben Sie den Namen der Regel und den Typ aus dem Dropdown-Menü im Formular am Seitenende ein und klicken auf <em>Add</em>. Dadurch wird am Seitenende eine Regel erstellt. In dieser Regel können Sie ein Muster eingeben, das mit der von Ihnen erstellten Art von Regel übereinstimmt, und den gewünschten Inhaltstyp in der Dropdown-Liste wählen. Dann können Sie <em>Up</em> und <em>Down</em> anklicken, um Ihren Eintrag jeweils nach oben und unten zu verschieben, um dessen Priorität zu verändern.</p>
<p>Ein Beispiel: Neulich habe ich eine digitale Kamera gekauft, und da das Installationsprogramm von Plone unter Windows auch CMFPhoto und PIL einrichtet, dachte ich, ich könnte aus meinen Bildern mit geringem Aufwand ein Online-Fotoalbum erstellen. Zuerst habe ich den FTP-Server aktiviert und bin dann zur Inhaltstyp-Registry gegangen, wo ich eine neue Regel erstellt habe, die von der Erweiterung abhängt, die <tt class="docutils literal">image/jpeg</tt> auf den Inhaltstyp <tt class="docutils literal">photo</tt> abbildet. Dann habe ich diese Regel nach oben über die vorhandene Regel für Bilder verschoben. Anschließend musste ich nur noch meine Fotos in meinen FTP-Client ziehen, und schon wurden sie automatisch in Plone geladen, mit Miniaturbildchen versehen und angezeigt.</p>
</div>
</div>
<div class="section" id="inhalte-suchen-und-kategorisieren">
<h3>Inhalte suchen und kategorisieren</h3>
<p>Wie Sie in Plone nach Inhalten suchen können, haben Sie bereits gesehen, aber nun werde ich ins Detail gehen und zeigen, wie die dahinter liegende Kategorisierung und Suche von Inhalten erfolgt. Das wesentliche Werkzeug, in dem all diese Informationen gespeichert sind, heißt <tt class="docutils literal">portal_catalog</tt>, eine leicht andere und erweiterte Version des zugrunde liegenden Werkzeugs ZCatalog. Eine sehr gute Online-Referenz zum ZCatalog finden Sie unter <a class="reference external" href="http://zope.org/Documentation/Books/ZopeBook/2_6Edition/SearchingZCatalog.stx">http://zope.org/Documentation/Books/ZopeBook/2_6Edition/SearchingZCatalog.stx</a>.</p>
<p>Der Katalog bietet für eine Plone-Site drei Schlüsselelemente: Er erstellt Indizes von Inhalten, enthält Metadaten über diese Inhalte im Index und bietet eine Suchschnittstelle zum schnellen Untersuchen von Inhalten in Ihrer Plone-Site. Von all den verschiedenen Objekten in Ihrer Zope-Site werden nur die eigentlichen Instanzen Ihrer Inhaltstypen katalogisiert. Zope-Objekte, -Werkzeuge und andere Objekte kommen nicht in den Katalog. Aus diesem Grund ist das Katalog-Werkzeug eng an die Inhaltstypen und ihre Benutzung gebunden. Auf den Katalog können Sie mit dem Werkzeug <tt class="docutils literal">portal_catalog</tt> im ZMI zugreifen.</p>
<div class="section" id="inhalte-indizieren">
<h4>Inhalte indizieren</h4>
<p>Als Erstes muss der Katalog Indizes vom Inhalt erstellen. Ein Index bietet vor allem eine Methode, um schnell und effizient im Inhalt zu suchen. Daher muss der Index-Inhalt für den Benutzer nicht klar sein oder Sinn machen. Er muss nur die schnelle und effiziente Suche ermöglichen. Wenn Sie in einer Plone-Site suchen, dann suchen Sie in den Indizes, und der Katalog gibt die zur Anfrage passende Ergebnisliste zurück.</p>
<p>Ein Index fragt ein Plone-Objekt nach einem bestimmten Wert ab, z.B. nach einer Methode oder einem Attribut, und indiziert dann, was immer dieses Objekt bei dieser Anfrage zurückgibt. Wie es den Inhalt tatsächlich indiziert, hängt von der Art des Indexes ab. Tabelle 11.1 führt alle in Plone verfügbaren Indizes auf.</p>
<p>Tabelle 11.1. Verfügbare Index-Typen</p>
<table border="1" class="docutils">
<colgroup>
<col width="22%" />
<col width="78%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>DateIndex</td>
<td>Das dient zur Indizierung von Datumsangaben. Damit können
Sie in Datums- und Zeitangaben suchen.</td>
</tr>
<tr><td>DateIndexRange</td>
<td>Eine effizientere Implementation von <tt class="docutils literal">DateIndex</tt> für
Fälle mit zwei Datumsangaben, z.B. Anfangs- und Enddatum,
bei denen Sie viel darin suchen müssen.</td>
</tr>
<tr><td>FieldIndex</td>
<td>Behandelt jedes Ergebnis automatisch und ermöglicht es
Ihnen, nach allem zu suchen, was der Index enthalten mag.
Er passt bei allen Suchen im Index.</td>
</tr>
<tr><td>KeywordIndex</td>
<td>Nimmt eine Folge von Stichwörtern und teilt sie in
separate Wörter auf. Gibt ein Ergebnis zurück, falls eines
der Stichwörter im Index auf die Anfrage passt. Ideal für
die Suche nach Themen oder Stichwörtern von Objekten.</td>
</tr>
<tr><td>PathIndex</td>
<td>Indiziert den Pfad eines Objekts, z.B.
<tt class="docutils literal">/Members/jane/myDocument</tt>, als Liste von Objekten.
Ermöglicht die Suche im Katalog nach allen Inhalten von
<tt class="docutils literal">Members</tt>, ohne den Ordner abfragen zu müssen. Ein
Pfadindex gibt alles unter dem <tt class="docutils literal">Members</tt>-Ordner zurück.</td>
</tr>
<tr><td>TextIndex</td>
<td>Ein alter Textindex, der Text nimmt, ihn aufteilt und dann
indiziert. Siehe <tt class="docutils literal">ZCTextIndex</tt>.</td>
</tr>
<tr><td>TopicIndex</td>
<td>Erstellt während der Katalogisierung vordefinierte
Ergebnismengen. Nützlich bei oft wiederholten Abfragen.</td>
</tr>
<tr><td>ZCTextIndex</td>
<td>Ein neuer Index, der effiziente Volltext-Suchmöglichkeiten
auf Textteilen bietet. Verfügt über viele verschiedene
Eigenschaften, die später im Detail erörtert werden.</td>
</tr>
</tbody>
</table>
<p>Welche Indizes in einem Katalog definiert sind, können Sie sehen, wenn Sie auf <em>portal_catalog</em> klicken und den <em>Indexes</em>-Reiter wählen. Dann erhalten Sie eine Liste aller in Ihrer Plone-Site definierten Indizes. Die Spalten enthalten den Indexnamen, den Typ, die Anzahl der Treffer und den Zeitpunkt der letzten Änderung im Index. Die verschiedenen Indextypen wurden zuvor kurz behandelt, aber in Tabelle 11.2 finden Sie eine Beschreibung aller Standardindizes einer Plone-Site.</p>
<p>Tabelle 11.2. In Plone eingerichtete Standardindizes</p>
<table border="1" class="docutils">
<colgroup>
<col width="16%" />
<col width="10%" />
<col width="74%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Typ</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Creator</td>
<td>FieldIndex</td>
<td>Der Benutzername der Person, die das Objekt erzeugt hat.</td>
</tr>
<tr><td>Date</td>
<td>FieldIndex</td>
<td>Die Sperrfrist; falls nicht vorhanden, ist es das Datum der letzten Änderung.</td>
</tr>
<tr><td>Description</td>
<td>TextIndex</td>
<td>Das Beschreibungsfeld.</td>
</tr>
<tr><td>SearchableText</td>
<td>ZCTextIndex</td>
<td>Beschreibung, Titel und Rumpf des Objekts als ein suchbarer Text.</td>
</tr>
<tr><td>Subject</td>
<td>KeywordIndex</td>
<td>Die Stichwörter zu einem Element.</td>
</tr>
<tr><td>Title</td>
<td>TextIndex</td>
<td>Der Titel des Elements.</td>
</tr>
<tr><td>Type</td>
<td>FieldIndex</td>
<td>Der Portal-Typ, wie er im Werkzeug <tt class="docutils literal">portal_types</tt> definiert ist.</td>
</tr>
<tr><td>allowedRolesAndUsers</td>
<td>KeywordIndex</td>
<td>Gibt an, wer diesen Inhalt anzeigen kann; eine effiziente Art, das zu untersuchen, damit Sie die Suchergebnisse filtern können.</td>
</tr>
<tr><td>created</td>
<td>FieldIndex</td>
<td>Gibt an, wann das Element erzeugt wurde.</td>
</tr>
<tr><td>effective</td>
<td>FieldIndex</td>
<td>Gibt an, wann das Element freigegeben wird.</td>
</tr>
<tr><td>end</td>
<td>FieldIndex</td>
<td>Nur bei Ereignissen; gibt an, wann das Element beendet wird.</td>
</tr>
<tr><td>expires</td>
<td>FieldIndex</td>
<td>Gibt an, wann das Element abläuft und nicht mehr sichtbar ist.</td>
</tr>
<tr><td>getId</td>
<td>FieldIndex</td>
<td>Die ID eines Elements.</td>
</tr>
<tr><td>id</td>
<td>FieldIndex</td>
<td>Identisch mit <tt class="docutils literal">getId</tt>.</td>
</tr>
<tr><td>in_reply_to</td>
<td>FieldIndex</td>
<td>Bei Diskussionen, ergibt das Element, dem dieser Kommentar gilt.</td>
</tr>
<tr><td>meta_type</td>
<td>FieldIndex</td>
<td>Der dem Element zugrunde liegende Metatyp.</td>
</tr>
<tr><td>modified</td>
<td>FieldIndex</td>
<td>Gibt an, wann das Element zuletzt verändert wurde.</td>
</tr>
<tr><td>path</td>
<td>PathIndex</td>
<td>Der Pfad zum Element.</td>
</tr>
<tr><td>portal_type</td>
<td>FieldIndex</td>
<td>Identisch mit <tt class="docutils literal">Type</tt>.</td>
</tr>
<tr><td>review_state</td>
<td>FieldIndex</td>
<td>Der Zustand des Objekts im Workflow.</td>
</tr>
<tr><td>start</td>
<td>FieldIndex</td>
<td>Nur bei Ereignissen; gibt an, wann das Ereignis beginnt.</td>
</tr>
</tbody>
</table>
<p>Wenn Sie einmal nicht genau wissen sollten, was in einem Index enthalten ist, können Sie sich im ZMI dessen Inhalt anschauen. Klicken Sie auf <em>portal_catalog</em>, und wählen Sie <em>Catalog</em>, womit Sie eine Liste aller gerade katalogisierten Objekte erhalten. Klicken Sie auf ein Objekt, und es erscheint ein Fenster mit dem Inhalt des Indexes und der Metadaten. Die Metadaten kommen zuerst, d.h., Sie müssen nach unten scrollen, um die Indizes zu sehen.</p>
<p>Um Indizes zu ändern, zu löschen oder hinzuzufügen, gehen Sie zum <em>Indexes</em>-Reiter zurück. Benutzen Sie das normale Dropdown-Menü namens <em>Add</em>, um einen neuen Index hinzuzufügen oder einen zu löschen. Wenn Sie einen bestimmten Index neu erstellen möchten, wählen Sie links die gewünschten Indizes und klicken auf den Button <em>Reindex</em>. Wenn Sie einen Index zum Katalog hinzufügen, hat er zunächst keinen Inhalt, d.h., Sie müssen dann auf den Button <em>Reindex</em> klicken, um sicherzugehen, dass Ihr Index auch einen Inhalt hat.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Wenn Ihre Site sehr groß ist, kann diese Indizierung ziemlich viel Zeit und Prozessorleistung in Anspruch nehmen. Das heißt, Sie werden diesen Vorgang vermutlich nicht dann ausführen wollen, wenn das System gerade unter Spitzenlast fährt.</p>
</div>
</div>
<div class="section" id="metadaten">
<h4>Metadaten</h4>
<p>Die vom Katalog zurückgegebenen Ergebnisse sind nicht die gefundenen Objekte, sondern die dazu im Katalog gefundenen Metadaten. Diese Metadaten bestehen aus einer Reihe von Feldern oder Spalten für alle Werte im Objekt. Entsprechend wird für eine Plone-Site eine Liste von Spalten erzeugt, wie es in Tabelle 11.3 beschrieben ist.</p>
<p>Tabelle 11.3. In Plone verfügbare Standard-Metadaten</p>
<table border="1" class="docutils">
<colgroup>
<col width="22%" />
<col width="78%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>CreationDate</td>
<td>Das Datum der Erzeugung des Objekts.</td>
</tr>
<tr><td>Creator</td>
<td>Der Benutzername der Person, die das Objekt erzeugt hat.</td>
</tr>
<tr><td>Date</td>
<td>Die Sperrfrist; falls nicht vorhanden, dann das Datum der letzten Änderung.</td>
</tr>
<tr><td>Description</td>
<td>Das Beschreibungsfeld.</td>
</tr>
<tr><td>EffectiveDate</td>
<td>Die Sperrfrist.</td>
</tr>
<tr><td>ExpiresDate</td>
<td>Das Ablaufdatum.</td>
</tr>
<tr><td>ModificationDate</td>
<td>Das Änderungsdatum.</td>
</tr>
<tr><td>Subject</td>
<td>Die Stichwörter des Objekts.</td>
</tr>
<tr><td>Title</td>
<td>Der Objekttitel.</td>
</tr>
<tr><td>Type</td>
<td>Der <tt class="docutils literal">portal_type</tt> des Objekts.</td>
</tr>
<tr><td>created</td>
<td>Identisch mit <tt class="docutils literal">CreationDate</tt>.</td>
</tr>
<tr><td>effective</td>
<td>Identisch mit <tt class="docutils literal">EffectiveDate</tt>.</td>
</tr>
<tr><td>end</td>
<td>Nur bei Ereignissen, das Ende des Ereignisses.</td>
</tr>
<tr><td>expires</td>
<td>Gibt an, wann das Objekt abläuft.</td>
</tr>
<tr><td>getIcon</td>
<td>Das Icon des Objekts.</td>
</tr>
<tr><td>getId</td>
<td>Die ID des Objekts.</td>
</tr>
<tr><td>getRemoteUrl</td>
<td>Nur bei Links; die URL, auf die der Link zeigt.</td>
</tr>
<tr><td>id</td>
<td>Identisch mit <tt class="docutils literal">getId</tt>.</td>
</tr>
<tr><td>location</td>
<td>Nur bei Ereignissen; gibt an, wo das Ereignis eintritt.</td>
</tr>
<tr><td>meta_type</td>
<td>Der <tt class="docutils literal">meta_type</tt> des Objekts.</td>
</tr>
<tr><td>modified</td>
<td>Gibt an, wann das Objekt verändert wurde.</td>
</tr>
<tr><td>portal_type</td>
<td>Der <tt class="docutils literal">portal_type</tt> des Objekts.</td>
</tr>
<tr><td>review_state</td>
<td>Der Zustand des Objekts im Workflow.</td>
</tr>
<tr><td>start</td>
<td>Nur bei Ereignissen; gibt an, wann das Ereignis eintritt.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="wie-objekte-indiziert-werden">
<h4>Wie Objekte indiziert werden</h4>
<p>Inhaltstypen werden automatisch indiziert, weil sie von einer Klasse namens <tt class="docutils literal">PortalContent</tt> erben, die ihrerseits von einer Klasse namens <tt class="docutils literal">CMFCatalogAware</tt> erbt. Die Klasse <tt class="docutils literal">CMFCatalogAware</tt> enthält den gesamten Code, der sicherstellt, dass beim Hinzufügen, Bearbeiten, Ausschneiden, Kopieren, Löschen oder Umbenennen eines Objekts der Katalog (und ebenso der Workflow) aktuell sind. Das Objekt wird im Wesentlichen an den Katalog übergeben, wo die passende Anweisung aufgerufen wird (indizieren, aus dem Index entfernen usw.).</p>
<p>Der Katalog geht dann alle Indizes durch und fragt alle aus dem Objekt mit Hilfe der Attribute oder Methoden des Objekts ab. Bei den meisten Indizes ist der Name des gesuchten Attributs bzw. der gesuchten Methode identisch mit dem des Indexes. Beim Indexnamen <tt class="docutils literal">Title</tt> würde er nach einem Attribut oder einer Methode namens <tt class="docutils literal">Title</tt> suchen und im Index das Ergebnis ablegen. Dann setzt er diesen Vorgang mit allen anderen Metadatenspalten fort.</p>
<p>Zwei Ausnahmen bei diesem Vorgang sind die Typen <tt class="docutils literal">FieldIndex</tt> und <tt class="docutils literal">TopicIndex</tt>. Wenn Sie einen <tt class="docutils literal">FieldIndex</tt> hinzufügen, können Sie angeben, dass der Index einen anderen Wert als den Namen des Indexes untersucht. Sie könnten z.B. einen Index mit der ID <tt class="docutils literal">getVersion</tt> anlegen, der Versionswerte untersucht. Wie Sie später noch sehen werden, haben manche Indizes Vorteile gegenüber anderen, d.h., es kann sinnvoll sein, verschiedene Indizes für den gleichen Wert zu haben.</p>
<p><tt class="docutils literal">TopicIndex</tt> ist insofern ein anderer Indextyp, als er bei der Erstellung des Inhaltsindexes eine Reihe von Mengen bildet. Wenn Sie viele Suchvorgänge auf allen Bildern durchführen möchten, könnten Sie eine Suche nach <tt class="docutils literal">o.portal_type == 'Image'</tt> hinzufügen. Dazu müssen Sie einen <tt class="docutils literal">TopicIndex</tt> erstellen und dann im <em>Indexes</em>-Reiter auf den Index klicken. Sie können einen Index sogar mit mehreren Ausdrücken erstellen. In Plone wird momentan nirgendwo ein <tt class="docutils literal">TopicIndex</tt> verwendet.</p>
<div class="section" id="wie-indizieren-sie-alle-inhalte-ihrer-plone-site-neu">
<h5>Wie indizieren Sie alle Inhalte Ihrer Plone-Site neu?</h5>
<p>Wenn Sie eine große Anzahl von Änderungen auf Codeebene vorgenommen, ein neues Produkt installiert, Ihr Plone-Wurzelobjekt umbenannt oder verschoben haben, müssen Sie eventuell den gesamten Inhalt auf Ihrer Site neu indizieren. Klicken Sie im ZMI hintereinander auf <em>portal_catalog</em>, <em>Advanced</em> und <em>Update Catalog</em>. Dann wird Ihr Katalog aktualisiert.</p>
<div class="admonition-achtung admonition">
<p class="first admonition-title">Achtung</p>
<p class="last">Diese Aufgabe ist noch aufwendiger als die Neuindizierung nur eines Indexes, und sie kann lange Zeit in Anspruch nehmen und viel Speicher und Rechenzeit benötigen, wenn Ihre Datenbank entsprechend groß ist.</p>
</div>
<div class="sidebar">
<p class="first sidebar-title">Relationale Datenbanken vs. Plone</p>
<!-- The development of content types in Plone is slightly different from developing using a relational database. A common development paradigm these days is LAMPâ"a combination of Linux, Apache, MySQL, and PHP or Perl. In this paradigm, data is stored in a table in the database, and a scripting language provides the application layer to pull the content out of the database and put it into templates. You search the content by sending queries to the database in SQL, using *SELECT* statements. -->
<p>Die Entwicklung von Inhaltstypen in Plone unterscheidet sich leicht von der Entwicklung mit einer relationalen Datenbank. Heute ist LAMP, d.h. eine Kombination von Linux, Apache, MySQL und PHP oder Perl, ein weit verbreitetes Entwicklungsparadigma. Dabei werden Daten in einer Tabelle in der Datenbank gespeichert, und eine Skriptsprache bietet die Anwendungsebene, um den Inhalt aus der Datenbank zu holen und ihn in Templates zu setzen. Inhalte werden dabei gesucht, indem Sie Anfragen an die Datenbank in SQL abschicken, also <tt class="docutils literal">SELECT</tt>-Anweisungen verwenden.</p>
<!-- Plone does this differently with the use of an object database. Any content item can contain any attributes of any type, and the underlying object database takes care of persisting those attributes in the database. For searching, all the objects are then indexed in the *portal_catalog* tool. You have to specifically tell the catalog exactly what attributes you'd like to index. Instead of doing SQL calls, you'd instead use the catalog to examine the indexes. -->
<p>Plone macht das anders, da es eine Objektdatenbank verwendet. Alle Inhaltselemente können beliebige Attribute irgendeines Typs enthalten, und die zugrunde liegende Objektdatenbank kümmert sich um die persistente Speicherung dieser Attribute in der Datenbank. Um suchen zu können, werden dann alle Objekte im Werkzeug <tt class="docutils literal">portal_catalog</tt> indiziert. Sie müssen dem Katalog explizit mitteilen, welche Attribute Sie genau indizieren lassen möchten. Anstatt SQL-Aufrufe zu verwenden, benutzen Sie dann den Katalog, um die Indizes zu untersuchen.</p>
<!-- This difference can be confusing in the development stage, especially since relationships between objects aren't created and maintained as they would be in a relational database application. Instead, there are two common ways to maintain a reference: using a catalog to maintain the relationship through keywords or other values or using a folder to group content. Archetypes, which I'll discuss in Chapter 13, allows you maintain relationships easily. It does so through the catalog. -->
<p class="last">Im Entwicklungsstadium kann dieser Unterschied verwirrend sein, besonders deswegen, weil Beziehungen zwischen Objekten nicht so erzeugt und verwaltet werden, wie es in einer Anwendung der Fall wäre, die auf einer relationalen Datenbank basiert. Stattdessen gibt es zwei übliche Arten, Referenzen zu verwalten: die Verwendung eines Katalogs zur Verwaltung der Beziehung über Stichwörter oder andere Werte, oder die Verwendung eines Ordners zur Gruppierung von Inhalten. Mit Archetypes, die ich in Kapitel 13 behandeln werde, können Sie Beziehungen sehr leicht verwalten. Auch diese verwenden dabei den Katalog.</p>
</div>
</div>
</div>
<div class="section" id="suchen-im-katalog">
<h4>Suchen im Katalog</h4>
<p>Die wichtigste Frage ist natürlich die, wie man im Katalog sucht und dessen Ergebnisse nutzt. Der erste Teil dieser Aufgabe hängt von den Indizes ab, daher behandle ich alle Indizes und zeige Ihnen, wie man darin sucht. Der zweite Teil beinhaltet die Manipulation der Ergebnisse, die ich Ihnen anschließend auch zeigen werde.</p>
<p>Alle folgenden Beispiele sind in Python geschrieben, weil dies die beste Art ist, in einem Katalog zu suchen. Ich zeige Ihnen auch schnell ein Beispiel dafür, wie man das in ein Page Template einbaut. Ich möchte Ihnen nahe legen, Python für die Manipulation des Katalogs einzusetzen, weil es wirklich der beste Platz mit der höchsten Flexibilität ist, um etwas zu machen, ohne sich um die Syntax sorgen zu müssen.</p>
<p>Ganz allgemein bewerkstelligen Sie eine Suche dadurch, dass Sie die Methode <tt class="docutils literal">searchResults</tt> auf dem Objekt <tt class="docutils literal">portal_catalog</tt> aufrufen und dabei eine Reihe von Schlüsselwortparametern übergeben. Zwar gibt es ein paar reservierte Schlüsselwörter, aber der Rest wird direkt auf die Indizes gleichen Namens abgebildet. Wenn Sie also z.B. im Index <tt class="docutils literal">SearchableText</tt> suchen möchten, würden Sie der Suchmethode den Schlüsselwortparameter für <tt class="docutils literal">SearchableText</tt> übergeben. Folgendes sind die reservierten Schlüsselwörter:</p>
<ul class="simple">
<li><strong>sort_on</strong>: Dies ist der Index, mit dem die Ergebnisse sortiert werden sollen, vorausgesetzt, dass der Index eine Sortierung erlaubt (Volltext-Indizes tun das nicht).</li>
<li><strong>sort_order</strong>: Hat einen der Werte <tt class="docutils literal">reverse</tt> oder <tt class="docutils literal">descending</tt>. Wenn nichts angegeben ist, lautet die Voreinstellung <tt class="docutils literal">ascending</tt>.</li>
<li><strong>sort_limit</strong>: Dies ist ein Optimierungshinweis, um die Sortierung etwas zu beschleunigen.</li>
</ul>
<p>Eine allgemeine Suche nach allen Elementen, in denen Plone erwähnt wird, und die in der Reihenfolge von <tt class="docutils literal">Date</tt> veröffentlicht sind, sieht ungefähr wie folgt aus:</p>
<pre class="literal-block">
context.portal_catalog.searchResults(
    review_state = &quot;published&quot;,
    SearchableText = &quot;Plone&quot;,
    sort_order = &quot;Date&quot;
)
</pre>
<p>Die Suche gibt die Schnittmenge der Indexergebnisse zurück, d.h., es werden alle Elemente gefunden, die Plone erwähnen <em>und</em> die veröffentlicht sind. Sie können keine Suche mit der Vereinigungsmenge von Ergebnissen durchführen, aber Sie könnten mehrere Ergebnisse bekommen und diese zusammensetzen, was allerdings ein eher unüblicher Fall ist.</p>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Wenn Sie eine Suche ohne Werte durchführen, wird der gesamte Inhalt des Katalogs zurückgegeben. Standardmäßig werden bei allen Suchvorgängen Werte für Sperrfrist und Ablaufdatum angegeben, um sicherzustellen, dass Sie nur Inhalte zwischen diesen Zeitpunkten sehen, es sei denn, der Benutzer, der die Suche ausführt, verfügt über das Recht <em>Access inactive portal content</em>.</p>
</div>
<div class="section" id="suche-in-einem-feld-oder-datumsindex">
<h5>Suche in einem Feld- oder Datumsindex</h5>
<p>Bei der Suche in einem <tt class="docutils literal">FieldIndex</tt> übergeben Sie den Wert des Feldes. Alle passenden Treffer werden zurückgegeben. Hiermit z.B. können Sie alle Bilder in einer Site suchen:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
    Type = &quot;Image&quot;
)
</pre>
<p>Ein Feldindex kann auch einen Bereich von Objekten annehmen, wobei dann der Index versucht, alle Werte dazwischen zu finden, indem er einen Vergleich der Werte vornimmt. Dieser Bereich könnte zwischen zwei Datumsangaben, zwei Zahlen oder zwei Strings liegen, es hängt wirklich nur vom Wert von <tt class="docutils literal">FieldIndex</tt> ab. Dabei übergeben Sie ein Dictionary an den Index statt nur eines Strings. Das Dictionary sollte zwei Werte enthalten: eine Liste namens <tt class="docutils literal">query</tt> mit den zu testenden Werten und einen Wertebereich. Dieser Bereich ist einer der folgenden Strings:</p>
<ul class="simple">
<li><strong>min</strong>: Alles, was größer ist als das kleinste Element</li>
<li><strong>max</strong>: Alles, was kleiner ist als das größte Element</li>
<li><strong>minmax</strong>: Alles zwischen dem kleinsten und dem größten Element</li>
</ul>
<p>Verwenden Sie z.B. Folgendes, um alle Ereignisse mit einer Startzeit größer als jetzt (mit anderen Worten alles, was in der Zukunft liegt) zu finden:</p>
<pre class="literal-block">
from DateTime import DateTime
now = DateTime()
results = context.portal_catalog.searchResults(
       Type = &quot;Event&quot;
       end = { &quot;query&quot;: [now,],
                &quot;range&quot;: &quot;min&quot; }
)
</pre>
<p>Um Elemente in einem Bereich zu suchen, z.B. alle Nachrichten im Dezember, müssen Sie die Start- und Endzeiten des Monats berechnen. Mit diesen Daten können Sie dann die folgende Abfrage erstellen:</p>
<pre class="literal-block">
from DateTime import DateTime
start = DateTime('2004/12/01')
end = DateTime('2004/12/31')
results = context.portal_catalog.searchResults(
        Type = &quot;News Item&quot;,
        created = { &quot;query&quot;: [start, end],
                         &quot;range&quot;: &quot;minmax&quot; }
)
</pre>
<p>Datumsindizes funktionieren auf die gleiche Weise wie Feldindizes, und oftmals werden Sie Datumsangaben innerhalb von Feldindizes sehen, was prima funktioniert.</p>
</div>
<div class="section" id="suche-in-einem-keywordindex">
<h5>Suche in einem KeywordIndex</h5>
<p>Ein <tt class="docutils literal">KeywordIndex</tt> gibt standardmäßig alle Werte zurück, die im Stichwortindex gefunden werden. Der einzige <tt class="docutils literal">KeywordIndex</tt> ist <tt class="docutils literal">Subject</tt>. Das ist das Stichwort, das ein Benutzer einem Objekt mit Hilfe des <em>Eigenschaften</em>-Reiters in der Plone-Schnittstelle zugewiesen hat. Benutzen Sie Folgendes, um nach allen Elementen mit dem Stichwort <tt class="docutils literal">Afrika</tt> zu suchen:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
        Subject = &quot;Afrika&quot;
)
</pre>
<p>Ähnlich wie beim <tt class="docutils literal">FieldIndex</tt> kann auch an <tt class="docutils literal">KeywordIndex</tt> eine kompliziertere Abfrage mit mehreren Objekten und einem and/or-Operator übergeben werden (<tt class="docutils literal">or</tt> ist die Vorgabe). Damit können Sie alle Objekte mit einer fast beliebigen Kombination von Stichwörtern finden. Benutzen Sie z.B. Folgendes, um alle Objekte zu finden, die die Stichwörter <tt class="docutils literal">Afrika</tt> und <tt class="docutils literal">Sonne</tt> enthalten:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
        Subject = { &quot;query&quot;: [&quot;Afrika&quot;, &quot;Sonne&quot;],
                     &quot;operator&quot;: &quot;and&quot; }
)
</pre>
</div>
<div class="section" id="suchen-in-einem-pfadindex">
<h5>Suchen in einem Pfadindex</h5>
<p>Mit einem Pfadindex können Sie in allen Objekten eines bestimmten Pfades suchen. Es werden alle Objekte unterhalb eines bestimmten Ortes angegeben. Wenn Sie also nach allen Objekten in <tt class="docutils literal">Members</tt> fragen, erhalten Sie alles, was in den Home-Verzeichnissen aller Mitglieder enthalten ist. Eine solche Suche nach allem, was <tt class="docutils literal">Members</tt> im Pfad hat, sieht wie folgt aus:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
        path = &quot;/Plone/Members&quot;
)
</pre>
<p>Wenn Sie das weiter einschränken möchten, können Sie das tun, indem Sie einen Ebenenparameter übergeben, der angibt, wo Sie das Element erwarten. Die Ebene ist eine Zahl, die für seine Position im Pfad steht, und zwar von links aus gesehen, wenn man ihn an den Schrägstrichen aufteilt. Im vorherigen Code z.B. liegt <tt class="docutils literal">Plone</tt> auf der Ebene 0, <tt class="docutils literal">Members</tt> auf der Ebene 1 usw. Ähnlich wie beim <tt class="docutils literal">KeywordIndex</tt> können Sie auch hier einen and/or-Operator übergeben. Um z.B. alle Objekte in den Ordnern <tt class="docutils literal">/Plone/Members/danae</tt> und <tt class="docutils literal">/Plone/testing/danae</tt> zu erhalten, benutzen Sie Folgendes:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
        path = { &quot;query&quot;: [&quot;danae&quot;],
                &quot;level&quot; : 2 }
)
</pre>
</div>
<div class="section" id="suchen-im-zctext-index">
<h5>Suchen im ZCText-Index</h5>
<p><tt class="docutils literal">ZCTextIndex</tt> ist der komplizierteste aller Indizes und verfügt über eine große Zahl von Optionen. Jeder <tt class="docutils literal">ZCTextIndex</tt> benötigt ein Lexikon, aber Plone erzeugt und konfiguriert glücklicherweise all das von sich aus. Wenn Sie auf <em>portal_catalog</em> klicken, den <em>Contents</em>-Reiter wählen und dann auf <em>plone_lexicon</em> klicken, können Sie die Standardkonfiguration des Lexikons sehen. Mit einem Klick auf den <em>Query</em>-Reiter sehen Sie alle Wörter, die im Lexikon enthalten sind, das aus den Inhalten Ihrer Plone-Site erstellt wurde.</p>
<p>Der <tt class="docutils literal">ZCTextIndex</tt> wird mit dem Format durchsucht, das ich in Kapitel 3 beschrieben habe. Es arbeitet mit ähnlichen Suchbegriffen, wie Sie sie auch bei Google oder anderen Suchmaschinen benutzen können. In der einfachsten Form können Sie wie folgt nach einem beliebigen Begriff suchen (beachten Sie, dass hierbei die Schreibweise irrelevant ist):</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults(
        SearchableText = &quot;space&quot;
)
</pre>
<p>Aber Sie können auch alles Folgende suchen:</p>
<ul class="simple">
<li><strong>Globbing</strong>: Verwenden Sie einen Stern für beliebig viele Buchstaben. So passt z.B. <tt class="docutils literal">Dien*</tt> auf <tt class="docutils literal">Dienstag</tt> und <tt class="docutils literal">Dienstage</tt>. Am Wortanfang können Sie allerdings keinen Stern verwenden.</li>
<li><strong>Einzelne Fragezeichen</strong>: Verwenden Sie ein Fragezeichen für einen Buchstaben. Beispiel: <tt class="docutils literal"><span class="pre">Ro?e</span></tt> passt auf <tt class="docutils literal">Rose</tt>, <tt class="docutils literal">Robe</tt>, <tt class="docutils literal">Rote</tt> usw. Am Wortanfang können Sie jedoch kein Fragezeichen verwenden.</li>
<li><strong>And</strong>: Mit <tt class="docutils literal">and</tt> geben Sie an, dass die beiden Begriffe rechts und links davon vorhanden sein müssen. Beispiel: <tt class="docutils literal">Rom and Dienstag</tt> gibt nur Ergebnisse zurück, in denen beide Wörter im Inhalt vorkommen.</li>
<li><strong>Or</strong>: Mit <tt class="docutils literal">or</tt> geben Sie an, dass mindestens einer der Begriffe vorkommen muss. Beispiel: <tt class="docutils literal">Rom or Dienstag</tt> gibt Ergebnisse zurück, falls eines der Wörter im Inhalt vorkommt.</li>
<li><strong>Not</strong>: Mit <tt class="docutils literal">not</tt> erhalten Sie Ergebnisse, in denen der Begriff nicht vorkommt (es wird ein <tt class="docutils literal">and</tt> als Präfix benötigt). Beispiel: <tt class="docutils literal">Willkommen and not Seite</tt> würde passende Seiten zurückgeben, in denen <tt class="docutils literal">Willkommen</tt>, aber nicht <tt class="docutils literal">Seite</tt> vorkommt.</li>
<li><strong>Phrases</strong>: Sätze können Sie in doppelten Anführungszeichen (&quot;) setzen, um damit mehrere Wörter nacheinander anzugeben. Beispiel: <tt class="docutils literal">&quot;Diese Seite&quot;</tt> passt auf <tt class="docutils literal">Diese Seite führt Sie ins Plone <span class="pre">Content-Management-System</span> ein.</tt>, aber nicht auf <tt class="docutils literal">Diese Startseite <span class="pre">von...</span></tt>.</li>
<li><strong>Not phrase</strong>: Sie können einem Satz ein Minuszeichen (-) als Präfix voranstellen. Beispiel: <tt class="docutils literal">Start <span class="pre">-&quot;Diese</span> Seite&quot;</tt> passt auf alle Seiten, in denen <tt class="docutils literal">Start</tt> vorkommt, aber nicht <tt class="docutils literal">Diese Seite</tt>.</li>
</ul>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Wenn Sie eine Suche ohne Text durchführen, werden keine Ergebnisse zurückgegeben.</p>
</div>
</div>
<div class="section" id="die-ergebnisse-benutzen">
<h5>Die Ergebnisse benutzen</h5>
<p>Nun haben Sie also einige Ergebnisse, aber was fangen Sie damit an? Die meisten Leute sehen sich als Erstes die Ergebnisse an und nehmen dabei an, dass es sich um eine Liste der katalogisierten Objekte handelt. Das ist aber nicht der Fall, sondern es handelt sich um eine Folge von &quot;Kataloghirnen&quot; (engl. catalog brains). Diese Hirne sind tatsächlich &quot;Lazy&quot;-Objekte, die die zuvor definierten Metadatenspalten enthalten. Auf all diese Spalten können Sie so zugreifen, als ob es sich um Attribute handeln würde. Verwenden Sie Folgendes, um z.B. alle IDs der Ergebnisobjekte auszugeben:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults()
for result in results:
    print result.getId
return printed
</pre>
<p>In diesem Beispiel ist <tt class="docutils literal">getId</tt> der Name einer Metadatenspalte, d.h., sie wird den Wert von <tt class="docutils literal">getId</tt> anzeigen, der im Katalog für dieses Objekt enthalten ist. Wenn Sie versuchen, auf einen Wert zuzugreifen, der nicht als Metadatenspalte existiert, erhalten Sie einen <tt class="docutils literal">AttributeError</tt>. Folgendes sind ein paar nützliche Methoden solcher Hirne:</p>
<ul class="simple">
<li><strong>getPath</strong>: Gibt den physischen Pfad dieses Objekts in Zope zurück.</li>
<li><strong>getURL</strong>: Gibt die URL für dieses Objekt zurück, bei der Virtual Hosting angewendet wurde.</li>
<li><strong>getObject</strong>: Gibt das eigentliche Objekt zurück.</li>
<li><strong>getRID</strong>: Eine eindeutige ID für das Objekt im Katalog, die sich jedes Mal ändert, wenn das Objekt nicht mehr katalogisiert ist. Ist nur für interne Aufgaben gedacht.</li>
</ul>
<p>Wenn Sie also für jedes Ergebnis das Objekt haben möchten, können Sie das haben, wie Sie im folgenden Beispiel gleich sehen werden. Allerdings gibt es einen Grund dafür, warum der Katalog das nicht selbst macht: Diese Operation ist aufwendig in puncto Rechenzeit, weil damit ein Weckvorgang des Objekts (und aller Objekte dazwischen) verbunden ist, bei dem es aus der Datenbank geholt wird, und außerdem werden viele Sicherheitsprüfungen vorgenommen. Wenn Sie es einrichten können, dass Ihre Metadaten die passende Information enthalten, werden Sie eine wesentlich schnellere Anwendung haben. Natürlich können Metadaten manchmal nicht alles enthalten, aber eine Überlegung ist es beim Entwurf wert. Um an alle Objekte zu kommen, können Sie Folgendes benutzen:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults()
for result in results:
    object = result.getObject()
    print object
return printed
</pre>
<p>Da Sie über eine Python-Liste dieser Hirne verfügen, ist es nun sehr leicht, die Ergebnisse in der geeigneten Weise zu manipulieren. Um herauszufinden, wie viele Ergebnisse gefunden wurden, können Sie einfach wie folgt die Funktion <em>len</em> auf der Liste aufrufen:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults()
print &quot;Anzahl der Ergebnisse&quot;, len(results)
return printed
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last"><tt class="docutils literal">len</tt> ist eine Python-Funktion, die Ihnen die Länge eines Objekts mitteilt.</p>
</div>
<p>Um nur die ersten zehn Elemente zu bekommen, verwenden Sie einen Python-Teilbereich wie folgt:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults()
return results[:10]
</pre>
<p>Um eine weitere Filterung vorzunehmen, können Sie die gesamte Liste wie folgt manuell filtern:</p>
<pre class="literal-block">
results = context.portal_catalog.searchResults()
for result in results[:10]:
    # Title returns a string so we can use the find method of
    # a string to look for occurence of a word
    if result.Title.find(&quot;Plone&quot;) &gt; -1:
        print result.Title
return printed
</pre>
<p>Um ein zufällig gewähltes Objekt aus dem Katalog zu erhalten, können Sie das Modul random wie folgt benutzen:</p>
<pre class="literal-block">
import random
results = context.portal_catalog.searchResults()
r = random.choice(results)
object = r.getObject()
return object
</pre>
</div>
</div>
<div class="section" id="alles-zusammen-erstellen-eines-suchformulars">
<h4>Alles zusammen: Erstellen eines Suchformulars</h4>
<p>In den vorangegangenen Betrachtungen habe ich Ihnen gezeigt, wie Sie einige Ergebnisse aus dem Katalog bekommen, und ich habe Script (Python)-Objekte benutzt, um das zu demonstrieren. Aber vermutlich fragen Sie sich, wie Sie das aus einem Page Template tun können?</p>
<p>Ich fange am anderen Ende an und nehme zuerst an, dass Sie die Ergebnisse aus einer Kataloganfrage schon haben und in einem Page Template mit <tt class="docutils literal">tal:repeat</tt> darüber iterieren. Auf diese Weise funktionieren eine Menge Portlets, auch die für zuletzt veröffentlichte Elemente und Ereignisse, die beide lediglich den Katalog abfragen und dann die Ergebnisse anzeigen. Diese Portlets betten die Abfrage in einem Page Template ein, entweder, indem sie dieses direkt aufrufen:</p>
<pre class="literal-block">
&lt;div tal:define=&quot;results python: here.portal_catalog.searchResults(Type=&quot;Event&quot;)&quot;&gt;
</pre>
<p>oder indem sie ein separates Script (Python)-Objekt aufrufen, das die Ergebnisse zurückgibt. Im folgenden Beispiel heißt das Skript <tt class="docutils literal">getCatalogResults</tt>:</p>
<pre class="literal-block">
##parameters=
kw = {}
# enter your query into the kw dictionary
return context.portal_catalog(**kw)
</pre>
<p>In einem Page Template würden Sie die Ergebnisse auf folgende Weise erhalten:</p>
<pre class="literal-block">
&lt;div tal:define=&quot;results here/getCatalogResults&quot;&gt;
</pre>
<p>Danach müssen Sie mit der normalen <tt class="docutils literal">tal:repeat</tt>-Syntax über die Ergebnisse iterieren. Auf alle Metadatenspalten können Sie direkt in TAL (Template Attribute Language) mit einem Pfadausdruck auf die Spalte zugreifen. Mit einem gegebenen Hirn könnten Sie mit einem Aufruf von <tt class="docutils literal">result/Title</tt> den Titel aus den Metadaten ermitteln. Listing 11.3 zeigt eine Beispielseite, die über den Inhalt von <tt class="docutils literal">getCatalogResults</tt> iteriert und jedes Element in einer einfachen ungeordneten Liste anzeigt.</p>
<p>Listing 11-3. Iterieren über <tt class="docutils literal">getCatalogResults</tt></p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
      lang=&quot;en-US&quot;
      metal:use-macro=&quot;here/main_template/macros/master&quot;
      i18n:domain=&quot;plone&quot;&gt;
&lt;body&gt;
&lt;div metal:fill-slot=&quot;main&quot;&gt;
&lt;ul tal:define=&quot;results here/getCatalogResults&quot;&gt;
    &lt;li tal:repeat=&quot;result results&quot;&gt;
        &lt;a href=&quot;&quot;
           tal:attributes=&quot;href result/getURL&quot;
           tal:content=&quot;result/Title&quot; /&gt;
        &lt;span tal:replace=&quot;result/Description&quot; /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Ein Merkmal der Methode <tt class="docutils literal">searchResults</tt> ist, dass sie bei einem Aufruf ohne Parameter in der ankommenden Abfrage nach diesen sucht. Wenn Sie also möchten, dass ein Formular Parameter zu Ihren Ergebnissen hinzufügt, müssen Sie nur die vorherige Ergebniszeile wie folgt ändern:</p>
<pre class="literal-block">
&lt;ul tal:define=&quot;

  results python: here.portal_catalog.searchResults(REQUEST=request)
  &quot;&gt;
</pre>
<p>Nun können Sie Ihre Abfrage erneut ausführen und einen beliebigen Index zur URL hinzufügen. Wenn Sie dieses Page Template z.B. <tt class="docutils literal">testResults</tt> nennen und in Ihrem Browser ans Ende der URL <tt class="docutils literal"><span class="pre">?Type=Document</span></tt> hinzufügen, würden nur die Dokumente in Ihrer Site erscheinen. Da Sie fast alle Abfragewerte übergeben können, können Sie ein Suchformular so einrichten, dass diese Information zum Suchformular durchgereicht wird. Genau das machen die Seiten zur Suche und zur erweiterten Suche. Wenn Sie auf eine Plone-Site gehen und in dem Suchkasten nach <tt class="docutils literal">Wein</tt> suchen, werden Sie bemerken, dass Ihre URL nun <tt class="docutils literal"><span class="pre">?SearchableText=Wein</span></tt> enthält.</p>
<p>Listing 11.4 zeigt ein Formular zum Aufrufen Ihrer Page Templates.</p>
<p>Listing 11.4. Ein Formular zum Aufrufen Ihres Templates</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
      lang=&quot;en-US&quot;
      metal:use-macro=&quot;here/main_template/macros/master&quot;
      i18n:domain=&quot;plone&quot;&gt;
&lt;body&gt;
&lt;div metal:fill-slot=&quot;main&quot;&gt;
  &lt;p&gt;Select a content type to search for&lt;/p&gt;
  &lt;form method=&quot;post&quot; action=&quot;testResults&quot;&gt;
    &lt;select name=&quot;Type&quot;&gt;
      &lt;option
       tal:repeat=&quot;value python:here.portal_catalog.uniqueValuesFor('Type')&quot;
       tal:content=&quot;value&quot; /&gt;
        &lt;/select&gt;
        &lt;br /&gt;
        &lt;input type=&quot;submit&quot; class=&quot;context&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Dieses Skript benutzt eine Methode namens <tt class="docutils literal">uniqueValuesFor</tt> im Katalog, die alle eindeutigen Werte zurückgibt, die in einem Index existieren. Damit können Sie eine Aufgabe wie das Ausfüllen eines kleinen Dropdown-Menüs in einem Formular erledigen, was sehr nützlich ist.</p>
<p>An diesem Punkt wird es zu einer Übung in HTML und Page Templates, Ihre Seiten so komplex zu machen, wie Sie es wünschen. Der beste Platz, um all das nachzuschauen, ist natürlich in den echten Templates von Plone, die viele Zeilen mit tollen Beispielen enthalten. Alle Portlets, die Sie in Plone kennen (z.B. der Kalender, die Ereignisse, Dazu passend usw.) sind so aufgebaut, dass Sie Katalogabfragen benutzen, um zu bestimmen, was angezeigt werden soll.</p>
<p>In diesem Kapitel habe ich Ihnen einen Überblick über Methoden gezeigt, um eine Plone-Site zu entwickeln, und ich habe gezeigt, wie Inhaltstypen in Ihrer Site funktionieren. Außerdem habe ich vorgeführt, wie ein Inhaltstyp gebaut und dann über den Katalog referenziert wird. In Plone ist das eine sehr wichtige Entwicklungsstrategie.</p>
<p>Im nächsten Kapitel werde ich zeigen, wie man einen neuen Inhaltstyp mehr oder weniger bei null beginnend erstellt. Und Sie werden sehen, wie Sie diesen neuen Inhaltstyp mit dem Katalogregister im Werkzeug <tt class="docutils literal">portal_types</tt> integrieren können.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch12.rst">
    <title>12. Ein Produkt in Python schreiben </title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch12.rst</link>
    <description>Wenn Sie ein Produkt für Plone schreiben, dürfen Sie fast alles tun, was Sie jemals mit Plone machen wollten. Inhaltstypen oder Werkzeuge in Python zu schreiben ist der beste Weg, höchste Flexibilität zu erreichen. Falls Sie dringend etwas Bestimmtes in Plone benötigen, was noch nicht anderswo vorhanden ist, dann haben Sie die Gelegenheit, dieses Feature hinzuzufügen, indem Sie ein Produkt schreiben. Das könnte z.B. die Speicherung einer firmenspezifischen Art von Inhalt oder irgendeine Manipulation sein, die nur Sie benötigen. Im vorigen Kapitel habe ich gezeigt, wie Sie einen Inhaltstyp anpassen können. Diese Anpassung bringt Sie jedoch nur bis zu einem gewissen Punkt. Sie können z.B. keine neuen Attribute zu Ihrem Inhaltstyp hinzufügen. Daher werden Sie lieber Ihren eigenen Inhaltstyp schreiben wollen.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Ein Produkt in Python schreiben</h2>
<p>Wenn Sie ein Produkt für Plone schreiben, dürfen Sie fast alles tun, was Sie jemals mit Plone machen wollten. Inhaltstypen oder Werkzeuge in Python zu schreiben ist der beste Weg, höchste Flexibilität zu erreichen. Falls Sie dringend etwas Bestimmtes in Plone benötigen, was noch nicht anderswo vorhanden ist, dann haben Sie die Gelegenheit, dieses Feature hinzuzufügen, indem Sie ein Produkt schreiben. Das könnte z.B. die Speicherung einer firmenspezifischen Art von Inhalt oder irgendeine Manipulation sein, die nur Sie benötigen. Im vorigen Kapitel habe ich gezeigt, wie Sie einen Inhaltstyp anpassen können. Diese Anpassung bringt Sie jedoch nur bis zu einem gewissen Punkt. Sie können z.B. keine neuen Attribute zu Ihrem Inhaltstyp hinzufügen. Daher werden Sie lieber Ihren eigenen Inhaltstyp schreiben wollen.</p>
<p>In diesem Kapitel werde ich zwei Beispiele beschreiben: einen Inhaltstyp und ein Werkzeug. Beide Beispiele sind relativ einfach gehalten, bereiten Sie aber auf das nächste Kapitel vor, in dem ich Ihnen zeigen werde, wie Sie <em>Archetypes</em> benutzen, ein Framework für Plone, mit dem Sie Inhaltstypen schnell und einfach mit nur einer minimalen Menge von Quellcode erzeugen können.</p>
<p>Insbesondere erzeuge ich einen eigenen Inhaltstyp in Plone und gehe durch den gesamten Code durch, der diesen Inhaltstyp erzeugt. Es handelt sich um einen recht interessanten Inhaltstyp, denn er benutzt mehrere Bausteine, die er aus einem C-Modul von dritter Seite herauszieht, und baut sie in Ihre Plone-Site ein. Ich zeige Ihnen dabei, wie Sie zuerst den Inhaltstyp erstellen und dann Rechte, eine Suchintegration, neue Elemente für die Benutzerschnittstelle sowie Installationskripten hinzufügen. Und schließlich behandle ich, wie man ein Plone-Werkzeug erstellen kann, d.h. eine Art, neue Werkzeuge zu einer Site hinzuzufügen. Beide Beispiele in diesem Kapitel können Sie online herunterladen, installieren und studieren. Außerdem finden Sie in Anhang B den gesamten Code.</p>
<div class="section" id="einen-eigenen-inhaltstyp-schreiben">
<h3>Einen eigenen Inhaltstyp schreiben</h3>
<p>Auf der Website zum Plone-Buch (<a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a>) wollte ich den Code dieses Buchs online anzeigen. Dazu hätte ich den Code nehmen und ihn einfach in Dokumente setzen können, aber dann würde er ohne Syntax-Hervorhebung angezeigt werden. Außerdem würden die Leerzeichen für die Einrückung in Python verschwinden. Für ein so tolles Produkt wie Plone wollte ich etwas, was gut aussieht. Also brauchte ich einen Inhaltstyp, der die Syntax im Code hübsch hervorhebt und es den Benutzern ermöglicht, ihn online anzuschauen. Abbildung 12.1 zeigt das fertige Beispielprodukt.</p>
<a class="reference external image-reference" href="img/12-01.png/image_view_fullscreen"><img alt="img/12-01.png" class="original" src="img/12-01.png" /></a>
<p>Abbildung 12.1. Ein in Plone geladenes Python-Beispielskript</p>
<p>Aus diesem Design können Sie einige Anforderungen für dieses Produkt ableiten. Insbesondere wird dieses Produkt die folgenden Attribute haben:</p>
<ul class="simple">
<li><strong>ID</strong>: Jedes Stück Code hat eine eindeutige ID. Dieses Attribut ist notwendig.</li>
<li><strong>Title</strong>: Jedes Stück Code sollte einen Titel haben. Dieses Attribut ist notwendig.</li>
<li><strong>Description</strong>: Jedes Stück Code sollte eine Beschreibung dessen haben, was es tun sollte. Dieses Attribut ist optional.</li>
<li><strong>Source code</strong>: Jedes Stück Code wird ein Quellcode-Attribut haben, das die Quelle zu diesem Inhaltstyp enthält. Das wird optional sein, aber es ist vernünftig, es notwendig zu machen.</li>
<li><strong>Language</strong>: Dies ist die Sprache für den Quellcode, z.B. Perl, Python, HTML usw.</li>
</ul>
<p>Der Inhaltstyp sollte natürlich mit Plone interagieren, damit Sie dessen Möglichkeiten nutzen können. Sie müssen sicherstellen, dass im Produkt gesucht werden kann, dass es mit den Sicherheitsmechanismen und dem Workflow zusammenarbeitet und korrekt persistent gespeichert wird. Außerdem wäre es nett, wenn die Benutzer Skripten direkt von ihrer Festplatte hochladen könnten, anstatt Code in irgendwelche Textbereiche einfügen zu müssen.</p>
<p>Beim Untersuchen dieses Codes musste ich einen einfachen Weg finden, Code in HTML umzuwandeln. Bei einer Sprache mit einer einfachen Syntax wie Python ist das ziemlich einfach (Python kann sogar seinen eigenen Code &quot;lexen&quot;), aber am liebsten hätte man das natürlich für mehrere Sprachen, z.B. HTML (Page Templates), JavaScript, Cascading Style Sheets (CSS) usw. Das <em>SilverCity</em>-Modul macht bereits genau das. Es ist auf SourceForge verfügbar (<a class="reference external" href="http://silvercity.sf.net/">http://silvercity.sf.net/</a>). Es verwendet C-Bibliotheken aus dem Scintilla-Texteditor, um den Code zu lexen. Ohne besonders auf die Implementierung einzugehen: Der Vorteil besteht darin, dass es zu fast einem Dutzend Programmiersprachen fröhlich HTML mit Syntax-Hervorhebung ausspuckt.</p>
<p>Bei einem Blick auf die Anforderungen werden Sie sehen, dass diese ziemlich leicht nachvollziehbar sind. Tatsächlich werden die ID, der Titel und die Beschreibung alle in der Dublin Core-Implementierung von Plone definiert. Sie brauchen sich also nur um den Quellcode und die Sprache zu kümmern. Plone verlangt eine ID und einen Titel, und eine Beschreibung ist wirklich sehr hilfreich.</p>
<div class="section" id="mit-dem-inhaltstyp-anfangen">
<h4>Mit dem Inhaltstyp anfangen</h4>
<p>Da Sie nun eine Vorstellung vom Inhaltstyp haben, den Sie in diesem Kapitel erstellen werden, können Sie anfangen, ihn zu erstellen, indem Sie Python-Code im Dateisystem schreiben. Dieser Inhaltstyp ist also auch ein Produkt, d.h., Sie erstellen ein neues Verzeichnis in Ihrem Produktverzeichnis. Der Name des zu erzeugenden Verzeichnisses ist der Name des Produkts, das Zope importieren wird. Treffen Sie also eine kluge Wahl für Ihren Namen. Ich habe mir spaßeshalber überlegt, das Produkt <em>SourceCode</em> oder <em>PloneSourceCode</em> zu nennen, fand diese Namen aber zu verwirrend, (man könnte denken, dass das Produkt der eigentliche Quellcode von Plone wäre). Aber <em>PloneSilverCity</em> schien ein netter Produktname zu sein, der auf seinen Ursprung hinweist und hinreichend obskur ist, dass ihn keiner mit etwas anderem verwechseln könnte.</p>
<p>Nachdem das Verzeichnis erstellt ist, füge ich normalerweise ein paar Dateien und Verzeichnisse hinzu, die ich benötige. Jedes Paket benötigt eine Datei namens <tt class="docutils literal">__init__.py</tt> darin. Dieser Dateiname wird von Python so verlangt und bedeutet, dass dieses Verzeichnis ein Python-Paket ist, d.h., dass es importiert werden kann. Wenn das Paket importiert wird, führt Zope diese Datei aus. In dieser Datei fügen Sie den Code zur Registrierung des Produkts in Zope hinzu.</p>
<p>Da Sie benutzerfreundlich sein möchten, können Sie auch ein paar Textdateien wie <tt class="docutils literal">readme.txt</tt>, <tt class="docutils literal">install.txt</tt> usw. hinzufügen. Eine andere nützliche Textdatei ist <tt class="docutils literal">refresh.txt</tt>. Damit können Sie sich in das Refresh-Modul von Zope einklinken und das Produkt dynamisch neu laden, während Sie es schreiben. Das ist gerade bei Ihren ersten Schritten unglaublich hilfreich, wenn Sie eine Klasse schreiben, und ich werde ihnen später zeigen, wie Sie das in Zope konfigurieren.</p>
<p>Im Moment haben Sie ein Verzeichnis namens <tt class="docutils literal">PloneSilverCity</tt> im Produktverzeichnis, das folgende leere Dateien enthält: <tt class="docutils literal">readme.txt</tt>, <tt class="docutils literal">refresh.txt</tt>, <tt class="docutils literal">install.txt</tt> und <tt class="docutils literal">__init__.py</tt>. Das stellt nun ein gültiges Python-Paket dar, das absolut nichts tut (aber nicht mehr lange).</p>
<div class="sidebar">
<p class="first sidebar-title">Mit ZClasses entwickeln</p>
<!-- You're creating the content type using Python, but you've probably heard about ZClasses in other documentation or on the Internet. ZClasses are an existing framework in Zope 2 for developing classes through the Web. Many people have developed and distributed ZClasses successfully, and there can be a role for them for rapid development. However, I really don't recommend them. It's hard to develop them using existing tools, place them in source code, distribute them, and so on. Almost everyone I've talked to about ZClasses agrees that it's worth the effort to learn how to develop with Python, and I've seen more than one presentation that has ZClasses in the list of mistakes people have made. -->
<p>Den Inhaltstyp erzeugen Sie mit Python, aber wahrscheinlich haben Sie schon von ZClasses gehört, sei es in andere Dokumentation oder im Internet. ZClasses sind ein Framework für Zope 2 und dienen zur Entwicklung von Klassen über das Web. Viele Leute haben ZClasses schon erfolgreich entwickelt und vertrieben, und für eine schnelle Entwicklung mögen sie eine Rolle spielen. Allerdings möchte ich sie wirklich nicht empfehlen. Es ist schwer, sie mit vorhandenen Werkzeugen zu entwickeln, sie in Quellcode zu setzen, zu vertreiben usw. Fast jeder, mit dem ich über ZClasses gesprochen habe, stimmt mir zu, dass sich der Aufwand lohnt, mit Python zu entwickeln, und ich habe schon mehrere Präsentationen gesehen, in denen ZClasses zu den Fehlerursachen gezählt wurden.</p>
<!-- If you do see documentation or other information relating to ZClasses, then I really recommend resisting the temptation to use it. For this reason, there's no mention of developing using ZClasses. If you're looking for a quick way to develop, then take a look at Archetypes, which is a slightly different approach. -->
<p class="last">Wenn Sie Dokumentation oder andere Informationen bzgl. ZClasses sehen, so möchte ich Ihnen wirklich empfehlen, der Versuchung zu widerstehen, sie zu benutzen. Aus diesem Grund wird hier die Entwicklung mit ZClasses nicht weiter behandelt. Wenn Sie eine Methode suchen, schnell zu entwickeln, sollten Sie sich Archetypes anschauen, die einen leicht anderen Ansatz verfolgen.</p>
</div>
</div>
<div class="section" id="integration-von-silvercity">
<h4>Integration von SilverCity</h4>
<p>Bevor Sie sich zu tief in den Zope-Code verstricken, könnte es sinnvoll sein herauszufinden, wie man SilverCity benutzt. Bei jeder Softwareentwicklung ist ein schichtweises Vorgehen, das das Testen einzelner Schichten erlaubt, von größter Bedeutung. Aus diesem Grund sollten Sie damit anfangen, dass Sie sicherstellen, SilverCity direkt aus einem Python-Modul heraus benutzen zu können. Wenn das funktioniert, müssen Sie nur noch die Zope-Schicht hinzufügen.</p>
<p>Schauen Sie sich SilverCity also einmal an. Zuerst müssen Sie es installieren. Glücklicherweise folgt dieses Modul den in Kapitel 10 skizzierten Installationsanweisungen für Python-Module. Für eine Installation unter Windows laden Sie die Datei <tt class="docutils literal"><span class="pre">SilverCity-0.9.5.win32-py2.3.exe</span></tt> von <a class="reference external" href="http://silvercity.sf.net">http://silvercity.sf.net</a> herunter und starten das grafische Installationsprogramm. Für Linux laden Sie die Datei <tt class="docutils literal"><span class="pre">SilverCity-0.9.5.tar.gz</span></tt> von der gleichen Website herunter und speichern sie auf der Platte. Dann packen Sie sie aus und starten das Programm <tt class="docutils literal">setup.py</tt>. Beispiel:</p>
<pre class="literal-block">
$ tar -zxf SilverCity-0.9.2.tgz
$ cd SilverCity-0.9.2
$ python setup.py install
...
</pre>
<p>Danach können Sie unter dem Python-Prompt in Windows oder Linux schnell sehen, ob es funktioniert:</p>
<pre class="literal-block">
$ python
Python 2.3.2 (#1, Oct  6 2003, 10:07:16)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; import SilverCity
&gt;&gt;&gt;
</pre>
<p>Das bedeutet, dass SilverCity erfolgreich installiert worden ist. Falls Sie kein ähnliches Ergebnis erhalten und SilverCity nicht importieren können, unterbrechen Sie an dieser Stelle und lösen zuerst das Problem, denn sonst funktioniert überhaupt nichts.</p>
<p>Nun müssen Sie die API (Application Programming Interface) dieses Moduls herausfinden. Weil ich faul bin, habe ich mir ein Beispielskript in <tt class="docutils literal">PySilverCity/Scripts</tt> namens <tt class="docutils literal">source2html.py</tt> angeschaut. Dieses Skript tut genau, was Sie wollen: Es spuckt HTML zu einem gegebenen Stück Code aus. Eine wirklich freche Art, es in Aktion zu sehen, besteht darin, dieses Skript wie folgt auf sich selbst loszulassen:</p>
<pre class="literal-block">
$python source2html.py source2html.py --generator=python

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
    &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
  &lt;title&gt;source2html.py&lt;/title&gt;
  &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
  &lt;link
    rel=&quot;stylesheet&quot;
    href=&quot;default.css&quot; /&gt;
&lt;/head&gt;
...
</pre>
<p>Das heißt, Sie müssen sich nur diese API anschauen und sie leicht abändern. Fügen Sie ein Modul namens <tt class="docutils literal">source.py</tt> im Verzeichnis <tt class="docutils literal">PloneSilverCity</tt> hinzu. Dort schreiben Sie den Code, der die Schnittstelle zur Bibliothek enthält. Dieses neue Modul enthält momentan keinen Zope- oder Plone-spezifischen Code. Es hat drei Hauptbestandteile: Es nennt Ihnen alle möglichen Sprachen, die Sie benutzen können, es nimmt einen Text an, und es gibt den korrekten Parser zurück, und schließlich führt es die eigentliche Umwandlung aus.</p>
<p>Fügen Sie zuerst die folgende Funktion <tt class="docutils literal">create_generator</tt> hinzu, mit der Sie den korrekten Parser erhalten:</p>
<pre class="literal-block">
from SilverCity import LanguageInfo
from StringIO import StringIO

def create_generator(source_file_name=None, generator_name=None):
    &quot;&quot;&quot; Make a generator from the given information
    about the object, such as its source and type &quot;&quot;&quot;
    if generator_name:
        return LanguageInfo.find_generator_by_name(generator_name)()
    else:
        if source_file_name:
            h = LanguageInfo.guess_language_for_file(source_file_name)
            return h.get_default_html_generator()()
        else:
            raise ValueError, &quot;Unknown file type, cannot create lexer&quot;
</pre>
<p>Zweitens, wenn Sie in Plone sind, müssen Sie genau herausfinden, welche Sprachen verfügbar sind, damit Sie diese den Benutzern anzeigen können. Schreiben Sie die folgende Funktion namens <tt class="docutils literal">list_generators</tt>, die diese Liste zurückgibt:</p>
<pre class="literal-block">
def list_generators():
    &quot;&quot;&quot; This returns a list of generators, a generator
    is a valid language, so these are things like perl,
    python, xml etc...&quot;&quot;&quot;
    lexers = LanguageInfo.get_generator_names()
    return lexers
</pre>
<p>Die Funktion <tt class="docutils literal">generate_html</tt> nimmt schließlich eine Quelldatei als String an, einen optionalen Generator und einen optionalen Dateinamen. SilverCity benötigt eine Datei, z.B. <tt class="docutils literal">buffer</tt>, um den Inhalt irgendwohin zu schreiben, d.h., Sie können das Python-Modul <tt class="docutils literal">StringIO</tt> zu diesem Zweck verwenden. Die Funktion  <tt class="docutils literal">generate_html</tt> lautet wie folgt:</p>
<pre class="literal-block">
def generate_html(source_file, generator=None, source_file_name=None):
    &quot;&quot;&quot; From the source make a generator
    and then make the html &quot;&quot;&quot;

    # SilverCity requires a file like object
    target_file = StringIO()
    generator = create_generator(source_file_name, generator)
    generator.generate_html(target_file, source_file)

    # return the html back
    return target_file.getvalue(), generator.name
</pre>
<p>Sie werden bemerken, dass sie die Funktion <tt class="docutils literal">create_generator</tt> aufruft, die Sie zuvor geschrieben haben, um den richtigen Generator für diese Sprache zu finden. Das ist der ganze Code, den Sie benötigen, um zu einer Datei das entsprechende HTML zu generieren. Ich bin nicht auf irgendwelche Besonderheiten des eigentlichen Lexens des Quellcodes oder der Erzeugung des HTML-Codes eingegangen. Die SilverCity-Bibliothek erledigt das alles für Sie. Um den vorigen Punkt zu wiederholen: In diesem Modul haben Sie keine Referenz auf Zope oder Plone, d.h., dieses Modul ist völlig selbstständig. Die eigentlichen Details dieses Moduls müssen Sie nicht kennen, solange Sie wissen, dass Sie eine Bibliothek von dritter Seite importieren.</p>
<p>In Pyhon-Skripten gibt es die Tradition, mindestens einen Test in den Code einzubauen. Sie könnten auch eine vollständige Unittest-Suite erstellen, aber das geht über das aktuelle Thema hinaus. Stattdessen werden Sie ein wenig Code hinzufügen, um zwei Dinge zu testen, nämlich zum einen, dass es funktioniert, und zum anderen, dass die Sprachen verfügbar sind:</p>
<pre class="literal-block">
if __name__ == &quot;__main__&quot;:
    import sys
    myself = sys.argv[0]
    file = open(myself, 'r').read()
    print generate_html(file, generator=&quot;python&quot;)
    print list_generators()
</pre>
<p>Wenn Sie dieses Skript ausführen, öffnet es sich selbst und übergibt seinen Code an den Teil, der die HTML-Syntax hervorhebt. Dann wird eine Menge HTML-Code ausgespuckt. Das könnten Sie einfach ins Zope-spezifische Modul setzen, das Sie gleich schreiben werden. Wenn sich allerdings alles in getrennten Skripten befindet, wird es später einfacher zu testen und zu ändern.</p>
</div>
<div class="section" id="die-klasse-schreiben">
<h4>Die Klasse schreiben</h4>
<p>Ein Inhaltstyp in Plone ist lediglich ein Objekt, das einige bestimmte Attribute und einige bestimmte Basisklassen hat. Sie müssen sich nicht einmal um das Lesen und Schreiben in der Datenbank kümmern, das machen alles die <tt class="docutils literal">Persistencebase</tt>-Klassen. Erstellen Sie nun ein Modul namens <tt class="docutils literal">PloneSilverCity.py</tt> im Paket.</p>
<p>Importieren Sie zuerst das Modul <tt class="docutils literal">source.py</tt>, das Sie vor wenigen Augenblicken geschrieben haben. Das geht mit einer einfachen Zeile, da sich das Modul im gleichen Paket befindet. Die Zeile, die die Funktion importiert, lautet wie folgt:</p>
<pre class="literal-block">
from source import generate_html, list_generators
</pre>
<p>Als Zweites benötigen Sie eine Klasse <tt class="docutils literal">PloneSilverCity</tt>, in der Sie die benötigte Funktionalität kapseln können. Bei dieser Klasse müssen Sie auf die folgenden vier Attribute aufpassen:</p>
<ul class="simple">
<li><strong>id</strong>: Speichert die eindeutige ID dieser Instanz der Klasse <tt class="docutils literal">PloneSilverCity</tt>.</li>
<li><strong>_raw</strong>: Speichert den rohen Quellcode in der Klasse.</li>
<li><strong>_raw_as_html</strong>: Speichert den Quellcode, nachdem er in HTML gelext wurde.</li>
<li><strong>_raw_language</strong>: Speichert die Sprache dieses Quellcodes.</li>
</ul>
<p>Für jedes dieser Attribute werden Sie einen <em>Accessor</em> schreiben, d.h. eine Methode, die den Wert dieses Attributs zurückgibt, damit Sie nicht das Attribut, sondern die Zugriffsmethode aufrufen. Ein Beispiel für eine Accessor-Methode ist <tt class="docutils literal">getLanguage</tt>, die den Wert des language-Attributs zurückgibt. Einen Accessor zu schreiben ist normalerweise eine gute Idee, besonders deswegen, weil Sie später Sicherheitsmechanismen auf diese Accessor-Methoden anwenden werden. In Zope stehen alle Methoden oder Attribute, die mit einem Unterstrich beginnen, nicht für webbasierte Methoden wie Page Templates oder Script (Python)-Objekte zur Verfügung. Ein gute Vorgehensweise besteht darin, am Anfang für all Ihre Attribute einen Unterstrich zu verwenden und dann die Sicherheitsmechanismen auf die Zugriffsmethoden anzuwenden.</p>
<p>Listing 12.1 zeigt die Basisklasse.</p>
<p>Listing 12.1. Die Basisklasse in Python</p>
<pre class="literal-block">
class PloneSilverCity:
    def __init__(self, id):
        self.id = id
        self._raw = &quot;&quot;
        self._raw_as_html = &quot;&quot;
        self._raw_language = None

    def getLanguage(self):
        &quot;&quot;&quot; Returns the language this code has been lexed with &quot;&quot;&quot;
        return self._raw_language

    def getRawCode(self):
        &quot;&quot;&quot; Returns the raw code &quot;&quot;&quot;
        return self._raw

    def getHTMLCode(self):
        &quot;&quot;&quot; Returns the html code &quot;&quot;&quot;
        return self._raw_as_html

    def getLanguages(self):
        &quot;&quot;&quot; Returns the list of languages available &quot;&quot;&quot;
        langs = []
        for name, description in list_generators():
            langs.append( {'name':lang, 'value':language} )
        langs.sort()
        return langs
</pre>
<p>Eine weitere Methode müssen Sie noch hinzufügen, nämlich eine <tt class="docutils literal">edit</tt>-Methode, mit der Sie eine Datei oder einen String hochladen können. Diese Methode liest die Datei und prüft, ob irgendetwas in der Datei steht. Wenn ja, wird sie gelesen und ein Dateiname wird bestimmt. Dann werden der Code, die Sprache und der Dateiname an die Erzeugungsfunktion übergeben. All das speichern Sie in den zuvor genannten Attributen, wie Sie in Listing 12.2 sehen können.</p>
<p>Listing 12.2. Die Methode zur Behandlung von Bearbeitungsschritten</p>
<pre class="literal-block">
def edit(self, language, raw_code, file=&quot;&quot;):
    &quot;&quot;&quot; The edit function, that sets
    all our parameters, and turns the code
    into pretty HTML &quot;&quot;&quot;
    filename = &quot;&quot;
    if file:
        file_code = file.read()

        # if there is a file and it's not blank...
        if file_code:
            raw_code = file_code
            if hasattr(file, &quot;name&quot;):
                filename = file.name
            else:
                filename = file.filename
            # set the language to None so set by SilverCity
            language = None

    self._raw = raw_code

    # our function, generate_html does the hard work here
    html, language = generate_html(raw_code, language, filename)
    self._raw_as_html = html
    self._raw_language = language
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Python-Entwickler, die sich gut auskennen, sehen eventuell ein Problem darin, <tt class="docutils literal">file.name</tt> und <tt class="docutils literal">file.filename</tt> zu verwenden. Zope-Dateiobjekte verfügen über ein Attribut namens <tt class="docutils literal">filename</tt>, das den Dateinamen repräsentiert, während in Python das Attribut <tt class="docutils literal">name</tt> heißt. Dieser Code funktioniert also in normalem Python oder in Zope.</p>
</div>
<p>Nun haben Sie also eine Python-Klasse, die das Objekt kapselt. An dieser Stelle sollten Sie die Klasse unter einem Python-Prompt sehr leicht ausführen können, um zu testen, ob sie das macht, was Sie wünschen. Beispiel:</p>
<pre class="literal-block">
$ python
Python 2.3.2 (#1, Oct  6 2003, 10:07:16)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; from PloneSilverCity import PloneSilverCity
&gt;&gt;&gt; p = PloneSilverCity(&quot;test.py&quot;)
&gt;&gt;&gt; p.edit(&quot;python&quot;, &quot;print 'hello world'&quot;)
&gt;&gt;&gt; p.getRawCode()
&quot;print 'hello world'&quot;
&gt;&gt;&gt; p.getHTMLCode()
'&lt;span class=&quot;p_word&quot;&gt;print&lt;/span&gt;
&lt;span class=&quot;p_default&quot;&gt;nbsp;&lt;/span&gt;
&lt;span class=&quot;p_character&quot;&gt;\'hellonbsp;world\'&lt;/span&gt;'
&gt;&gt;&gt; p.getLanguage()
'python'
</pre>
</div>
<div class="section" id="aus-einem-paket-ein-produkt-machen">
<h4>Aus einem Paket ein Produkt machen</h4>
<p>Nun haben Sie ein einfaches Paket, aber das ist noch kein Plone-Produkt. Sie müssen es mit Plone initialisieren. Das heißt,Sie müssen zusätzliche Informationen zum Modul <tt class="docutils literal">PloneSilverCity.py</tt> hinzufügen. Insbesondere müssen Sie eine Factory-Funktion hinzufügen. Die Verwendung einer Factory ist ein wohlbekanntes Muster im objektorientierten Design. Sie definiert, wie ein Objekt erzeugt wird. Fügen Sie im Modul <tt class="docutils literal">PloneSilverCity.py</tt> den folgenden Konstruktor hinzu:</p>
<pre class="literal-block">
def addPloneSilverCity(self, id, REQUEST=None):
    &quot;&quot;&quot; This is our factory function and creates
    an empty PloneSilverCity object &quot;&quot;&quot;
    obj = PloneSilverCity(id)
    self._setObject(id, obj)
</pre>
<p>Die Funktion <tt class="docutils literal">addPloneSilverCity</tt> gehört nicht zur Klasse <tt class="docutils literal">PloneSilverCity</tt>. Als Konstruktor für die Klasse wird sie in das Modul außerhalb der Klasse gesetzt. Diese Funktion ist die erste Plone-spezifische Funktion. Der Methode werden drei Parameter übergeben: das Objekt <tt class="docutils literal">self</tt>, der ID-String für das Objekt und <tt class="docutils literal">REQUEST</tt>. Das Objekt <tt class="docutils literal">self</tt> ist eigentlich der <tt class="docutils literal">context</tt>, den Sie zuvor schon gesehen haben, wenn auch unter einem anderen Namen. Da die Objekte immer innerhalb eines Ordners erzeugt werden, zeigt <tt class="docutils literal">self</tt> auf den Ordner, in dem dieses Objekt erzeugt wird. Diese Funktion erzeugt eine Instanz von <tt class="docutils literal">PloneSilverCity</tt> namens <tt class="docutils literal">obj</tt> und übergibt sie an die Methode <tt class="docutils literal">_setObject</tt> des Ordners. Die Methode <tt class="docutils literal">_setObject</tt> ist in Zope etwas Besonderes: Sie instanziiert das Objekt in der Datenbank und registriert das Objekt im umgebenden Ordner.</p>
<p>Als Nächstes fügen Sie die Factory-Typinformation hinzu, die in Kapitel 11 behandelt wurde (das ist Ihre erste Chance, sie selbst zu erzeugen). Die Factory-Typinformation enthält in einem Dictionary alle Informationen über den Inhaltstyp. Diese Angaben werden in <tt class="docutils literal">portal_types</tt> geladen, wenn das Produkt in Ihrer Plone-Instanz installiert wird. Sie stellen jene Information dar, die Sie vorher gesehen haben, als Sie die Factory-Typinformation über das Web geändert haben.</p>
<p>Bevor ich die Factory-Information erstelle, erstelle ich normalerweise eine Konfigurationsdatei, die alle wiederholten Variablen zu dem Produkt enthält. Diese Datei heißt <tt class="docutils literal">config.py</tt>, und darin schreiben Sie wie folgt den Namen des Produkts, den Namen seiner Ebenen in den Skins und den Namen, den der Benutzer dafür sieht:</p>
<pre class="literal-block">
product_name = &quot;PloneSilverCity&quot;
plone_product_name = &quot;Source Code&quot;
layer_name = &quot;silvercity&quot;
layer_location = &quot;PloneSilverCity/skins&quot;
</pre>
<p>Dann können Sie die Factory-Typ-Information erstellen und diese Strings verwenden. Die ID z.B. wird <tt class="docutils literal">Source Code</tt> sein, da das den Benutzern in Plone angezeigt wird. Der Aktionsabschnitt der Typinformation ist ein Tupel von Dictionaries aller Aktionen, die auf diesem Objekt vorkommen können. Wenn diese Factory in Plone geladen wird, wird der <em>Actions</em>-Reiter im Werkzeug <tt class="docutils literal">portal_types</tt> mit diesem Inhalt gefüllt. All diese Aktionen haben eine entsprechende Methode, ein Template oder Skript, das aufgerufen wird. Die meisten davon haben eine direkte Entsprechung zu Page Templates, was ich später in diesem Abschnitt noch erörtern werde.</p>
<p>Wie Sie nun wissen, ist eine Aktion etwas, das Benutzer mit einem Element in der Plone-Datenbank machen können. Mit Blick auf diese Beispielanwendung können die Benutzer zwei offensichtliche Dinge mit dem Quellcode anstellen. Sie können ihn anzeigen und den hübsch hervorgehobenen Code sehen, und sie können das Element bearbeiten und einen Quellcode hochladen. Tatsächlich verlangt Plone, dass es eine Aktion namens <em>view</em> und eine Aktion namens <em>edit</em> gibt, d.h., diese beiden passen hier gut ins Konzept. Sie werden auch eine dritte Aktion haben wollen: Es ist nett, den Quellcode in seiner ursprünglichen Form herunterladen zu können. In Sprachen wie Python, in denen die Formatierung ein wesentliches Element ist, ist das wirklich nützlich. Diese Aktion zeigt direkt auf <tt class="docutils literal">getRawCode</tt>, was die Methode für den Zugriff auf den rohen Code ist.</p>
<p>Jede Aktion hat ein zugehöriges Recht, wie in Listing 12.3 gezeigt wird (wo es genau herkommt, werde ich im weiteren Verlauf dieses Abschnitts zeigen).</p>
<p>Listing 12.3. Die Factory-Typinformationen und Aktionen</p>
<pre class="literal-block">
factory_type_information = {
     'id': plone_product_name,
     'meta_type': product_name,
     'description': ('Provides syntax highlighted HTML of source code.'),
     'product': product_name,
     'factory': 'addPloneSilverCity',
     'content_icon': 'silvercity.gif',
     'immediate_view': 'view',
     'actions': (
                 {'id': 'view',
                  'name': 'View',
                  'action': 'silvercity_view_form',
                  'permissions': (view_permission,)},
                 {'id': 'edit',
                  'name': 'Edit',
                  'action': 'silvercity_edit_form',
                  'permissions': (edit_permission,)},
                 {'id': 'source',
                  'name': 'Source',
                  'action': 'getRawCode',
                  'permissions': (view_permission,)},
                 ),
     }
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">An dieser Stelle kann das Produkt vom Python-Prompt aus nicht importiert werden, weil der Code noch unvollständig ist.</p>
</div>
<div class="section" id="die-rechte-einstellen">
<h5>Die Rechte einstellen</h5>
<p>Ein fundamentales Konzept bei Websites ist die Annahme, das man nichts und niemandem trauen darf. Bevor ein Zugriff auf eine Eigenschaft erfolgt oder irgendeine Methode aufgerufen wird, müssen Sie zuerst überprüfen, ob die Partei, die eine Aktion ausführen möchte, das auch tun darf. Bei den meisten Systemen gibt es drei Rechte: das Recht, ein Element hinzuzufügen, das Recht, ein Element zu löschen, und das Recht, ein Element zu bearbeiten. Plone kennt ein weiteres Recht: das Recht, ein Element über das Web (oder ein anderes Protokoll) anzuzeigen. In Plone hat der umgebende Ordner das Recht, etwas zu löschen. Wenn Sie etwas in diesem Ordner löschen können, können Sie auch den hier von Ihnen hinzugefügten Inhaltstyp löschen.</p>
<p>Das heißt, Sie müssen sich noch um drei Rechte kümmern. Es ist normal, jene zu benutzen, die im CMFCore-Paket enthalten sind: <em>Add portal content</em>, <em>Modify portal content</em> und <em>View</em>. Wieder zurück in der Konfigurationsdatei können Sie die benötigten Rechte wie folgt ändern:</p>
<pre class="literal-block">
from Products.CMFCore import CMFCorePermissions

add_permission = CMFCorePermissions.AddPortalContent
edit_permission = CMFCorePermissions.ModifyPortalContent
view_permission = CMFCorePermissions.View
</pre>
<p>Das heißt, die Variable <tt class="docutils literal">add_permission</tt> steht für das aus CMFCorePermissions importierte Recht. Rechte haben nichts Magisches an sich, jedes einzelne Recht ist lediglich ein String. Die Verwendung des eingebauten Rechts ist bequem und für Ihre Benutzer leicht zu verstehen. Plone ist bereits so konfiguriert, dass es den richtigen Personen erlaubt, Inhalte mit Hilfe des Rechts <em>Add portal content</em> zu erstellen. Außerdem ist der Standard-Workflow so definiert, dass er diese Rechte benutzt und ändert. Diese Rechte waren es, die Sie zur Factory-Typinformation hinzugefügt haben.</p>
<p>Sollten Sie eigene Rechte erstellen wollen, könnten Sie das recht einfach tun. Angenommen, Sie möchten das Recht <em>Add</em> in das eigenständige Recht <em>Add Source Code</em> ändern. Dann würden Sie die Datei wie folgt ändern:</p>
<pre class="literal-block">
add_permission = &quot;Add Source Code&quot;
</pre>
<p>Nachdem das Produkt importiert worden wäre, hätten Sie im <em>Security</em>-Reiter ein neues Recht namens <em>Add Source Code</em>. Warum würden Sie das tun? Nun, es ist bequem, ein Recht zu benutzen, das jeder andere auch benutzt. Allerdings könnte es sein, dass Sie eine feinere Granularität oder andere Sicherheitseinstellungen benötigen. Aus diesem Grund können Sie Ihre eigenen Sicherheitseinstellungen einfach selbst anfertigen.</p>
</div>
<div class="section" id="die-initialisierung-abschlieszen">
<h5>Die Initialisierung abschließen</h5>
<p>Nun müssen Sie die Initialisierung des Produkts einrichten. Das machen Sie im Modul <tt class="docutils literal">__init__.py</tt>, damit dann, wenn Zope diese Datei beim Hochfahren liest, es die Initialisierung des Produkts abschließt, wie in Listing 12.4 gezeigt wird.</p>
<p>Listing 12.4. Das Modul <tt class="docutils literal">__init__.py</tt></p>
<pre class="literal-block">
import PloneSilverCity

from Products.CMFCore import utils
from Products.CMFCore.DirectoryView import registerDirectory

from config import product_name, add_permission

contentConstructors = (PloneSilverCity.addPloneSilverCity,)
contentClasses = (PloneSilverCity.PloneSilverCity,)
contentFTI = (PloneSilverCity.factory_type_information,)

registerDirectory('skins', globals())

def initialize(context):
    product = utils.ContentInit(product_name,
        content_types = contentClasses,
        permission = add_permission,
        extra_constructors = contentConstructors,
        fti = contentFTI)
    product.initialize(context)
</pre>
<p>Was passiert in diesem Code? Nun, eigentlich nicht sehr viel, der Code ist nur ein wenig ausführlich. Zuerst legen Sie Referenzen auf Klassen und Konstruktoren an, die in <tt class="docutils literal">contentClasses</tt> und <tt class="docutils literal">contentConstructors</tt> verwendet werden. Diese legen die Factory-Funktion zur Erstellung der Objekte und die eigentliche Klasse fest. Diese werden dann an die Funktion <tt class="docutils literal">ContentInit</tt> übergeben, und zwar in <tt class="docutils literal">initialize</tt>, einer speziellen Funktion, die während der Produktinitialisierung aufgerufen wird. <tt class="docutils literal">ContentInit</tt> verrichtet die ganze Arbeit, um das Produkt in Plone einzurichten. Die Parameter dieser Funktion sind die folgenden:</p>
<ul class="simple">
<li><strong>product_name</strong>: Der Name des Produkts, wie er in der Konfigurationsdatei definiert ist (in diesem Fall <tt class="docutils literal">PloneSilverCity</tt>).</li>
<li><strong>content_types</strong>: Das Tupel der Klassen, die dieses Produkt definieren. Normalerweise ist das nur eine Klasse, es können aber auch mehrere sein.</li>
<li><strong>permission</strong>: Das Recht, das benötigt wird, um eine Instanz dieses Objekts zu erzeugen; in diesem Fall die Variable <tt class="docutils literal">add_permission</tt>, die ich in <tt class="docutils literal">config.py</tt> definiert habe.</li>
<li><strong>fti</strong>: Steht für Factory-Typinformation und ist das Dictionary mit der Factory-Typinformation, die Sie im Modul <tt class="docutils literal">PloneSilverCity.py</tt> für den Inhalt definiert haben.</li>
</ul>
</div>
</div>
<div class="section" id="produkt-module-andern">
<h4>Produkt-Module ändern</h4>
<p>Nun können Sie zum Modul <tt class="docutils literal">PloneSilverCity.py</tt> und zu der Aufgabe zurückkehren, es in ein Plone-Produkt zu verwandeln. Am Anfang des Moduls werden Sie die <tt class="docutils literal">import</tt>-Anweisungen erstellen. Diese holen wie folgt aus unterschiedlichen Orten verschiedene Dinge, die für die Plone-Initialisierung notwendig sind:</p>
<pre class="literal-block">
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
from Products.CMFCore.PortalContent import PortalContent
</pre>
<p>Diese Import-Anweisungen stellen die Grundfunktionalität für das Produkt bereit und kommen bei den meisten Inhaltstypen vor. Die importierten Definitionen lauten wie folgt:</p>
<ul class="simple">
<li><strong>InitializeClass</strong>: Diese Funktion initialisiert die Klasse und wendet alle Sicherheitsdeklarationen an, die diese haben wird. Diese Sicherheitsdeklarationen geben Sie durch die Verwendung der Klasse <tt class="docutils literal">ClassSecurityInfo</tt> an.</li>
<li><strong>ClassSecurityInfo</strong>: Diese Klasse bietet eine Reihe von Sicherheitsmethoden, mit denen Sie den Zugriff auf die Methoden des Inhaltstyps beschränken können.</li>
<li><strong>DefaultDublinCoreImpl</strong>: Diese Klasse enthält eine Implementation von Dublin Core-Metadaten. Der Dublin Core wurde in Kapitel 11 behandelt. Sie verleiht einem Objekt alle Dublin Core-Attribute und -Methoden für den Zugriff darauf, wie Title, Description, Creator usw.</li>
<li><strong>PortalContent</strong>: Dies ist die Basisklasse für alle Inhalte in einer Plone-Site und einige der darin benötigten Schlüsselattribute. Durch die Verwendung dieser Basisklasse erhält das Objekt eine ganze Menge an Funktionalität, z.B. zur persistenten Speicherung des Objekts in der Datenbank, der Katalogisierung des Objekts für die Suche mit dem Objekt <tt class="docutils literal">portal_catalog</tt> und seiner Registrierbarkeit mit dem Werkzeug <tt class="docutils literal">portal_types</tt>.</li>
</ul>
<p>Außerdem müssen Sie die Konfigurationsvariablen und Rechte importieren. Das erfolgt mit den beiden folgenden Zeilen:</p>
<pre class="literal-block">
from config import plone_product_name, product_name
from config import add_permission, edit_permission, view_permission
</pre>
<p>In der Klasse wiederum müssen Sie zwei Basisklassen hinzufügen, um sie vollständig Plone-kompatibel zu machen: <tt class="docutils literal">PortalContent</tt> und <tt class="docutils literal">DefaultDublinCoreImpl</tt>. Weiterhin müssen Sie der Klasse einen <tt class="docutils literal">meta_type</tt> geben. In Zope hat jedes Produkt einen eindeutigen <tt class="docutils literal">meta_type</tt>:</p>
<pre class="literal-block">
class PloneSilverCity(PortalContent, DefaultDublinCoreImpl):
    meta_type = product_name
</pre>
<p>Plone muss wissen, welche Basisklassen der Inhaltstyp implementiert. Andere Teile der Anwendung müssen wissen, welche Klassen implementiert werden. Geben Sie daher wie folgt explizit an, welche Klassen der Inhaltstyp implementiert:</p>
<pre class="literal-block">
__implements__ = (
    PortalContent.__implements__,
    DefaultDublinCoreImpl.__implements__
    )
</pre>
<div class="section" id="sicherheit-zur-klasse-hinzufugen">
<h5>Sicherheit zur Klasse hinzufügen</h5>
<p>Wenn Sie bereits entschieden haben, die Aktionen abzusichern, müssen Sie diese Sicherheitsmechanismen auch auf die Klasse anwenden. In einer Umgebung wie Plone, in der Objekte veröffentlicht werden, kann jeder alle Methoden der Klasse über das Web aufrufen, es sei denn, sie beginnen mit einem Unterstrich. Das ist natürlich nicht so gut, und daher müssen Sie all Ihre Methoden schützen.</p>
<p>Um das in der Klasse selbst zu tun, erstellen Sie eine Instanz der Klasse <tt class="docutils literal">ClassSecurityInfo</tt> mit der folgenden Zeile:</p>
<pre class="literal-block">
security = ClassSecurityInfo()
</pre>
<p>Dieses Sicherheitsobjekt bietet eine Schnittstelle zum Sicherheitsapparat von Zope. Dann können Sie Methoden auf das Objekt anwenden. Meine Lieblingsmethode dafür besteht darin, direkt über der Methode eine Zeile hinzuzufügen, in der die Sicherheit angebracht wird. Auf diese Weise kann man sich leicht merken, wo die Sicherheit angewendet wird, und später werden Sie nicht vergessen, sie zu aktualisieren, falls das nötig sein sollte. Die Methode <tt class="docutils literal">declareProtected</tt>  erwartet das Recht und den Methodennamen, um die <tt class="docutils literal">edit</tt>-Methode zu schützen. Tun Sie also Folgendes, damit nur diejenigen sie aufrufen können, die das Recht zum Bearbeiten haben:</p>
<pre class="literal-block">
security.declareProtected(edit_permission, &quot;edit&quot;)
</pre>
<p>Das wiederholen Sie für jede Methode, wobei Sie das passende Recht und den entsprechenden Methodennamen angeben. Die einzige Methode, die geschützt werden muss, ist <tt class="docutils literal">__init__</tt>, weil sie mit einem Unterstrich beginnt. Um diese ganze Sicherheit anzuwenden, müssen Sie die Klasse initialisieren. Ohne diesen Schritt wird die gesamte anschließend deklarierte Sicherheit <em>nicht</em> angewendet, d.h., Ihr Objekt wird öffentlich zugänglich sein.</p>
<p>Mit anderen Worten, vergessen Sie die folgende Zeile nicht:</p>
<pre class="literal-block">
InitializeClass(PloneSilverCity)
</pre>
<p>Die API für <tt class="docutils literal">ClassSecurityInfo</tt> enthält die folgenden Methoden für die Klasse:</p>
<ul class="simple">
<li><strong>declarePublic</strong>: Erwartet eine Liste von Namen. Alle Namen werden für alle Benutzer als öffentlich zugänglich über eingeschränkten Code wie auch über das Web deklariert.</li>
<li><strong>declarePrivate</strong>: Erwartet eine Liste von Namen. Alle Namen sind privat und sind nicht über eingeschränkten Code zugänglich.</li>
<li><strong>declareProtected</strong>: Erwartet ein Recht und eine beliebige Zahl von Namen. Alle Namen sind nur mit dem angegebenen Recht zugänglich.</li>
<li><strong>declareObjectPublic</strong>: Setzt das gesamte Objekt als öffentlich zugänglich.</li>
<li><strong>declareObjectPrivate</strong>: Setzt das gesamte Objekt als privat und unzugänglich für eingeschränkten Code.</li>
</ul>
<p>Mit diesen Methoden können Sie fast jede gewünschte Sicherheit einstellen. Allerdings fand ich es fast immer ausreichend, den Schutz für alle Methoden mit einem bestimmten Recht explizit zu setzen.</p>
</div>
<div class="section" id="integration-mit-der-suche">
<h5>Integration mit der Suche</h5>
<p>Im vorigen Kapitel habe ich Ihnen gezeigt, wie die Suche funktioniert und welche Indizes existieren. Da die Indizes mit Dublin Core-Objekten arbeiten und Sie Dublin Core als Basisklasse verwendet haben, werden Titel, Beschreibung, Erzeuger, Änderungsdatum usw. Ihres Objekts alle für Sie indiziert, d.h., es ist keine weitere Arbeit dazu nötig. Durch das Ableiten von der Klasse <tt class="docutils literal">PortalContent</tt> wird weiterhin bei jeder Änderung des Objekts der Katalog für Sie aktualisiert. Auch hierbei müssen Sie sich um nichts weiter kümmern.</p>
<p>Ein bestimmter Index jedoch benötigt ein wenig Unterstützung, nämlich <tt class="docutils literal">SearchableText</tt>. Wie ich zuvor demonstriert habe, bietet der Index <tt class="docutils literal">SearchableText</tt> einen Volltext-Index, den Plone verwendet, wenn eine Suche ausgeführt wird. Es wäre nett, wenn der Index auch im Quelltext suchen würde, damit beim Hochladen eines Codes mit einem <tt class="docutils literal">import</tt> darin diese Anweisung von der Suche erfasst würde. Da der Katalog im Objekt nachschaut und versucht, ein Attribut oder eine Methode mit dem entsprechenden Namen des Indexes zu finden, müssen Sie lediglich eine Methode mit diesem Namen erstellen, die den gewünschten Wert zurückgibt.</p>
<p>Am einfachsten macht man das, indem man einen String aus den gewünschten Feldern erstellt, z.B. dem Titel, der Beschreibung und dem rohen Code. Mit dem Recht <em>View</em> lässt sich das schützen, denn jeder, der sich das Objekt anschaut, kann den Inhalt sowieso ohne weiteres sehen. Folgendes ist eine <tt class="docutils literal">SearchableText</tt>-Methode, die diese Aufgabe erfüllt:</p>
<pre class="literal-block">
security.declareProtected(view_permission, &quot;SearchableText&quot;)
def SearchableText(self):
    &quot;&quot;&quot; Used by the catalog for basic full text indexing &quot;&quot;&quot;
    return &quot;%s %s %s&quot; % ( self.Title()
                        , self.Description()
                        , self._raw
                        )
</pre>
</div>
<div class="section" id="der-unterschied-zwischen-einer-python-und-einer-plone-klasse">
<h5>Der Unterschied zwischen einer Python- und einer Plone-Klasse</h5>
<p>Wie Sie sehen, besteht ein beträchtlicher Unterschied zwischen einem normalen Python-Produkt und einem, das in Plone registriert ist. Allerdings liegen die meisten dieser Unterschiede darin, wie das Produkt registriert und die Sicherheit gewährleistet wird. Die eigentliche Klasse ist sehr ähnlich. Listing 12.5 beschreibt alle Unterschiede zwischen der reinen Python- und der Plone-Implementierung.</p>
<p>Listing 12.5. Die Plone-Version der Klasse</p>
<pre class="literal-block">
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
from Products.CMFCore.PortalContent import PortalContent

from config import meta_type, product_name
from config import add_permission, edit_permission, view_permission
from source import generate_html, list_generators

factory_type_information = {
     'id': plone_product_name,
     'meta_type': product_name,
     'description': ('Provides syntax highlighted HTML of source code.'),
     'product': product_name,
     'factory': 'addPloneSilverCity',
     'content_icon': 'silvercity.gif',
     'immediate_view': 'view',
     'actions': (
                 {'id': 'view',
                  'name': 'View',
                  'action': 'silvercity_view_form',
                  'permissions': (view_permission,)},
                 {'id': 'source',
                  'name': 'View source',
                  'action': 'getRawCode',
                  'permissions': (view_permission,)},
                 {'id': 'edit',
                  'name': 'Edit',
                  'action': 'silvercity_edit_form',
                  'permissions': (edit_permission,)},
                 ),

     }

def addPloneSilverCity(self, id, REQUEST=None):
    &quot;&quot;&quot; This is our factory function and creates
    an empty PloneSilverCity object inside our Plone
    site &quot;&quot;&quot;
    obj = PloneSilverCity(id)
    self._setObject(id, obj)

class PloneSilverCity(PortalContent, DefaultDublinCoreImpl):

    meta_type = product_name

    __implements__ = (
        PortalContent.__implements__,
        DefaultDublinCoreImpl.__implements__
        )

    security = ClassSecurityInfo()
    def __init__(self, id):
        DefaultDublinCoreImpl.__init__(self)
        self.id = id
        self._raw = &quot;&quot;
        self._raw_as_html = &quot;&quot;
        self._raw_language = None

    security.declareProtected(edit_permission, &quot;edit&quot;)
    def edit(self, language, raw_code, file=&quot;&quot;):
        &quot;&quot;&quot; The edit function, that sets
        all our parameters, and turns the code
        into pretty HTML &quot;&quot;&quot;
        filename = &quot;&quot;
        if file:
            file_code = file.read()

            # if there is a file and its not blank...
            if file_code:
                raw_code = file_code
                if hasattr(file, &quot;name&quot;):
                    filename = file.name
                else:
                    filename = file.filename
                # set the language to None so set by SilverCity
                language = None

        self._raw = raw_code

        # our function, generate_html does the hard work here
        html, language = generate_html(raw_code, language, filename)
        self._raw_as_html = html
        self._raw_language = language

    security.declareProtected(view_permission, &quot;getLanguage&quot;)
    def getLanguage(self):
        &quot;&quot;&quot; Returns the language that code has been lexed with &quot;&quot;&quot;
        return self._raw_language

    security.declareProtected(view_permission, &quot;getLanguages&quot;)
    def getLanguages(self):
        &quot;&quot;&quot; Returns the list of languages available &quot;&quot;&quot;
        langs = []

        for name, description in list_generators():
            # these names are normally in uppercase
            langs.append( {'name':lang, 'value':language } )

        langs.sort()
        return langs

    security.declareProtected(view_permission, &quot;getRawCode&quot;)
    def getRawCode(self):
        &quot;&quot;&quot; Returns the raw code &quot;&quot;&quot;
        return self._raw

    security.declareProtected(view_permission, &quot;getHTMLCode&quot;)
    def getHTMLCode(self):
        &quot;&quot;&quot; Returns the html code &quot;&quot;&quot;
        return self._raw_as_html

    security.declareProtected(view_permission, &quot;SearchableText&quot;)
    def SearchableText(self):

        &quot;&quot;&quot; Used by the catalog for basic full text indexing &quot;&quot;&quot;

        return &quot;%s %s %s&quot; % ( self.Title()
                            , self.Description()
                            , self._raw
                            )

InitializeClass(PloneSilverCity)
</pre>
</div>
</div>
<div class="section" id="skins-hinzufugen">
<h4>Skins hinzufügen</h4>
<p>Nun, da Sie den Hauptcode haben, bleiben noch zwei Dinge für Sie zu tun: die Skins und eine Installationsmethode erstellen. Die Skins gehören zu den einfacheren Teilen, weil ein Großteil der notwendigen Arbeit bereits vom Plone-Framework erledigt wird. Skins habe ich detailliert in vorangegangenen Kapiteln behandelt, in denen ich erörtert habe, wie man im Dateisystem eine Skin für eine Plone-Site erstellt. Jedes Produkt, das eine eigene Benutzerschnittstelle haben muss (UI, User Interface), macht das mit einem eigenen FSDV (File System Directory View), d.h., hier werdn Sie erneut das Gleiche machen.</p>
<p>Skins kommen in das Verzeichnis <tt class="docutils literal">skins</tt> des Produkts. Dieser Verzeichnisname wird in der Datei <tt class="docutils literal">__init__.py</tt> definiert, wo Sie das Verzeichnis mit der Funktion <tt class="docutils literal">registerDirectory</tt> registrieren. Wenn Sie diesen Namen ändern möchten, sollten Sie ihn auf jeden Fall registrieren. Sie können so viele Verzeichnisse registrieren, wie Sie wollen, aber das ist rekursiv und dabei wird auch alles in und unter diesem registrierten Verzeichnis registriert.</p>
<p>Die einfachste all Ihrer Aufgaben bei diesem Produkt besteht darin, ein Icon für das Objekt hinzuzufügen, das in Plone erscheint. Der Name dieses Icons wird bereits in der Factory-Typinformation mit der Zeile <tt class="docutils literal">'content_icon': 'silvercity.gif'</tt> definiert, d.h., Sie müssen lediglich ein Icon namens <tt class="docutils literal">silvercity.gif</tt> im Verzeichnis <tt class="docutils literal">skins</tt> hinzufügen. Dieses Icon wird immer dann angezeigt, wenn Sie das Objekt in der Benutzerschnittstelle von Plone anzeigen. Wenn SilverCity eine Datei lext, gibt es HTML aus, in dem CSS-Tags benutzt werden, d.h., Sie müssen sicherstellen, dass die entsprechende CSS-Datei auch verfügbar ist. Bei diesem Produkt kopieren Sie einfach das CSS aus dem SilverCity-Produkt und setzen es in das Verzeichnis <tt class="docutils literal">skins</tt>.</p>
<p>Diese zwei Dinge sind nun erledigt. Nun müssen Sie als Nächstes wirklich die Seiten zum Anzeigen und Bearbeiten der Seiten schreiben. Ich habe zuvor die Ähnlichkeit zu einem Dokument beschrieben, wenn Sie also nach Seiten zum Anzeigen und Bearbeiten suchen, dann sind die Seiten zu einem Dokument der beste Platz dafür. Dies sind <tt class="docutils literal">document_view.pt</tt> und <tt class="docutils literal">document_edit_form.cpt</tt>, die sich im Verzeichnis <tt class="docutils literal">CMFPlone/skins/plone_content</tt> befinden.</p>
<div class="section" id="die-anzeigen-seite-erstellen">
<h5>Die Anzeigen-Seite erstellen</h5>
<p>Um die <em>Anzeigen</em>-Seite zu ändern, nehmen Sie die <em>Anzeigen</em>-Seite zu einem Dokument, kopieren sie in das Verzeichnis <tt class="docutils literal">skins</tt> Ihres Produkts und benennen sie in <tt class="docutils literal">silvercity_view.pt</tt> um. Es gibt keinen Grund, die gesamte Seite neu zu erstellen, wenn diese <em>Anzeigen</em>-Seite so ähnlich ist und Sie lediglich zwei kleine Änderungen vornehmen müssen.</p>
<p>Wie schon erwähnt, spuckt SilverCity HTML aus, in dem der ganze Code mit Hilfe von CSS hervorgehoben wurde, zu dem Sie ein eigenes Stylesheet haben. Sie müssen dafür sorgen, dass die <em>Anzeigen</em>-Seite dieses CSS einfügt und das Haupt-Template einen Slot für CSS namens <tt class="docutils literal">css_slot</tt> besitzt. Um die eigene CSS-Datei in diesen Slot einzufügen, müssen Sie dafür nur einen Wert angeben. Beispiel:</p>
<pre class="literal-block">
&lt;metal:cssslot fill-slot=&quot;css_slot&quot;&gt;

&lt;link
   rel=&quot;stylesheet&quot;
   href=&quot;&quot;
   tal:attributes=&quot;href string:$portal_url/silvercity.css&quot; /&gt;
&lt;/metal:cssslot&gt;
</pre>
<p>Hierbei referenzieren Sie eine CSS-Datei namens <tt class="docutils literal">silvercity.css</tt>. Diese befindet sich im Verzeichnis <tt class="docutils literal">skins</tt>, und Sie werden aus der Skin darauf zugreifen, wenn diese dargestellt wird. Das Originaldokument hat eine Eigenschaft namens <tt class="docutils literal">cookedBody</tt>, was ein Attribut eines Dokuments ist. Ich habe diesen Teil des Codes entfernt und stattdessen den Code eingefügt. Wie Sie bis hierher gesehen haben, gibt die Funktion <tt class="docutils literal">getHTMLCode</tt> das HTML zurück, also müssen Sie nur noch Folgendes tun:</p>
<pre class="literal-block">
&lt;div id=&quot;bodyContent&quot;&gt;
    &lt;div tal:replace=&quot;structure here/getHTMLCode&quot; /&gt;
&lt;/div&gt;
</pre>
<p>Wenn Sie irgendetwas anderes Bestimmtes in diesem Page Template ändern möchten, dann ist jetzt die Gelegenheit dazu. Es wäre eventuell nett, z.B. die Sprache anzuzeigen, in der es geschrieben wurde, oder ein Icon, oder seinen Verlauf zu ändern.</p>
</div>
<div class="section" id="die-bearbeiten-seite-erstellen">
<h5>Die Bearbeiten-Seite erstellen</h5>
<p>Wie bei der <em>Anzeigen</em>-Seite können Sie auch die <em>Bearbeiten</em>-Seite nehmen, in die Skin kopieren und dort in <tt class="docutils literal">silvercity_edit_form.cpt</tt> umbenennen. Das größte Problem dabei ist, dass das <em>Bearbeiten</em>-Formular so entworfen ist, dass es mit einem WYSIWYG-Editor (What You See Is What You Get) wie Epoz benutzt werden kann. Solange kein guter WYSIWYG-Editor für Quellcode in Webbrowsern verfügbar ist, müssen Sie das ausschalten, weil Sie in einem HTML-Editor z.B. kein SQL schreiben können.</p>
<p>Dies ist eine ziemlich große Änderung des Page Templates. Erinnern Sie sich daran, dass Sie es von der Website zum Plone-Buch herunterladen können. Entfernen Sie in diesem Template alle Stellen, in denen der Editor erwähnt wird, und ersetzen Sie sie durch einen einfachen HTML-Textbereich. Lassen Sie den Namen des HTML-Felds unverändert, denn es gibt keinen guten Grund, ihn zu ändern. Außerdem verhält es sich später dann so, wie es soll mit dem Skript, das das Formular auswertet. Ein Dokument hat unten eine Reihe von Auswahlmöglichkeiten für das Format, was normalerweise Einträge wie <em>Einfacher Text</em>, <em>HTML</em> usw. sind. Das werden Sie durch ein Dropdown-Menü für alle Sprachen ersetzen, über die die SilverCity-Hauptbibliothek verfügt. Die zuvor geschriebene Methode <tt class="docutils literal">getLanguages</tt> gibt eine Liste aller Sprachen zurück. Jedes Element ist ein Dictionary, das den Wert, z.B. CPP, und einen netten Namen dafür, z.B. C oder C++, enthält.</p>
<p>Listing 12.6 geht in einer Schleife über die zuvor geschriebene Methode <tt class="docutils literal">getLanguages</tt>. Sie können auch eine Laufvariable für die aktuelle Sprache definieren, damit Sie die aktuelle Sprache in der Schleife über die Sprachen hervorheben können.</p>
<p>Listing 12.6. Hinzufügen eines Dropdown-Menüs für die Sprachauswahl</p>
<pre class="literal-block">
&lt;div class=&quot;field&quot;&gt;
  &lt;label
   for=&quot;language&quot;
   i18n:translate=&quot;label_silvercity_language&quot;&gt;
Language&lt;/label&gt;

    &lt;div class=&quot;formHelp&quot; i18n:translate=&quot;help_silvercity_language&quot;&gt;
        Select the name of the language that you are adding
    &lt;/div&gt;
    &lt;select name=&quot;text_format&quot;
            tal:define=&quot;l here/getLanguage&quot;&gt;
        &lt;option tal:repeat=&quot;item here/getLanguages&quot;
            tal:content=&quot;item/name&quot;
            tal:attributes=&quot;value item/value;
                            selected python:test(item['value'] == l, 1, 0)&quot; /&gt;
    &lt;/select&gt;
&lt;/div&gt;
</pre>
<p>Wenn die <em>Bearbeiten</em>-Seite abgeschickt wird, müssen Sie die Validierer und Aktionen so einrichten, dass sie etwas mit dem Formular machen. Die Validierung sollte überprüfen, ob ein gültiger Titel und eine gültige ID angegeben wurden. Fügen Sie Folgendes zur Datei <tt class="docutils literal">silvercity_edit.cpt.metadata</tt> hinzu:</p>
<pre class="literal-block">
[validators]
validators..Save = validate_id,validate_title
validators..Cancel =
</pre>
<p>Woher kommen diese Validierungen? Nun, ich habe ein wenig geschummelt und habe mir wieder die Validierungen eines Dokuments angeschaut. Dabei erfolgen drei Validierungen, von denen Sie aber nur zwei benötigen. Durch die Überprüfung dessen, was diese Validierung ergibt, können Sie sehen, welche benötigt werden und welche nicht. Sie finden alle Validierungen in <tt class="docutils literal">plone_skins/plone_form_scripts</tt>, und deren Objektname beginnt mit <tt class="docutils literal">validation</tt>.</p>
<p>Nun benötigen Sie die Aktion, also nehmen Sie das Bearbeiten-Skript eines Dokuments (<tt class="docutils literal">document_edit.cpy</tt>) und kopieren es nach SilverCity. Zum größten Teil ist das Skript so in Ordnung, d.h., Sie können es mit nur einer Änderung beibehalten. Ändern Sie die Meldungen von <tt class="docutils literal">Document</tt> in <tt class="docutils literal">Source code</tt>. Listing 12.7 zeigt das Bearbeiten-Skript.</p>
<p>Listing 12.7. Das Bearbeiten-Skript</p>
<pre class="literal-block">
##parameters=text_format, text, file='', SafteyBelt='',
  title='', description='', id=''
##title=Edit a document

filename=getattr(file,'filename', '')
if file and filename:
    # if there is no id, use the filename as the id
    if not id:
        id = filename[max( filename.rfind('/')
                       , filename.rfind('\\')
                       , filename.rfind(':') )+1:]
    file.seek(0)

# if there is no id specified, keep the current one
if not id:
    id = context.getId()

new_context = context.portal_factory.doCreate(context, id)
new_context.edit( text_format
                , text
                , file
                , safety_belt=SafetyBelt )

from Products.CMFPlone import transaction_note
transaction_note('Edited source code %s at %s' %
  (new_context.title_or_id(), new_context.absolute_url())
  )

new_context.plone_utils.contentEdit( new_context
                                   , id=id
                                   , title=title
                                   , description=description )

return state.set(context=new_context,
  portal_status_message='Source code changes saved.')
</pre>
<p>Dieses Skript macht ein paar Dinge. Zuerst holt es den Dateinamen, falls einer existiert. Falls keine ID angegeben ist, wird die ID auf diesen Dateinamen gesetzt. Das bedeutet: Wenn ein Benutzer <tt class="docutils literal">library.c</tt> hochlädt, wird die ID dieses Objekts <tt class="docutils literal">library.c</tt> sein. Als Zweites weist es <tt class="docutils literal">portal_factory</tt> an, ein Objekt zu erstellen (siehe den Kasten &quot;Portal Factory&quot; für weitere Informationen darüber, was das bedeutet). Dann ruft es die <tt class="docutils literal">edit</tt>-Methode auf dem Objekt (die Sie zuvor geschrieben haben) und <tt class="docutils literal">contentEdit</tt> im Werkzeug <tt class="docutils literal">plone_utils</tt> auf. Ohne jetzt tiefer ins Werkzeug <tt class="docutils literal">plone_utils</tt> zu schauen, nimmt <tt class="docutils literal">contentEdit</tt> die angegebenen Schlüsselwörter und ändert diese Attribute, falls die Klasse Dublin Core implementiert. Da Sie das Attribut <tt class="docutils literal">__implements__</tt> vorher eingerichtet haben, wird die <tt class="docutils literal">edit</tt>-Methode in Listing 12.7 diese Arbeit für Sie erledigen. Alle Änderungen an Titel, ID oder Beschreibung werden im Objekt geändert.</p>
<div class="sidebar">
<p class="first sidebar-title">Portal-Factory</p>
<!-- One problem exists with the way objects are created. Before you can even get to the edit form, you have to create an object. Then the edit form for that object will display. In practice, people accidentally create objects, get to the edit form, and then realize it was the wrong type. This is annoying and leaves spare objects lying around in your database. It's like creating a file on the file system, realizing it's wrong, and then leaving it there. -->
<p>Es gibt ein Problem mit der Art, wie Objekte erzeugt werden. Bevor Sie überhaupt zum <em>Bearbeiten</em>-Formular kommen können, müssen Sie ein Objekt erzeugen. Dann wird das <em>Bearbeiten</em>-Formular zu diesem Objekt angezeigt. In der Praxis erzeugen die Leute oft unabsichtlich ein Objekt, kommen zum <em>Bearbeiten</em>-Formular und stellen dann fest, dass es den falschen Typ hat. Das ist ärgerlich und führt dazu, dass ungenutzte Objekte in Ihrer Datenbank herumliegen. Das ist, als ob Sie eine Datei im Dateisystem erzeugen, dann feststellen, dass es eine falsche Datei ist, und sie dann liegen lassen.</p>
<!-- To solve this, the *portal_factory* tool allows you to temporarily create objects. It'll create a temporary object and then let you edit it. Only once you've clicked the edit button will your object be created. To assign an object to *portal_factory*, go to the *portal_factory* tool, and in the form select all the content types for which you'd like to use this tool. The only catch is that you must ensure your edit scripts correctly integrate with the tool, as shown in this example. -->
<p class="last">Um das zu lösen, können Sie mit dem Werkzeug <tt class="docutils literal">portal_factory</tt> Objekte temporär erzeugen. Es erzeugt ein temporäres Objekt und lässt Sie es dann bearbeiten. Aber erst nachdem Sie den <em>Bearbeiten</em>-Button angeklickt haben, wird Ihr Objekt erzeugt. Um ein Objekt an <tt class="docutils literal">portal_factory</tt> zuzuweisen, gehen Sie zum Werkzeug <tt class="docutils literal">portal_factory</tt> und wählen im Formular alle Inhaltstypen, für die Sie dieses Werkzeug benutzen möchten. Der einzige Nachteil ist der, dass Sie garantieren müssen, dass Ihre Bearbeiten-Skripten korrekt mit dem Werkzeug zusammenarbeiten, wie in diesem Beispiel gezeigt wird.</p>
</div>
</div>
</div>
<div class="section" id="installation-des-produkts-in-plone">
<h4>Installation des Produkts in Plone</h4>
<p>Es gibt eine standardisierte Art, ein Produkt in Plone zu installieren: Sie gehen ins Plone-Control Panel und klicken auf das Produkt, um es zu installieren. Dieses Skript benutzt das Werkzeug <tt class="docutils literal">portal_quickinstaller</tt> für die Installation. Damit dieses Produkt funktioniert, müssen Sie eine gewisse Funktionalität offen legen, die das Werkzeug lesen kann. Schließlich wollen Sie, dass so viele Leute wie möglich Ihr Produkt benutzen. Wenn Sie etwas nur für den internen Gebrauch schreiben und Sie es nicht an andere Leute verteilen, können Sie diesen Schritt auslassen. Aber Sie müssen diese Schritte sowieso von Hand ausführen, und es ist immer besser, man hat ein Skript für die Installation.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Der Quick Installer macht aus dieser Installationsfunktion eine externe Methode und führt sie hinter den Kulissen für Sie aus. Außerdem führt er auch noch einige weitere Aufgaben aus. Das heißt, wenn Sie möchten, könnten Sie eine externe Methode erstellen, um das zu machen. Deswegen steht in den  Installationsanweisungen zu den meisten Produkten, dass Sie eine externe Methode erstellen sollen.</p>
</div>
<p>Für eine Integration mit dem Quick Installer müssen Sie ein spezielles Modul namens <tt class="docutils literal">Install.py</tt> im Verzeichnis <tt class="docutils literal">Extensions</tt> erstellen. Dieses Modul muss eine Funktion namens <tt class="docutils literal">install</tt> enthalten. Das Quick Installer-Werkzeug führt die Funktion <tt class="docutils literal">install</tt> aus, und die Ausgabe landet in einer Datei auf dem Server. Da die Methode <tt class="docutils literal">install</tt> das Produkt in den Portal-Typen installieren muss, fügen Sie nun ein FSDV hinzu, das auf das Verzeichnis <tt class="docutils literal">skins</tt> zeigt, und fügen dieses neue Verzeichnis zu den Skin-Ebenen hinzu.</p>
<p>Importieren Sie nun die Funktionen, und richten Sie die Variablen wie üblich ein. Die <tt class="docutils literal">factory_type_information</tt> müssen Sie aus dem Produkt installieren, damit Sie es im Skript verwenden können, wie in Listing 12.8 zu sehen ist.</p>
<p>Listing 12.8. Der Anfang der Installationsfunktion</p>
<pre class="literal-block">
from Products.CMFCore.TypesTool import ContentFactoryMetadata
from Products.CMFCore.DirectoryView import createDirectoryView
from Products.CMFCore.utils import getToolByName
from Products.PloneSilverCity.PloneSilverCity import factory_type_information

from Products.PloneSilverCity.config import plone_product_name, product_name
from Products.PloneSilverCity.config import layer_name, layer_location

def install(self):
    &quot;&quot;&quot; Install this product &quot;&quot;&quot;
</pre>
<p>Danach ist alles generisch und könnte auf jedem Produkt laufen, außer dann natürlich, wenn Sie möchten, dass es bei der Installation irgendwas Spezielles tut. Um Ihr Produkt zum Werkzeug <tt class="docutils literal">portal_types</tt> hinzuzufügen, überprüfen Sie zuerst, ob Ihr Produkt schon registriert ist. Es könnte sein, dass ein anderer schon ein anderes Produkt mit dem gleichen Namen installiert hat. Dazu rufen Sie die Methode <tt class="docutils literal">manage_addTypeInformation</tt> auf, wie in Listing 12.9 angegeben.</p>
<p>Listing 12-9. Rest der Installationsfunktion</p>
<pre class="literal-block">
out = []
typesTool = getToolByName(self, 'portal_types')
skinsTool = getToolByName(self, 'portal_skins')

if id not in typesTool.objectIds():
   typesTool.manage_addTypeInformation(
       add_meta_type =  factory_type_information['meta_type']
       id = factory_type_information['id']
       )
   out.append('Registered with the types tool')
else:
   out.append('Object &quot;%s&quot; already existed in the types tool' % (id))
</pre>
<p>Als Nächstes müssen Sie ein FSDV im <tt class="docutils literal">skins</tt>-Verzeichnis hinzufügen. Wieder müssen Sie als Erstes prüfen, ob Sie nicht schon eines haben. Dann fügen Sie die Verzeichnisansicht wie folgt hinzu:</p>
<pre class="literal-block">
if skinname not in skinsTool.objectIds():
    createDirectoryView(skinsTool, skinlocation, skinname)
    out.append('Added &quot;%s&quot; directory view to portal_skins' % skinname)
</pre>
<p>Und schließlich iterieren Sie über alle Skins und fügen Ihr neues FSDV zu allen Skins hinzu. Dies ist eine generische Funktion. Jede Skin wird als String aufgeführt, in dem alle Ebenen mit Kommata voneinander getrennt sind. Nun müssen Sie den String nur noch aufteilen und Ihre neue Skin nach der Ebene namens <tt class="docutils literal">custom</tt> einfügen wie in Listing 12.10 zu sehen ist.</p>
<p>Listing 12-10. Die Skin in der Installationsmethode setzen</p>
<pre class="literal-block">
skins = skinsTool.getSkinSelections()
for skin in skins:
    path = skinsTool.getSkinPath(skin)
    path = [ p.strip() for p in p.split(',') ]
    if skinname not in path:
        path.insert(path.index('custom')+1, skinname)

        path = &quot;, &quot;.join(path)
        skinsTool.addSkinSelection(skin, path)
        out.append('Added &quot;%s&quot; to &quot;%s&quot; skins' % (skinname, skin))
    else:
        out.append('Skipping &quot;%s&quot; skin' % skin)
return &quot;\n&quot;.join(out)
</pre>
<p>Das wars. Nun ist Ihr Produkt bereit, ausgeführt zu werden.</p>
</div>
<div class="section" id="das-produkt-testen">
<h4>Das Produkt testen</h4>
<p>Um das Produkt zu testen, starten Sie Ihre Plone-Instanz neu, damit sie das Produktverzeichnis einliest. Wenn Sie Ihr Produkt noch nicht im entsprechenden Produktverzeichnis entwickelt haben, dann kopieren Sie es jetzt dorthin, damit es Teil Ihres normalen Installationsprozesses wird. Wenn es mit Ihrem Produkt irgendwelche Probleme gibt, dann startet Zope eventuell noch, aber das Produkt wird dann unter Umständen im Control Panel als defekt angezeigt.</p>
<p>Dann installieren Sie es in Plone mit Hilfe der Seite <em>Produkte hinzufügen/löschen</em> im Plone-Control Panel. Nun sollten Sie zu einem Ordner gehen und ein Quellcode-Objekt hinzufügen können. Das Icon wird Ihr Icon in der Skin sein, und der Name ist jener, den Sie im Dateisystem definiert haben. Danach erhalten Sie die <em>Bearbeiten</em>-Seite. Beachten Sie, dass die URL nun mit <tt class="docutils literal">silvercity_edit_form</tt> endet und das passend geänderte <em>Bearbeiten</em>-Formular anzeigt.</p>
<p>Sie könnten weiteren Code hinzufügen, eine Sprache auswählen und auf <em>Speichern</em> klicken, oder Sie könnten eine Datei von Ihrem Computer hochladen. Nach einem Klick auf <em>Speichern</em> gelangen Sie zu der Anzeigefunktion zurück, und es wird hoffentlich der Code mit der hervorgehobenen Syntax angezeigt.</p>
<p>Dieses Produkt ist ein kleines Beispiel dafür, wie einfach man ein Produkt in Plone schreiben kann. Auch wenn es viele Seiten waren, wurden auf den meisten die Infrastruktur und die Skins eingerichtet. Es läge jetzt nahe, das mit anderen Web-Skriptsprachen wie PHP zu vergleichen. Aber Sie müssen daran denken, dass Sie dadurch, dass Ihr Code in Plone ist, eine ganze Menge Dinge erreicht haben, ohne dafür etwas neu schreiben zu müssen. Insbesondere haben Sie Folgendes erreicht:</p>
<ul class="simple">
<li>Volltext-Suche im Inhalt</li>
<li>Integration mit dem Workflow</li>
<li>Integration mit Portal-Mitgliedschaft und -Authentifikation</li>
<li>Persistenz dank der Plone-Datenbank, ohne SQL schreiben oder andere Arbeit verrichten zu müssen</li>
</ul>
<p>Außerdem kann später dadurch Ihr ganzes Produkt wirklich besser skalieren. Wenn Sie z.B. ein Bug-Tracking-System benötigen, nehmen Sie das Collector-Produkt hinzu, und wenn Sie ein Produkt zur Bilderverwaltung benötigen, nehmen Sie CMFPhoto. Indem Sie das Framework verwenden, verleihen Sie Ihrer ganzen Site eine Menge an Flexibilität und Skalierbarkeit.</p>
<p>Auch wenn dieses Produkt insofern ein wenig mogelt, als dass es eine Menge an vorhandenem Code wiederverwendet, so demonstriert es doch eine ganze Reihe von Schlüsselfunktionen beim Schreiben eines Produkts in Plone.</p>
</div>
<div class="section" id="fehlersuche-bei-der-entwicklung">
<h4>Fehlersuche bei der Entwicklung</h4>
<p>Wenn Sie Ihr eigenes Produkt entwickeln, werden Sie früher oder später zwei Dinge feststellen (es sei denn, Sie verfügen über so viel Zen, dass Sie eigentlich am Code des Zope-Kerns mitschreiben sollten): Ihr Produkt versagt, und Sie müssen den Fehler finden.</p>
<p>Während der Entwicklung möchten Sie eventuell versuchen, das Produkt vom Python-Prompt aus zu importieren, um zu sehen, wie es funktioniert. Leider werden Sie dabei sehr wahrscheinlich einen Fehler bekommen. Das liegt daran, dass Sie bei diesem Import eine Lawine an Zope-bezogenen Imports auslösen. Mit einigen davon können Sie fertig werden, aber nicht mit allen. Eines der häufigen Probleme besteht darin, dass Sie den folgenden Fehler bekommen:</p>
<pre class="literal-block">
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in ?
  File &quot;PloneSilverCity/__init__.py&quot;, line 1, in ?
    import PloneSilverCity
  File &quot;PloneSilverCity/PloneSilverCity.py&quot;, line 4, in ?
    from Globals import InitializeClass
  File &quot;/opt/Zope-2.7/lib/python/Globals.py&quot;, line 23, in ?
    import Acquisition, ComputedAttribute, App.PersistentExtra, os
  File &quot;/opt/Zope-2.7/lib/python/App/PersistentExtra.py&quot;, line 15, in ?
    from Persistence import Persistent
ImportError: cannot import name Persistent
</pre>
<p>Das können Sie lösen, indem Sie sicherstellen, dass Sie vor dem Import von PloneSilverCity erst Zope importieren und die Startup-Methode ausführen. Um Zope importieren zu können, muss das Verzeichnis, das Zope enthält, in Ihrem Pfad enthalten sein. Auf meinem Computer ist das <tt class="docutils literal"><span class="pre">/opt/Zope-2.7/lib/python</span></tt>. Allerdings werden Sie dann beim Versuch, CMFCore zu importieren, Fehler erhalten, falls Sie Zope mit der Option &quot;Instance Home&quot; konfiguriert haben, was vermutlich der Fall ist.</p>
<p>Am leichtesten kann man PloneSilverCity importieren, indem man Zope mit <tt class="docutils literal">zopectl</tt> von der Kommandozeile im Debug-Modus ausführt. Dabei wird ein Python-Prompt aufgemacht, unter dem Sie von Python direkt auf die Zope-Datenbank zugreifen können. Kapitel 14 behandelt das etwas detaillierter, aber Sie können es auch leicht jetzt tun (vorausgesetzt, dass Ihr Zope gerade nicht läuft). Das Skript <tt class="docutils literal">zopectl</tt> finden Sie im Verzeichnis <tt class="docutils literal">bin</tt> Ihrer Zope-Instanz. Auf meinem Computer z.B. ist das <tt class="docutils literal">/var/zope/bin</tt>. Listing 12.11 zeigt ein Beispiel eines laufenden <tt class="docutils literal">zopectl</tt> mit PloneSilverCity.</p>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Beim Niederschreiben dieser Zeilen funktioniert <tt class="docutils literal">zopectl</tt> nicht unter Windows. Auf Linux jedoch ist es eine bequeme Art, Ihren Code zu testen. Leider verlangt <tt class="docutils literal">zopectl</tt>, dass man die Zope-Objektdatenbank (ZODB) sperrt, was man nicht machen kann, während Zope läuft, es sei denn, Sie setzen ZEO ein (was ich in Kapitel 14 erörtern werde).</p>
</div>
<p>Listing 12.11. Fehlersuche im Produkt mittels Zope</p>
<pre class="literal-block">
$ cd /var/zope/bin
$ ./zopectl debug
Starting debugger (the name &quot;app&quot; is bound to the top-level Zope object)
&gt;&gt;&gt; from PloneSilverCity.PloneSilverCity import PloneSilverCity
&gt;&gt;&gt; p = PloneSilverCity(&quot;test&quot;)
&gt;&gt;&gt; p.edit(&quot;python&quot;, &quot;import test&quot;)
&gt;&gt;&gt; p.getRawCode()
'import test'
&gt;&gt;&gt; p
&lt;PloneSilverCity at test&gt;
</pre>
<p>Falls Ihr Produkt defekt ist, bekommen Sie einen Traceback zu einer von zwei Stellen, entweder zur Fehlerprotokollseite oder zu einem der Ereignisprotokolle. Und wenn Sie es wirklich vermasselt haben, dann startet Plone erst gar nicht. Das passiert normalerweise, wenn ein Import fehlschlägt. Wenn das der Fall ist, startet Plone gar nicht erst. Ich empfehle dann, Plone von der Kommandozeile aus zu starten, egal ob unter Windows oder Linux. Mit der Ausgabe des Fehlers in der Konsole haben Sie eine sofortige Fehlermeldung. Listing 12.12 z.B. zeigt, was passiert, wenn Sie versuchen, Plone mit SilverCity zu starten, falls dabei ein beliebiger Import-Fehler auftritt.</p>
<p>Listing 12.12. Ein Beispielfehler beim Hochfahren</p>
<pre class="literal-block">
$ bin/runzope
------
2003-12-19T17:44:05 INFO(0) ZServer HTTP server started at Fri Dec 19 17:44:05 2003
        Hostname: laptop
        Port: 8080
------
2003-12-19T17:44:05 INFO(0) ZServer FTP server started at Fri Dec 19 17:44:05
2003
        Hostname: basil.agmweb.ca
        Port: 8021
------
2003-12-19T17:44:16 ERROR(200) Zope Could not import Products.PloneSilverCity
Traceback (most recent call last):
  File &quot;/opt/Zope-2.7/lib/python/OFS/Application.py&quot;, line 533, in import_product
    product=__import__(pname, global_dict, global_dict, silly)
  File &quot;/var/zope.zeo/Products/PloneSilverCity/__init__.py&quot;, line 1, in ?
    import PloneSilverCity
  File &quot;/var/zope.zeo/Products/PloneSilverCity/PloneSilverCity.py&quot;, line 1
     import ThisModuleDoesNotExist
                                  ^
 ImportError: No module named ThismoduleDoesNotExist
</pre>
<p>An diesem Punkt stoppt Zope, und Sie müssen diesen Import reparieren, bevor Sie es erneut starten können. Wahrscheinlich ist das der am leichtesten zu reparierende Fehler. Er kommt aber wahrscheinlich nur dann vor, wenn Sie das neueste hippige Produkt aus dem Internet installieren, um festzustellen, dass es von einem Dutzend anderen Modulen abhängt.</p>
<p>Die nächste Art möglicher Fehler ist die von Fehlern innerhalb des Programms oder der Programmlogik. Angenommen, Ihr Produkt addiert zwei Zahlen, von denen eine aber ein String ist (was in Python ein Fehler ist). Es wird eine Fehlerausnahme aufgelöst, die Plone mit dem Fehlerwert und -typ an die Benutzerschnittstelle weitergibt. An dieser Stelle sollten Sie auf <em>Plone-Konfiguration</em> und <em>Fehlerprotokoll</em> klicken, um den Traceback anzuschauen, den Fehler zu finden und das Problem zu lösen.</p>
<p>Wenn Sie etwas im Produkt ändern, wird diese Änderung in Python nicht sofort umgesetzt. Um das zu erzwingen, müssen Sie ein Produkt namens <em>Refresh</em> verwenden. Das ist ein unglaublich nützliches Werkzeug für neue Entwickler, das Sie dadurch aktivieren, dass Sie eine Datei namens <tt class="docutils literal">refresh.txt</tt> in Ihrem Verzeichnis <tt class="docutils literal">Products</tt> haben. Sie werden bemerken, dass PloneSilverCity eine solche hat. Dann wählen Sie im ZMI das Control Panel, dann <em>Products</em>, gefolgt von <em>PloneSilverCity</em> (oder dem Namen Ihres Produkts), und klicken anschließend auf den <em>Refresh</em>-Reiter. Falls Ihr Produkt eine Datei namens <tt class="docutils literal">refresh.txt</tt> enthält, können Sie auf den <em>Refresh</em>-Button klicken. Plone wird Ihr Produkt dann dynamisch mitsamt dem ganzen neuen Code neu laden. Falls Sie Zope im Debug-Modus betreiben, können Sie das Produkt so einstellen, dass es diese Überprüfung bei jedem Lauf dynamisch vornimmt, so dass Sie nicht jedes Mal zu diesem Bildschirm zurückkehren müssen.</p>
<p>Leider gibt es hierbei nicht nur Vorteile. Hinter den Kulissen passieren nämlich einige recht &quot;interessante&quot; Dinge mit Python, damit das funktioniert. Tatsächlich kann das Refresh-Produkt zu unerwarteten Ergebnissen in Ihrem Produkt führen, wenn auch zu nichts, was nicht durch einen Neustart von Zope zu reparieren wäre. Relativ einfache Produkte, die lediglich Manipulationen an Daten vornehmen, werden keine Probleme haben, andere hingegen schon. Zu Beginn werden Ihre Produkte jedoch eher einfacher Natur sein, und die Chancen stehen gut, dass Sie an dieser Stelle keine Probleme haben werden.</p>
<p>Und wenn schließlich doch etwas schief geht und Sie nicht herausfinden können, was es ist, dann müssen Sie zu einem Debugger greifen. Sie haben derart viele Möglichkeiten bei der Fehlersuche mit Zope, dass ich hier nur die eine beschreiben möchte, die ich selbst am meisten benutze, den Python-Debugger. Diesen Python-Debugger rufen Sie auf, indem Sie die folgende Zeile zu einem Stück Code hinzufügen:</p>
<pre class="literal-block">
import pdb; pdb.set_trace()
</pre>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">In Python ist es unüblich, zwei Programmzeilen mit einem Semikolon hintereinander zu setzen. Hier ist das aber sehr praktisch, da später beim Löschen oder Auskommentieren nur eine Zeile betroffen ist.</p>
</div>
<p>Das bewirkt, dass ein Breakpoint in der Ausführung des Codes gesetzt wird, an dem Zope die Abarbeitung anhält und den Debugger aufruft. Das ist der Grund, warum man während der Entwicklung Plone wirklich besser von einer Konsole startet. Mit einem Dienst oder Daemon funktioniert das nicht, weil es keine Konsole gibt, mit der eine Verbindung möglich wäre. Wenn Sie Ihr Problem nun reproduzieren, gelangen Sie zum Python-Debugger, mit dem Sie den Fehler in Ihrem Produkt suchen können. In meinem nun reparierten und korrekt importierenden PloneSilverCity habe ich z.B. die folgende <tt class="docutils literal">pdb</tt>-Trace-Funktion in die Methode <tt class="docutils literal">getLanguages</tt> gesetzt:</p>
<pre class="literal-block">
def getLanguages(self):
     &quot;&quot;&quot; Returns the list of languages available &quot;&quot;&quot;
     import pdb; pdb.set_trace()
     langs = []
     ...
</pre>
<p>Wenn Sie Zope nun starten und sich mit der Skin verbinden (was Sie bald hinzufügen werden), wird diese Funktion aufgerufen, und in der Konsole, in der Sie Zope gestartet haben, werden Sie Folgendes sehen können:</p>
<pre class="literal-block">
--Return--
&gt; /var/tmp/python2.3-2.3.2-root/usr/lib/python2.3/pdb.py(992)set_trace()-&gt;None
-&gt; Pdb().set_trace()
(Pdb)
</pre>
<p>Um eine Liste von Hilfestellungen zu erhalten, können Sie <strong>help</strong> eingeben. Die zwei wesentlichen Optionen sind <tt class="docutils literal">n</tt> für nächstes und <tt class="docutils literal">s</tt> für einen Schritt in ein Element hinein. Beispiel:</p>
<pre class="literal-block">
(Pdb) n
&gt; /var/zope.zeo/Products/PloneSilverCity/PloneSilverCity.py(97)getLanguages()
-&gt; langs = []
(Pdb) n
&gt; /var/zope.zeo/Products/PloneSilverCity/PloneSilverCity.py(99)getLanguages()
-&gt; for value, description in list_generators():
(Pdb) langs
[]
</pre>
<p>Für weitere Informationen zum Debugger empfehle ich die Online-Dokumentation auf der Python-Website (<a class="reference external" href="http://python.org/doc/current/lib/module-pdb.html">http://python.org/doc/current/lib/module-pdb.html</a>). Sie haben auch andere Möglichkeiten, Fehler mit Zope zu suchen, z.B. können Sie ZEO verwenden, um einen Interpreter zu bekommen. ZEO wird in Kapitel 14 behandelt. Mit integrierten Entwicklerumgebungen wie <em>Wing</em> (<a class="reference external" href="http://wingide.com/wingide">http://wingide.com/wingide</a>) oder <em>Komodo</em> (<a class="reference external" href="http://www.activestate.com/Products/Komodo">http://www.activestate.com/Products/Komodo</a>) können Sie Fehler in Zope-Instanzen auch auf entfernten Rechnern suchen und haben dabei noch eine nette grafische Benutzerschnittstelle.</p>
</div>
</div>
<div class="section" id="eigene-werkzeuge-schreiben">
<h3>Eigene Werkzeuge schreiben</h3>
<p>Ein Werkzeug ist vor allem deswegen einfacher zu schreiben als ein Inhaltstyp, weil man wenig tun muss, um das Produkt zu registrieren und weil die Benutzerschnittstelle einfach ist. Beispiel: ich benutze ein einfaches Statistikwerkzeug auf meiner ZopeZen-Website (<a class="reference external" href="http://www.zopezen.org">http://www.zopezen.org</a>), das mir Informationen über die Menge an Inhalten, die Anzahl der Benutzer usw. gibt. Dieses einfache Werkzeug gibt ein paar Zahlen aus, die mich als Manager der Site interessieren. Abbildung 12.2 zeigt meine ZopeZen-Statistik.</p>
<a class="reference external image-reference" href="img/12-02.png/image_view_fullscreen"><img alt="img/12-02.png" class="original" src="img/12-02.png" /></a>
<p>Figure 12-2. PloneStats auf ZopeZen</p>
<p>Das sind Statistiken über eine Website, die ich auch bekommen kann, indem ich die Web-Protokolldateien für meinen Plone-Server parse. Werkzeuge wie <em>Analog</em>, <em>Webalizer</em>, <em>WebTrends</em> usw. nehmen Ihnen gerne die Arbeit ab, Ihre Plone- oder Apache-Protokolldateien zu parsen. Auch hierbei gilt, dass Sie den gesamten Code zu diesem Projekt im Kollektiv unter <a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a> im Paket PloneStats finden.</p>
<div class="section" id="das-werkzeug-starten">
<h4>Das Werkzeug starten</h4>
<p>Das Werkzeug sollten Sie auf die gleiche Weise in ein Produktverzeichnis setzen, wie Sie es beim Inhaltstyp gemacht haben, d.h., indem Sie ein Verzeichnis innerhalb des Produktverzeichnisses der Instanz erstellen. In diesen Ordner fügen Sie die Dateien <tt class="docutils literal">refresh.txt</tt>, <tt class="docutils literal">install.txt</tt>, <tt class="docutils literal">readme.txt</tt> und <tt class="docutils literal">__init__.py</tt> hinzu.</p>
<p>In diesem Verzeichnis lautet der Name des Hauptmoduls <tt class="docutils literal">stats.py</tt>. Es enthält den gesamten Code zur Erstellung der Statistiken. Auch hier behandle ich das Aussehen des Moduls, ohne zusätzlichen Zope-Code zu betrachten. Da Sie es aber direkt mit den anderen Plone-Werkzeugen koppeln, macht es allerdings wenig Sinn, es außerhalb von Zope zu benutzen.</p>
<p>Listing 12.13 zeigt den Anfang des Werkzeugs. Dies ist eine einfache Version, die über zwei Methoden verfügt: eine, die die Anzahl der Inhaltstypen nach Typ und Workflow-Zustand zurückgibt, und eine andere für Benutzer, die die Gesamtzahl der Benutzer der Site zurückgibt.</p>
<p>Listing 12.13. Das grundlegende Statistik-Objekt</p>
<pre class="literal-block">
class Stats:
    def getContentTypes(self):
        &quot;&quot;&quot; Returns the number of documents by type &quot;&quot;&quot;
        pc = getToolByName(self, &quot;portal_catalog&quot;)
        # call the catalog and loop through the records
        results = pc()
        numbers = {&quot;total&quot;:len(results),&quot;bytype&quot;:{},&quot;bystate&quot;:{}}
        for result in results:
            # set the number for the type
            ctype = str(result.Type)
            num = numbers[&quot;bytype&quot;].get(ctype, 0)
            num += 1
            numbers[&quot;bytype&quot;][ctype] = num

            # set the number for the state
            state = str(result.review_state)
            num = numbers[&quot;bystate&quot;].get(state, 0)
            num += 1
            numbers[&quot;bystate&quot;][state] = num
        return numbers

    def getUserCount(self):
        &quot;&quot;&quot; The number of users &quot;&quot;&quot;
        pm = getToolByName(self, &quot;portal_membership&quot;)
        count = len(pm.listMemberIds())
        return count
</pre>
</div>
<div class="section" id="das-paket-in-ein-werkzeug-umwandeln">
<h4>Das Paket in ein Werkzeug umwandeln</h4>
<p>Um das Paket in ein Werkzeug umzuwandeln, müssen Sie den gleichen Prozess wie beim Inhaltstyp durchmachen. Mit anderen Worten, Sie müssen das Werkzeug im Modul <tt class="docutils literal">__init__.py</tt> registrieren. Genau wie beim Beispiel mit dem Inhaltstyp erstellen Sie eine Datei namens <tt class="docutils literal">config.py</tt>, die alle Konfigurationen enthält. Diese Datei sieht wie folgt aus:</p>
<pre class="literal-block">
from Products.CMFCore import CMFCorePermissions

view_permission = CMFCorePermissions.ManagePortal

product_name = &quot;PloneStats&quot;
unique_id = &quot;plone_stats&quot;
</pre>
<p>Die Sicherheit bei diesem Produkt ist einfacher, was daran liegt, dass das Produkt selbst recht einfach ist. Es interagiert lediglich mit anderen Werkzeugen und produziert einige Statistiken. Es gibt nichts, was Benutzer hinzufügen, bearbeiten oder löschen könnten, oder etwas, mit dem sie sonstwie interagieren könnten. Das heißt, Sie haben es nur mit einem einzigen Recht, <em>ManagePortal</em>, zu tun, dem Recht nämlich, die Konfiguration von Plone zu verwalten, das normalerweise nur an Manager vergeben wird. Also können nur Manager ins ZMI gehen und die Information sehen, die das Werkzeug bietet. Wenn Sie wollten, könnten Sie recht einfach eine hübsch ausschauende Skin für das Plone-Control Panel oder ein Portlet hinzufügen, das diese Information in Ihrer Site darstellt.</p>
<p>Was <tt class="docutils literal">__init__.py</tt> anbelangt, fügen Sie nun den Initialisierungscode für das Werkzeug hinzu. Es gibt ein besonderes Initialisierungsskript für Werkzeuge namens <tt class="docutils literal">ToolInit</tt>. In diesem Werkzeug sieht die Datei <tt class="docutils literal">__init__.py</tt> wie folgt aus:</p>
<pre class="literal-block">
from Products.CMFCore import utils
from stats import Stats
from config import product_name

tools = (stats.Stats,)

def initialize(context):
    init = utils.ToolInit( product_name,
                    tools = tools,
                    product_name = product_name,
                    icon='tool.gif'
                    )
init.initialize(context)
</pre>
<p>Die Funktion <tt class="docutils literal">ToolInit</tt> kann mehrere Werkzeuge annehmen. In diesem Fall haben Sie es aber nur mit einem zu tun. Bei mehreren Werkzugen können Sie nur einen Produktnamen und ein Produkt-Icon haben, das im ZMI angezeigt wird. Das ist alles, was man braucht, um das Werkzeug zu registrieren. Nun müssen Sie das Hauptmodul vervollständigen, um es in ein echtes Werkzeugobjekt zu verwandeln.</p>
</div>
<div class="section" id="den-werkzeug-code-andern">
<h4>Den Werkzeug-Code ändern</h4>
<p>Als Nächstes fügen Sie den Code zu der Klasse hinzu, um ihn in ein Werkzeug umzuwandeln. Wie beim Inhaltstyp besteht dieser Schritt nur aus dem Hinzufügen der Sicherheit durch Ableitung von den korrekten Basisklassen, z.B. so:</p>
<pre class="literal-block">
from Globals import InitializeClass
from OFS.SimpleItem import SimpleItem
from AccessControl import ClassSecurityInfo

from Products.CMFCore.utils import UniqueObject, getToolByName
</pre>
<p>Die Klasse <tt class="docutils literal">SimpleItem</tt> ist die vorgegebene Basisklasse für ein einfaches Objekt in Zope (nicht für einen Ordner). Tatsächlich erben alle Inhaltstypen von einer Klasse, die irgendwo in der Klassenhierarchie von <tt class="docutils literal">SimpleItem</tt> erbt. Es ist nur so, dass Sie die ganzen zusätzlichen Attribute dieser anderen Klassen nicht benötigen. <tt class="docutils literal">UniqueObject</tt> garantiert, dass es genau eine Instanz dieses Objekts in Ihrer Plone-Site gibt und dass sie nicht umbenannt oder verschoben werden kann. Das heißt, Ihr Objekt wird immer verfügbar sein.</p>
<p>Als Nächstes importieren Sie wie üblich die Variablen aus der Konfigurationsdatei. Durch die Zuweisung an die ID Ihres Objekts garantieren Sie, dass das Werkzeug die ID dessen haben wird, was immer <tt class="docutils literal">unique_id</tt> in der Konfigurationsdatei ist - in diesem Fall <tt class="docutils literal">plone_stats</tt>. Die zwei Basisklassen für das Werkzeug sind <tt class="docutils literal">UniqueObject</tt> und <tt class="docutils literal">SimpleItem</tt>, die das Minimum dessen darstellen, was es benötigt. Beispiel:</p>
<pre class="literal-block">
from config import view_permission, product_name, unique_id

class Stats(UniqueObject,  SimpleItem):
    &quot;&quot;&quot; Prints out statistics for a Plone site &quot;&quot;&quot;
    meta_type = product_name
    id = unique_id
</pre>
<p>Dann müssen Sie die Sicherheit einrichten, wobei Sie wiederum die Klasse <tt class="docutils literal">ClassSecurityInfo</tt> benutzen werden, um explizit Rechte an den Methoden zu setzen. Beispiel:</p>
<pre class="literal-block">
security = ClassSecurityInfo()
security.declareProtected(view_permission, 'getContentTypes')
def getContentTypes(self):
    ...
</pre>
</div>
</div>
<div class="section" id="einige-elemente-zur-benutzerschnittstelle-hinzufugen">
<h3>Einige Elemente zur Benutzerschnittstelle hinzufügen</h3>
<p>Der Hauptcode ist vollständig, also wäre es hübsch, dem Benutzer eine Antwort anzuzeigen, wenn Sie im ZMI auf das Werkzeug klicken, etwa in Form eines Beispiels dafür, wie das Produkt verwendet wird. Dazu werden Sie das ZMI so abändern, dass Sie etwas darstellen können.</p>
<p>Konkret heißt das, dass Sie ein Page Template schreiben, das tut, was Sie von ihm wollen. In diesem Beispiel ist es ein einfaches Page Template, das sich ins ZMI einklinkt. Das ZMI ist eine anspruchslose Benutzerschnittstelle, die lediglich Webseiten für den Benutzer ausspuckt, d.h., die Seite wird nicht durch aufwendige Makros oder Slots erstellt. Sie müssen nur ein wenig HTML schreiben und Folgendes hinzufügen:</p>
<pre class="literal-block">
&lt;span tal:replace=&quot;structure here/manage_tabs&quot; /&gt;
</pre>
<p>Diese eine <tt class="docutils literal">tal:replace</tt>-Funktion bekommt die <em>Management</em>-Reiter und sorgt dafür, dass sie oben auf der Seite erscheinen. Meine ZMI-Seite iteriert über die zwei Methoden des Werkzeugs <tt class="docutils literal">plone_stats</tt> und spuckt die Ergebnisse für den Benutzer aus, wie es in Listing 12.14 zu sehen ist.</p>
<p>Listing 12.14. Eine Seite zur Anzeige in der <em>Management</em>-Schnittstelle</p>
<pre class="literal-block">
&lt;html&gt;
&lt;body&gt;
&lt;span tal:replace=&quot;structure here/manage_tabs&quot; /&gt;

&lt;p&gt;Statistics for this Plone site.&lt;/p&gt;

&lt;h3&gt;Content Types&lt;/h3&gt;
&lt;span tal:define=&quot;numbers here/getContentTypes&quot;&gt;
    &lt;p&gt;
        Total count: &lt;i tal:replace=&quot;numbers/total&quot; /&gt;&lt;br /&gt;
        Content types by type:
    &lt;/p&gt;

    &lt;span tal:repeat=&quot;type python:numbers['bytype'].keys()&quot;&gt;
        &lt;ul&gt;
            &lt;li&gt;
              &lt;span tal:replace=&quot;type&quot; /&gt;:
              &lt;i tal:replace=&quot;python: numbers['bytype'][type]&quot; /&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/span&gt;

    &lt;p&gt;Content types by state:&lt;/p&gt;
    &lt;span tal:repeat=&quot;type python:numbers['bystate'].keys()&quot;&gt;
        &lt;ul&gt;
            &lt;li&gt;
              &lt;span tal:replace=&quot;type&quot; /&gt;:
              &lt;i tal:replace=&quot;python: numbers['bystate'][type]&quot; /&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/span&gt;
&lt;/span&gt;

&lt;h3&gt;Users&lt;/h3&gt;
&lt;p&gt;
  User count: &lt;i tal:replace=&quot;here/getUserCount&quot; /&gt;
&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Sie heißt <tt class="docutils literal">output.pt</tt> und wird ins Verzeichnis <tt class="docutils literal">www</tt> platziert. Sie müssen kein separates Verzeichnis benutzen, aber wenn Sie es tun, ist es leichter zu merken.</p>
<p>Der letzte Schritt besteht darin, dieses Page Template für Ihr Produkt ins ZMI einzuklinken. Das machen Sie dadurch, dass Sie zur Klasse <tt class="docutils literal">Stats</tt> zurückkehren und Folgendes hinzufügen (zuerst importieren Sie die Klasse <tt class="docutils literal">PageTemplateFile</tt>, die mit dem Template aus dem Dateisystem umgehen kann):</p>
<pre class="literal-block">
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
</pre>
<p>Dann registrieren Sie das Page Template als Methode für das Produkt, auf das zugegriffen werden kann. Im Folgenden kann die Methode <tt class="docutils literal">outputPage</tt> nun über das Web aufgerufen werden, und es wird das entsprechende Page Template zurückgegeben:</p>
<pre class="literal-block">
outputPage = PageTemplateFile('www/output.pt', globals())
security.declareProtected(view_permission, 'outputPage')
</pre>
<p>Und schließlich werden die Reiter oben im ZMI von einem Tupel namens <tt class="docutils literal">manage_options</tt> bestimmt, das eine Liste aller Reiter enthält, die auf einer Seite angezeigt werden sollen. Dort müssen Sie die neue Management-Seite einfügen, was Sie wie folgt tun:</p>
<pre class="literal-block">
manage_options = (
    {'label':'output', 'action':'outputPage'},
    ) + SimpleItem.manage_options
</pre>
<div class="section" id="das-werkzeug-testen">
<h4>Das Werkzeug testen</h4>
<p>Nun ist das Werkzeug fertig, d.h., Sie können testen, ob es funktioniert. Zuerst starten Sie Ihre Plone-Instanz neu, damit sie das Produktverzeichnis einliest und Ihr neues Werkzeug registriert. Als Zweites gehen Sie ins ZMI und dort in die obere rechte Ecke zum Dropdown-Menü namens <em>Add</em>. Sie werden feststellen, dass in der Liste nun PloneStats aufgeführt ist. Wählen Sie diese Option, und klicken Sie auf <em>Add</em>. Das nächste Formular listet die verfügbaren Werkzeuge im Produkt PloneStats auf, in diesem Fall erscheint nur eines, wie in Abbildung 12.3 zu sehen ist.</p>
<a class="reference external image-reference" href="img/12-03.png/image_view_fullscreen"><img alt="img/12-03.png" class="original" src="img/12-03.png" /></a>
<p>Abbildung 12.3. Hinzufügen des Werkzeugs</p>
<p>Wählen Sie das Werkzeug, und klicken Sie auf <em>Add</em>. Klicken Sie dann auf das Werkzeug, um zu testen, ob es funktioniert. Sie sollten eine Reihe von Statistiken sehen, wie Sie sie zuvor für ZopeZen gesehen haben.</p>
<p>Dieses Werkzeug ist einfach, weil ich nicht wirklich weiß, welche Art von Darstellung die Leute gerne hätten. Wenn ich ein standardisiertes Berichtwerkzeug erstelle, dann können Sie es benutzen, wie Sie wollen. Einige Ideen, die einem dafür einfallen, sind z.B. eine Seite im Control Panel, ein kleiner Portlet-Kasten, eine PDF-Datei (Portable Document Format) mit hübschen Grafiken, die via E-Mail an einen Manager verschickt wird, oder eine API, damit externe Berichtswerkzeuge wie <em>Crystal Reports</em> mein Werkzeug benutzen können. An dieser Stelle warte ich ab und beobachte, was in der Zukunft alles passiert.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch13.rst">
    <title>13. Entwickeln mit Archetypes</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch13.rst</link>
    <description>Archetypes ist ein Framework, um die Entwicklung von Plone-Produkten zu automatisieren. Wenn einmal eine formale Beschreibung für einen Inhaltstyp vorliegt, erledigt Archetypes praktisch alles, einschließlich der Erstellung von Views und Editformularen für den Entwickler. Dies erlaubt Ihnen, neue Inhaltstypen schnell und mit einer minimalen Codegröße zu entwickeln. Weniger Code bedeutet weniger Fehlerquellen, weniger Code, den Sie verwalten müssen, wenn Plone sich ändert, schnellere Entwicklungszyklen und generell weniger Kosten.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Entwickeln mit Archetypes</h2>
<p>Archetypes ist ein Framework, um die Entwicklung von Plone-Produkten zu automatisieren. Wenn einmal eine formale Beschreibung für einen Inhaltstyp vorliegt, erledigt Archetypes praktisch alles, einschließlich der Erstellung von Views und Editformularen für den Entwickler. Dies erlaubt Ihnen, neue Inhaltstypen schnell und mit einer minimalen Codegröße zu entwickeln. Weniger Code bedeutet weniger Fehlerquellen, weniger Code, den Sie verwalten müssen, wenn Plone sich ändert, schnellere Entwicklungszyklen und generell weniger Kosten.</p>
<p>Da das gesamte Produkt auf der Objektbeschreibung (Schema) basiert, ermöglicht es Ihnen, Tools zur automatischen Codegenerierung zu verwenden. Beispielsweise können Sie mit ArchGenXML, das später in diesem Kapitel behandelt wird, aus einem UML-Modell (Unified Modeling Language), das Sie mit einem UML-Designer Ihrer Wahl erstellt haben, ein lauffähiges Plone-Produkt generieren. Sie haben am Schluss ein lauffähiges Plone-Produkt vorliegen, ohne eine Zeile Code programmiert zu haben. Wenn Sie das Gefühl gehabt haben, dass sich das Schreiben von Produkten in Plone im Kapitel 12 ein wenig nach harter Arbeit anfühlt, wird dieses Kapitel eine willkommene Erleichterung bringen.</p>
<p>Dies bedeutet nicht notwendigerweise, dass Archetypes für jede Art von Produkten das Mittel der Wahl ist; manchmal ist Archetypes für den Zweck nicht optimal. Zum Beispiel hatte in einem Fall mein Inhaltstyp nur ein Feld, aber 16 unterschiedliche Permutationen auf den Daten des Feldes zu präsentieren, was bedeutete, dass nur wenig vom Archetypes-Framework benutzt wurde. Zugegeben, das war ein extremer Fall. Auch ist Archetypes nicht primär gedacht, um Low-level-Programmieraufgaben abzudecken. Aber in den allermeisten Fällen werden Sie sehen, dass Archetypes genau das ist, was Sie brauchen.</p>
<p>Einige Menschen beschweren sich, dass Archetypes das Leben für den Programmierer zu leicht mache und dass es dadurch schwer sei, Geld für eine Arbeit zu verlangen, die nur zehn Minuten dauert. Persönlich hatte ich nie ein Problem damit, denn Archetypes hat mir oft aus einer Sackgasse geholfen, wenn sich plötzlich die Projektanforderung von vier auf vierzehn Inhaltstypen ändert. Hier macht sich die Flexibilität der Archetypes bezahlt.</p>
<p>Eine Anekdote, von der ich einmal gehört habe, betrifft eine Webentwicklungsfirma. Wenn diese Firma Kundenbesuche macht, nehmen sie einen Programmierer mit. Während der Kunde seine Anforderungen vorträgt, tobt sich der Programmierer im Archetypes-Framework aus, und am Ende der Besprechung steht ein schneller Prototyp zur Demonstration zur Verfügung.</p>
<p>Insgesamt gesehen haben die meisten Plone-Entwickler Archetypes als den Weg zur Produktentwicklung akzeptiert, wodurch sich bereits eine große Wissensbasis entwickelt hat und Archetypes sich als Standard in der Plone-Entwicklung etabliert hat. Einige der Schlüsseleigenschaften von Archetypes sind:</p>
<ul class="simple">
<li>Archetypes erzeugt automatisch Seiten zum Ansehen und Editieren, so dass man zu einem gewissen Ausmaß ohne Schreiben von Template-Code auskommt.</li>
<li>Für jedes Objekt wird automatisch eine systemweit eindeutige ID verwaltet, die der Benutzer nicht ändern kann. Dies bedeutet, dass Objekte wiedergefunden werden können, auch nachdem sie innerhalb des Systems verschoben worden sind. Diese UIDs kommen auch bei Referenzen zwischen Objekten zum Einsatz.</li>
<li>Es erzeugt Referenzen zwischen Objekten. Jedes Objekt kann beliebige Referenzen zu anderen Objekten haben. Zum Beispiel können Sie eine beliebige Anzahl von Linkobjekten an ein NewsItem hängen (natürlich nur, wenn dieses mit Archetypes entwickelt wurde).</li>
<li>Es besitzt standardmäßige Sicherheitseinstellungen. Die gesamte Sicherheitsarbeit wird für Sie erledigt. Wenn Sie also mit den Standardeinstellungen zufrieden sind (und das ist meistens der Fall), brauchen Sie dahingehend nichts manuell entwickeln.</li>
<li>Es bietet optional alternative Speichermöglichkeiten für Attribute. So können Sie zum Beispiel bestimmte Attribute in eine relationale Datenbank speichern statt in die ZODB.</li>
<li>Es steht ein Framework zur transparenten Transformation von Feldinhalten zur Verfügung, zum Beispiel eine automatische Umwandlung von Word-Dokumenten in HTML.</li>
</ul>
<p>Von seinem prinzipiellen Aufbau her ist Archetypes nicht notwendigerweise Plone-spezifisch, es könnte also zum Beispiel direkt in Zusammenhang mit dem Content Management Framework (CMF) verwendet werden. Allerdings kommt es derzeit nur in der Plone-Entwicklung zum Einsatz. Allerdings bringt die schemabasierende Entwicklung eine gewisse Zukunftssicherung mit sich, da in mittlerer Zukunft Plone auf Zope 3 portiert werden wird, und wenn das Archetypes-Programmiermodell dort verfügbar ist, wird eine Anpassung von Plone-Produkten wesentlich einfacher sein.</p>
<p>In diesem Kapitel werde ich die Erstellung von neuen Inhaltstypen mit Archetypes behandeln. Dieses Kapitel bringt tatsächlich all die Informationen, die Sie in den letzten Kapiteln gesammelt haben, in einen Zusammenhang und führt schnell durch einige grundlegende Konzepte. Nach einer Anleitung zum Installieren von Archetypes führe ich Sie in die Erstellung eines einfachen Inhaltstyps ein.</p>
<div class="section" id="einfuhrung-in-archetypes">
<h3>Einführung in Archetypes</h3>
<p>Archetypes wird mit den Installationspaketen von Plone geliefert, daher ist es wahrscheinlich, dass Archetypes bereits in Ihrer Plone-Installation enthalten ist. Wenn Sie sich nicht sicher sind, ob das der Fall ist, sollten Sie überprüfen, ob Archetypes in der Produktliste im ZMI im Control Panel aufscheint. In den Codebeispielen habe ich Archetypes 1.2.5 verwendet, da diese Version mit Plone 2.0 geliefert wird. Mittlerweile ist Archetypes in der Version 1.3.x erhältlich, die einige Erweiterungen bietet, auf die ich an entsprechender Stelle hinweisen möchte.</p>
<p>Um Archetypes zu installieren, gehen Sie zur Website <a class="reference external" href="http://sf.net/projects/archetypes">http://sf.net/projects/archetypes</a> und holen sich von dort die entsprechende Release von Archetypes. In meinen Beispielen verwendete ich   <tt class="docutils literal"><span class="pre">archetypes-1.2.5-rc4.tgz</span></tt>.</p>
<pre class="literal-block">
$ tar -zxf archetypes-1.2.5-rc4.tgz
$ cd archetypes-1.2.5-rc4l
</pre>
<p>An diesem Punkt werden Sie entscheiden, was zu installieren ist. Das Minimum, das zu installieren ist, sind das <tt class="docutils literal">Archetypes</tt>-Verzeichnis sowie <tt class="docutils literal">generator</tt>, <tt class="docutils literal">validation</tt> und <tt class="docutils literal">PortalTransforms</tt>. Ab Archetypes 1.3 kommt hier noch <tt class="docutils literal">MimetypesRegistry</tt> hinzu. Zur Installation schieben Sie diese in das <tt class="docutils literal">Products</tt>-Verzeichnis Ihrer Plone-Installation. In meinem Fall ist dies <tt class="docutils literal">/var/zope/</tt>, also führe ich folgende Befehle aus:</p>
<pre class="literal-block">
$ mv Archetypes /var/zope/Products
$ mv generator /var/zope/Products
$ mv validation /var/zope/Products
$ mv PortalTransforms /var/zope/Products
$ mv MimetypesRegistry /var/zope/Products
</pre>
<p>ArchExample und ArchGenXML sind beide optional, und Sie werden sie nicht unbedingt für die Funktion von Plone benötigen. Sie werden aber beide in diesem Kapitel behandelt, so empfiehlt es sich hier, diese zu installieren. In Archetypes 1.3 sind diese Produkte nicht im Basispaket enthalten und müssen von <a class="reference external" href="http://sf.net/projects/archetypes">http://sf.net/projects/archetypes</a> heruntergeladen werden.</p>
<p>Um ArchExample zu installieren, verschieben Sie ArchExample wie folgt in das <tt class="docutils literal">Products</tt>-Verzeichnis Ihrer Plone-Installation:</p>
<pre class="literal-block">
$ cd ..
$ mv ArchExample /var/zope/Products
</pre>
<p>Wenn Sie ArchGenXML einsetzen wollen, können Sie es an einem beliebigen Ort installieren, von wo Sie es als Kommandozeilenbefehl aufrufen können. Ich installiere es normalerweise ebenfalls in mein <tt class="docutils literal">Products</tt>-Verzeichnis zusammen mit allen anderen Produkten. Dort stört es nicht, und ich finde es leicht, wenn ich es zum Generieren von Produkten benötige. Ich installiere es mit folgendem Befehl:</p>
<pre class="literal-block">
$ mv ArchGenXML /var/zope/Products
</pre>
<p>Wie in der ArchGenXML-Dokumentation erwähnt wird, braucht ArchGenXML das <em>PyXML</em>-Modul für Python. Wenn Sie den Windows- oder Mac-Installer für Plone benutzt haben, ist PyXML bereits installiert. Wenn nicht, können Sie es von <a class="reference external" href="http://pyxml.sf.net">http://pyxml.sf.net</a> beziehen. In meinem Fall installierte ich die Version 0.8.3, deshalb führte ich nach dem Herunterladen folgende Befehle aus:</p>
<pre class="literal-block">
$ tar -xvf PyXML-0.8.3.tar.gz
$ cd PyXML-0.8.3
$ python setup.py install
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Normalerweise benötigen Sie unter Unix Root-Rechte, um Python-Pakete wie PyXML ins globale Python-Verzeichnis zu installieren.</p>
</div>
<p>Nun da Sie alles installiert haben, möchte ich Ihnen einige Beispiele zeigen.</p>
<div class="section" id="einblick-in-archetypes">
<h4>Einblick in Archetypes</h4>
<p>Eine ganze Menge von großartigen Beispielen sind für Archetypes verfügbar, deshalb möchte ich anstatt ein neues zu erfinden, ArchExample zeigen, das beim Archetypes-Repository dabei ist. Darin wird ein neuer Inhaltstyp namens <em>Article</em> eingeführt, anhand dessen die Stärken von Archetypes demonstriert werden.</p>
<p><tt class="docutils literal">Article.py</tt> enthält den Kern des Sourcecodes, und Sie werden sehen, dass der Code ziemlich anders aussieht als die früheren Codebeispiele. Eine wichtige Neuerung ist die Verwendung von  Schemata. Der Begriff <em>Schema</em> ist hier angelehnt an den Schemabegriff aus der Datenbankentwicklung.</p>
<pre class="literal-block">
StringField(&quot;blurb&quot;,
            searchable = 1,
            widget = TextAreaWidget(),
            ),
</pre>
<p>Dieses Stück Code zeigt ein Attribut namens <tt class="docutils literal">blurb</tt> innerhalb des Inhaltstyps, das den Datentyp <tt class="docutils literal">string</tt> hat und das im Edit-Formular als TextArea angezeigt werden soll. Ich werde später auf alle Optionen für Felder und Kontrollelemente (Widgets) eingehen. Abbildung 13.1 zeigt den neuen Inhaltstyp <em>Article</em> im Einsatz beim Editieren.</p>
<a class="reference external image-reference" href="img/13-01.png/image_view_fullscreen"><img alt="img/13-01.png" class="original" src="img/13-01.png" /></a>
<p>Abbildung 13.1. Edit-Formular des neuen Inhaltstyps</p>
<p>Mit lediglich vier Codezeilen wurde zu unserem Inhaltstyp ein Feld hinzugefügt. Für jedes Standard-Formelement in Plone gibt es eine Felddefinition in Archetypes; alle &quot;langweiligen&quot; Aufgaben wie z.B. Feldvalidierung, Fehlerbehandlung, richtige Darstellung werden vom Archetypes-Framework für Sie erledigt. Um die Einfachheit zu demonstrieren, ändern Sie einfach die Beschriftung (Label) des Kontrollelements (Widget) auf <em>Article blurb</em> und deklarieren das Eingabefeld als Muss-Feld (required). Diese Änderungen erreichen Sie durch unten stehenden Code:</p>
<pre class="literal-block">
StringField(&quot;blurb&quot;,
            required = 1,
            searchable = 1,
            widget = TextAreaWidget(label=&quot;Article Blurb&quot;),
            ),
</pre>
<p>Es wurde der Parameter <tt class="docutils literal">required = 1</tt> hinzugefügt, der das Feld zu einem Muss-Feld macht, sowie ein <tt class="docutils literal">label</tt>-Parameter, der an das Widget übergeben wird. Wenn Sie Plone neu starten und einen neuen Artikel hinzufügen, werden Sie Ihre Änderungen sehen. Das Feld <em>blurb</em> hat nun eine andere Beschriftung, und durch den roten Punkt beim Eingabefeld wird deutlich, dass es sich um ein Muss-Feld handelt.</p>
<a class="reference external image-reference" href="img/13-02.png/image_view_fullscreen"><img alt="img/13-02.png" class="original" src="img/13-02.png" /></a>
<p>Abbildung 13.2. Das veränderte Eingabefeld</p>
<p>Dies ist nicht nur eine kosmetische Änderung, denn sie spiegelt die Modifikation im darunter liegenden Schema wider, und das ist die wirkliche Stärke von Archetypes. Wenn Sie dies mit dem Schreiben von Python-Produkten vergleichen, werden Sie sehen, dass viel von der Knochenarbeit des sich ständig wiederholenden Kodierens entfällt und im Hintergrund durch das Framework erledigt wird. Man kann es auch so formulieren: Wenn Sie für einen Inhaltstyp das Schema definieren können, ist das Erstellen der Software keine Hexerei mehr. Außerdem sind Änderungen an existierenden Inhaltstypen leicht durchzuführen, was einem bei den schnelllebigen Projekten der heutigen Zeit sehr zugute kommt.</p>
<p>Wenn Sie Änderungen am Code durchführen, um die folgenden Beispiele auszuprobieren, müssen Sie Plone neu starten, um jene wirksam zu machen. Mehr Informationen darüber gibt es später im Abschnitt 'Entwickeln mit Archetypes'.</p>
</div>
<div class="section" id="schemata-felder-und-kontrollelemente">
<h4>Schemata, Felder und Kontrollelemente</h4>
<p>In Archetypes hat jeder Inhaltstyp, der im Prinzip eine registrierte Python-Klasse mit ganz bestimmten Basisklassen ist, ein Schema, das Felddefinitionen enthält, die ihrerseits noch ihre Darstellung über Kontrollelemente (Widgets) definieren.</p>
<p>Abbildung 13.3 zeigt die Beziehung zwischen Schemata, Feldern und Kontrollelementen.</p>
<a class="reference external image-reference" href="img/13-03.png/image_view_fullscreen"><img alt="img/13-03.png" class="original" src="img/13-03.png" /></a>
<p>Abbildung 13.3. Schemata, Felder und Kontrollelemente</p>
<div class="section" id="schemata-und-das-basisschema">
<h5>Schemata und das Basisschema</h5>
<p>Um ein Schema zu erzeugen, werden die Felddefinitionen in einem Tupel (unveränderliche Liste in Python) an den Konstruktor des Schemas übergeben. Zum Beispiel hat das Article-Schema drei Felder: <em>group</em>, <em>burb</em> und <em>body</em>. Der folgende Code stellt den Beginn der Schemadefinition dar:</p>
<pre class="literal-block">
Schema((
    StringField('group',
        vocabulary=ARTICLE_GROUPS,
        widget=SelectionWidget(),
        ),
        # other fields here
        )
</pre>
<p>Es ist möglich, ein Schema aus anderen Schemata zusammenzusetzen. Das passiert auch hier, denn bei jeder Schemadefinition wird das BasisSchema (BaseSchema) zum selbst definierten Schema hinzugefügt.</p>
<p>Das <em>BaseSchema</em> enthält die zwei Elemente, die bei jedem Inhaltstyp in Plone enthalten sind: einen Titel und eine ID (Kurzname), die beide den Konventionen des Plone-Namensschemas entsprechen. In ArchExample passiert dies durch Addieren von <em>BaseSchema</em> zum eigentlichen Schema, zum Beispiel:</p>
<pre class="literal-block">
schema = BaseSchema +  Schema((
    StringField('group',
                vocabulary=ARTICLE_GROUPS,
                widget=SelectionWidget(),
                ),
    ...
</pre>
<p>Die Einträge im Schema werden beim Abrufen der Schemadefinition, wenn das Objekt dargestellt wird, in der gleichen Reihenfolge retourniert wie bei der Schemadefinition. Dies bedeutet, dass Sie durch Verschieben der Felddefinitionen innerhalb des Schemas die Reihenfolge in der Darstellung beliebig einstellen können. Deswegen wurde das Basisschema auch am Beginn addiert, damit ID und Titel als erste Felder erscheinen.</p>
</div>
<div class="section" id="felder">
<h5>Felder</h5>
<p>Bis jetzt haben Sie das <em>StringField</em> gesehen, das ein allgemeines Feld für Textinhalte eines Inhaltstyps darstellt. Eine große Anzahl von Feldtypen ist in Archetypes verfügbar, wie Sie in Tabelle 13.1 sehen. Im Laufe der Zeit entstehen immer weitere Felddefinitionen, und wenn Sie es benötigen, können Sie sich Ihre eigenen Feldtypen dazudefinieren.</p>
<p>Jedes Feld hat ein Default-Kontrollelement, das benutzt wird, wenn Sie kein eigenes spezifizieren. Im Falle von <em>StringField</em> ist dies zum Beispiel <em>StringWidget</em>, ein einzeiliges Textfeld. Im vorigen Beispiel habe ich aber ein <em>TextAreaWidget</em> festgelegt, um eine mehrzeilige Eingabe zu bewirken. All diese Feld- und Widget-Definitionen werden vom Modul <tt class="docutils literal">Products.Archetypes.public</tt> importiert, zum Beispiel:</p>
<pre class="literal-block">
from Products.Archetypes.public import BooleanField
</pre>
<p>Alle Felder werden auf dieselbe Art instanziiert - durch den Konstruktoraufruf an die Klasse <tt class="docutils literal">Field</tt> mit mindestens dem zwingenden Parameter <tt class="docutils literal">name</tt>. Sie können eine zusätzlich beliebige Anzahl von Schlüsselwortparametern übergeben, zum Beispiel:</p>
<pre class="literal-block">
from Products.Archetypes.public import IntegerField
# a simple field for age
age = IntegerField('age')
</pre>
<p>Tabelle 13.1. In Archetypes verfügbare Felder</p>
<table border="1" class="docutils">
<colgroup>
<col width="20%" />
<col width="19%" />
<col width="20%" />
<col width="41%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Typ</th>
<th class="head">Default-Widget</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>BooleanField</td>
<td>Boolesche
Werte</td>
<td>ComputedWidget</td>
<td>Einfache Speicherung von
<tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt> für ein
Feld.</td>
</tr>
<tr><td>DateTimeField</td>
<td>Datums- und
Zeitwerte</td>
<td>CalendarWidget</td>
<td>Speichert Datums- und
Zeitwerte.</td>
</tr>
<tr><td>FileField</td>
<td>Dateien</td>
<td>FileWidget</td>
<td>Speicherung von größeren
Datenpaketen wie etwa großen
Textdateien, Word-Dateien oder
anderen Binärdaten.</td>
</tr>
<tr><td>FixedPointField</td>
<td>Fixpunktzahlen</td>
<td>DecimalWidget</td>
<td>Zum Speichern von Zahlen mit
fixer Kommastelle (z.B.
Geldbeträge).</td>
</tr>
<tr><td>FloatField</td>
<td>Fließkomma</td>
<td>DecimalWidget</td>
<td>Speichern von Zahlen mit
Fließkomma. Entspricht dem
Datentyp <tt class="docutils literal">float</tt> in Python.</td>
</tr>
<tr><td>ImageField</td>
<td>Bilddaten</td>
<td>ImageWidget</td>
<td>Speichert ein Bild und
unterstützt automatische
Skalierung.</td>
</tr>
<tr><td>IntegerField</td>
<td>Ganzzahl</td>
<td>StringWidget</td>
<td>Ganzzahlige Werte, entspricht
dem Python-Typ <tt class="docutils literal">int</tt>.</td>
</tr>
<tr><td>LinesField</td>
<td>Listen</td>
<td>LinesWidget</td>
<td>Speichert Listen von Strings.</td>
</tr>
<tr><td>PhotoField</td>
<td>Image</td>
<td>PhotoWidget</td>
<td>Dasselbe wie ImageField, aber
mit mehr Skalierungsstufen.</td>
</tr>
<tr><td>ReferenceField</td>
<td>Referenzen</td>
<td>ReferenceWidget</td>
<td>Enthält Referenzen zu anderen
Objekten.</td>
</tr>
<tr><td>StringField</td>
<td>Zeichenketten
(String)</td>
<td>StringWidget</td>
<td>Zeichenketten für einzeilige
Texte.</td>
</tr>
<tr><td>TextField</td>
<td>String</td>
<td>TextWidget</td>
<td>Dasselbe wie StringField, aber
für größere, mehrzeilige Texte.
Der Text kann außerdem in
andere Formate transformiert
werden.</td>
</tr>
<tr><td>ComputedField</td>
<td>Pseudofeld</td>
<td>ComputedWidget</td>
<td>Ein Pseudofeld, das nicht als
Wert gespeichert wird, sondern
aufgrund eines Ausdrucks
(Expression) ausgewertet wird.</td>
</tr>
</tbody>
</table>
<p>Jeder Feldtyp hat Attribute, die einzelnen Formularfeldern zugewiesen werden können. Zwei dieser Attribute sind bereits vorgekommen: <em>name</em> und <em>widget</em>.  <em>name</em> ist der einzige erforderliche Parameter und soll keine Umlaute, Leer- oder Sonderzeichen enthalten. Als Konvention hat sich etabliert, dass Feldnamen immer mit Kleinbuchstaben beginnen. Das Attribut <em>name</em> agiert als ID des Feldes und wird intern verwendet, vergleichbar mit dem <em>id</em>-Attribut von Zope-Objekten.</p>
<p>Tabelle 13.2. Feldattribute</p>
<table border="1" class="docutils">
<colgroup>
<col width="22%" />
<col width="47%" />
<col width="30%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
<th class="head">Mögliche Werte</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>accessor</td>
<td>Der Name der Zugriffsmethode auf ein
Feld. Somit können Sie den Namen
einer beliebigen Methode einsetzen,
die den Wert dieses Feldes ermittelt
(z.B. <tt class="docutils literal">specialGetMethod</tt>).</td>
<td>Siehe 'Überschreiben
von Standardmethoden'
später im Kapitel.</td>
</tr>
<tr><td>default</td>
<td>Der Defaultwert für das Feld, wird
beim Instanziieren des Objekts
gesetzt.</td>
<td>Der Wert sollte dem
Datentyp des Feldes
entsprechen.</td>
</tr>
<tr><td>default_method</td>
<td>Alternative zu default: Ein
Stringwert, der den Namen der
Methode enthält, die den <tt class="docutils literal">default</tt>
-Wert errechnet. Zum Beispiel kann
man damit ein Datumsfeld mit dem
aktuellen Datum vorbelegen.</td>
<td>Stringwert (zum
Beispiel
<tt class="docutils literal">getSpecial</tt>
<tt class="docutils literal">Description</tt>).</td>
</tr>
<tr><td>edit_accessor</td>
<td>Ähnlich wie accessor gibt dieses
Attribut den Namen einer
Zugriffsfunktion an, jedoch gibt
diese Funktion den Rohwert eines
Feldes zurück, der im Gegensatz zum
Accessor nicht transformiert wird.</td>
<td>Beliebiger Methodenname
(zum Beispiel
<tt class="docutils literal">rawGetMethod</tt>).</td>
</tr>
<tr><td>enforceVocabulary</td>
<td>Wenn dieses Attribut gesetzt ist,
kann ein Feld mit Werteliste
(Vokabular) nur Werte annehmen, die
im Vokabular enthalten sind.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>index</td>
<td>Wenn Sie ein Feld in einem eigenen
Katalogindex haben wollen, dann
spezfizieren Sie den Indextyp hier
als String. Wenn Sie <tt class="docutils literal">:schema</tt>
anhängen, wird dies auch als
Metadatenspalte hinzugefügt.</td>
<td>Der Name eines
Indextyps, zum Beispiel
<tt class="docutils literal">KeywordIndex</tt> oder
<tt class="docutils literal">KeywordIndex:schema</tt></td>
</tr>
<tr><td>name</td>
<td>Eindeutiger Name des Feldes.</td>
<td>Stringwert; per
Konvention sollte der
Wert mit einem
Kleinbuchstaben
beginnen, ansonsten
muss der Feldname den
Python-Namensregeln für
Variablen entsprechen
(zum Beispiel,
<tt class="docutils literal">description</tt>,
<tt class="docutils literal">user_name</tt> oder
<tt class="docutils literal">coffee_bag_6</tt>)</td>
</tr>
<tr><td>mode</td>
<td>Zugriffsmodus für das Feld;
standardmäßig ist ein Feld für Lesen
und Schreiben (<tt class="docutils literal">rw</tt>) eingestellt.</td>
<td>Für lesenden Zugriff:
<tt class="docutils literal">r</tt>, für schreibenden
Zugriff: <tt class="docutils literal">w</tt>, für
Lesen und Schreiben:
<tt class="docutils literal">rw</tt>.</td>
</tr>
<tr><td>multiValued</td>
<td>Legt fest, ob ein Feld Mehrfachwerte
enthalten kann, zum Beispiel bei
Referenzfeldern.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>mutator</td>
<td>Das Gegenstück zum Accessor. Dieses
Attribut legt den Namen der
Set-Methode fest. Wenn Sie nichts
angeben, wird wie beim Accessor eine
Standardfunktion dafür
auto-generiert.</td>
<td>Methodenname (zum
Beispiel
<tt class="docutils literal">specialSetMethod</tt>)</td>
</tr>
<tr><td>primary</td>
<td>Wenn dieses Attribut auf <tt class="docutils literal">True</tt>
gesetzt ist, dann wird dieses Feld
zum Primärfeld, dessen Inhalt bei
WebDAV- und FTP-Anfragen
zurückgegeben wird (zum Beispiel das
<tt class="docutils literal">body</tt>-Feld bei Plone-Dokumenten).
Pro Inhaltstyp kann es naturgemäß
nur ein Primärfeld geben, und wenn
mehrere angegeben werden, wird das
erste angegebene Primärfeld eines
Schemas genommen.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>required</td>
<td>Legt fest, ob ein Feld eine Eingabe
zwingend vorschreibt.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>schemata</td>
<td>Innerhalb von Schemata kann man
Felder wiederum gruppieren. Felder
mit gleichem <tt class="docutils literal">schemata</tt>-Namen
werden zum Beispiel beim
Edit-Formular eines Inhaltstyps
(<em>base_edit</em>) zu Teilformularen
gruppiert. Dies empfiehlt sich
speziell bei Klassen mit sehr vielen
Feldern.</td>
<td>Stringwert</td>
</tr>
<tr><td>metadata</td>
<td>Legt fest, ob ein Feld ein
Metadatenfeld ist (z.B <tt class="docutils literal">Title</tt>).</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>searchable</td>
<td>Legt fest, ob ein Feld für die
Volltextsuche mitindiziert wird,
indem dieses Feld bei
<tt class="docutils literal">SearchableText</tt> mit
berücksichtigt wird.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>validators</td>
<td>Die Validierer, die in Bezug auf
dieses Feld ausgeführt werden. Beim
Ändern des Feldinhaltes werden diese
Validierer der Reihe nach
aufgerufen.</td>
<td>Beliebiger Validierer;
siehe den Abschnitt
'Eingabevalidierung'
später im Kapitel.</td>
</tr>
<tr><td>vocabulary</td>
<td>Eine Auswahlliste von Werten, aus
denen der Benutzer den Wert für
dieses Feld festlegen kann. Im
Formular stehen diese Werte z.B. als
Dropdown-Liste zur Verfügung.</td>
<td>Liste von Strings (z.B.
<tt class="docutils literal">[&quot;Gruen&quot;, &quot;Rot&quot;,</tt>
`` &quot;Blau&quot;]``) oder
Liste von Wertepaaren
aus Beschriftung und
Wert, z.B.
<tt class="docutils literal"><span class="pre">((&quot;#00ff00&quot;,</span></tt>
<tt class="docutils literal"><span class="pre">&quot;Gruen&quot;),</span></tt>
<tt class="docutils literal"><span class="pre">(&quot;#ff0000&quot;,&quot;Rot&quot;),</span></tt>
<tt class="docutils literal"><span class="pre">(&quot;#0000ff&quot;,&quot;Blau&quot;))</span></tt></td>
</tr>
<tr><td>storage</td>
<td>Legt fest, wie ein Wert
abgespeichert werden soll;
standardmäßig wird
<tt class="docutils literal">AttributeStorage</tt> verwendet, was
bedeutet, dass Feldwerte als normale
Python-Attribute gespeichert werden.</td>
<td>Jede beliebige Storage-
Klasse, wie zum
Beispiel
<tt class="docutils literal">AttributeStorage</tt>
oder <tt class="docutils literal">SQLStorage</tt>.
Sie können eine
vollständige Liste der
Storage-Klassen dem
Archetypes-API
entnehmen. Später im
Kapitel wird die
Verwendung von
<tt class="docutils literal">SQLStorage</tt> näher
erläutert.</td>
</tr>
<tr><td>widget</td>
<td>Das Kontrollelement, das für dieses
Feld verwendet werden soll.</td>
<td>Konstruktoraufruf zu
einer passenden Widget-
Klasse (z.B.
<tt class="docutils literal">TextAreaWidget()</tt>)</td>
</tr>
</tbody>
</table>
<p>Nachdem die Felder und deren Attribute behandelt wurden, ist es Zeit, die einzelnen Kontrollelemente zu behandeln.</p>
</div>
<div class="section" id="kontrollelemente">
<h5>Kontrollelemente</h5>
<p>Ein Kontrollelement, auch Widget genannt, enthält die Information, wie ein Feld eines Objekts dargestellt werden soll. Die Auswahl eines Kontrollelements hängt natürlich mit dem Feldtyp zusammen, so kann ein <em>StringField</em> auf verschiedenste Arten dargestellt werden z.B. mit einem <em>StringWidget</em> oder einem <em>TextAreaWidget</em>. Archetypes enthält eine große Zahl von Kontrollelementen, die alle über <tt class="docutils literal">Products.Archetypes.public</tt> importiert werden können. Zum Beispiel:</p>
<pre class="literal-block">
from Products.Archetypes.public import BooleanWidget
</pre>
<p>Alle Widgets werden auf dieselbe Art instanziiert - durch einen Konstruktoraufruf an die entsprechende Widget-Klasse mit optionalen Parametern. Zum Beispiel:</p>
<pre class="literal-block">
from Products.Archetypes.public import IntegerField
from Products.Archetypes.public import IntegerWidget
# a simple field for age
age = IntegerField('age',
       widget=IntegerWidget(label=&quot;Your age&quot;)
       )
</pre>
<p>Widgets können Extra-Attribute haben, die vom Typ des Widgets abhängen; zum Beispiel kann man bei einem <em>StringWidget</em> ein  <tt class="docutils literal">size</tt>-Attribut setzen, das dann im HTML-Code auch als <tt class="docutils literal">size</tt>-Parameter erscheint. Um etwa ein Textfeld mit 20 Zeichen Breite zu definieren, schreiben Sie folgenden Code:</p>
<pre class="literal-block">
bankAccountNumber = StringField('bank',
   widget=StringWidget(
           label=&quot;Bank account number&quot;,
           size=20)
         )
</pre>
<p>Tabelle 13.3 beschreibt alle Widgets, die von Archetypes zur Verfügung gestellt werden.</p>
<p>Tabelle 13.3. Die verfügbaren Widgets</p>
<table border="1" class="docutils">
<colgroup>
<col width="26%" />
<col width="43%" />
<col width="30%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
<th class="head">Weitere Attribute</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>BooleanWidget</td>
<td>Zeigt eine Checkbox zur Eingabe
von Boolschen Werten an.</td>
<td>--</td>
</tr>
<tr><td>CalendarWidget</td>
<td>Dient zur Eingabe von
Datumswerten. Zeigt Eingabefelder
für Jahr, Monat, Tag und Uhrzeit
sowie ein Popup-Fenster, um den
Tag aus einem Kalender
auszuwählen.</td>
<td>--</td>
</tr>
<tr><td>ComputedWidget</td>
<td>Das Widget für ComputedField. Es
hat folglich kein Eingabefeld,
sondern nur ein Anzeigefeld
(sichtbar in base_view).</td>
<td>--</td>
</tr>
<tr><td>DecimalWidget</td>
<td>Das Widget für das
FixedPointField.</td>
<td><tt class="docutils literal">size</tt></td>
</tr>
<tr><td>FileWidget</td>
<td>Zeigt ein HTML-Element zum Upload
von Dateien an; dient als
Standard-Widget für das
FileField.</td>
<td>--</td>
</tr>
<tr><td>IdWidget</td>
<td>Einfaches HTML-Eingabefeld für
autogenerierte IDs.</td>
<td>--</td>
</tr>
<tr><td>ImageWidget</td>
<td>Dient zum Anzeigen und Editieren
von Bildern.</td>
<td>Es gibt einen Parameter
namens
<tt class="docutils literal">display_threshold</tt>,
mit dem man angeben
kann, welche Bildgröße
(in Bytes angegeben)
inline maximal
angezeigt werden soll.
Wenn die Bildgröße über
diesem Wert liegt, wird
das Bild nur als Link
angezeigt.</td>
</tr>
<tr><td>IntegerWidget</td>
<td>Einfaches Eingabefeld für
Ganzzahlen.</td>
<td><tt class="docutils literal">size</tt></td>
</tr>
<tr><td>KeywordWidget</td>
<td>Dieses Widget zeigt eine Liste
von Schlüsselwörtern vom Katalog
in einem komplexen Widget, wie
Sie es vom   <em>Eigenschaften</em>-
Reiter kennen, der bei jedem
Plone-Objekt zur Verfügung steht.</td>
<td>--</td>
</tr>
<tr><td>LabelWidgets</td>
<td>Zeigt Beschriftungen (Labels) für
Formelemente an. Dieses Widget
editiert keine Werte.</td>
<td>--</td>
</tr>
<tr><td>LinesWidget</td>
<td>Dient zur Eingabe von
LinesFields, also Listen von
Strings.</td>
<td><tt class="docutils literal">rows</tt> und
<tt class="docutils literal">columns</tt></td>
</tr>
<tr><td>MultiSelectionWidget</td>
<td>Auswahlliste; standardmäßig ist
es ein HTML-select Element
(Listbox), bei dem mehrere Werte
gleichzeitig ausgewählt werden
können.</td>
<td><tt class="docutils literal">format</tt>, kann
entweder <tt class="docutils literal">select</tt>
oder <tt class="docutils literal">checkbox</tt> sein.</td>
</tr>
<tr><td>PasswordWidget</td>
<td>HTML-Element zum Eingeben von
Passwörtern.</td>
<td>--</td>
</tr>
<tr><td>RichWidget</td>
<td>Kontrollelement, das den vom
System eingestellten WYSIWYG-
Texteditor (Epoz oder Kupu) als
RichText-Editor einsetzt. Erlaubt
die Eingabe in verschiedenen
Formaten, die automatisch
transformiert werden.</td>
<td>Mögliche Werte sind
<tt class="docutils literal">format</tt>, <tt class="docutils literal">rows</tt>,
<tt class="docutils literal">mode</tt>, und <tt class="docutils literal">cols</tt>.</td>
</tr>
<tr><td>ReferenceWidget</td>
<td>Auswahlelement, um Referenzen zu
anderen Objekten zu editieren.</td>
<td>--</td>
</tr>
<tr><td>SelectionWidget</td>
<td>HTML-Auswahlliste. Wenn der Modus
<tt class="docutils literal">flex</tt> ist (Standardwert), wird
bei mehr als 4 Auswahloptionen
eine HTML-Dropdown-Liste
angezeigt, bei kleinerer Zahl
eine Liste mit Radiobuttons.</td>
<td><tt class="docutils literal">format</tt> kann einer
von den folgenden
Werten sein: <tt class="docutils literal">flex</tt>,
<tt class="docutils literal">select</tt> oder
<tt class="docutils literal">radio</tt>.</td>
</tr>
<tr><td>StringWidget</td>
<td>Eingabeelement für einzeilige
Textfelder.</td>
<td><tt class="docutils literal">size</tt> und
<tt class="docutils literal">maxlength</tt></td>
</tr>
<tr><td>TextAreaWidget</td>
<td>Mehrzeiliges Texteingabefeld, das
auch das Heraufladen von Content
in verschiedenen Formaten
ermöglicht.</td>
<td><tt class="docutils literal">rows</tt>, <tt class="docutils literal">cols</tt>,
<tt class="docutils literal">format</tt>,
<tt class="docutils literal">divider</tt>,
<tt class="docutils literal">append_only</tt></td>
</tr>
</tbody>
</table>
<p>Für jedes der Widgets, die in Tabelle 13.3 aufgelistet sind, beschreibt Tabelle 13.4 die Attribute, die für alle Widgets gelten. Zum Beispiel haben Sie schon das Attribut <tt class="docutils literal">label</tt> gesehen, das die Beschriftung Ihres Widgets festlegt. In Verbindung mit den extra Attributen jedes Widgets haben Sie das komplette Set von Widget-Attributen.</p>
<p>Tabelle 13.4 Attribute der Widgets</p>
<table border="1" class="docutils">
<colgroup>
<col width="16%" />
<col width="47%" />
<col width="37%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
<th class="head">Mögliche Werte</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>label</td>
<td>Legt die Beschriftung fest, die
zusammen mit diesem Feld im
Benutzerinterface erscheint.</td>
<td>Beliebiger Text, z.B.
<tt class="docutils literal">Beginn</tt> für ein Feld mit
Namen <tt class="docutils literal">start_date</tt>.</td>
</tr>
<tr><td>modes</td>
<td>Anzeigemodi des Feldes, es gibt
hier zwei Modi: <tt class="docutils literal">view</tt> (Ansicht)
und <tt class="docutils literal">edit</tt> (Bearbeiten).</td>
<td>Liste von Strings;
Standardwert:
<tt class="docutils literal">(&quot;view&quot;, &quot;edit&quot;)</tt>.</td>
</tr>
<tr><td>populate</td>
<td>Gibt an, ob ein Widget mit dem Wert
des Feldes befüllt werden soll.
Normalerweise ist dies der Fall,
aber in speziellen Fällen wie zum
Beispiel bei Passwortfeldern ist es
nicht erwünscht, den Feldinhalt
angezeigt zu bekommen.
Standardmäßig ist der Wert auf
<tt class="docutils literal">True</tt> gesetzt.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>postback</td>
<td>Wenn beim Ausfüllen des Edit-
Formulars ein Fehler auftritt,
d.h. eine Validierung fehlschlägt,
und man nach der Eingabe wieder auf
das Formular geleitet wird, um die
Eingabe zu korrigieren, legt dieses
Attribut fest, ob ein Widget mit
dem bereits eingegebenen Wert
wieder befüllt werden soll.
Standardmäßig ist dieses Attribut
auf <tt class="docutils literal">True</tt> gesetzt, aber zum
Beispiel bei Passwortfeldern ist
dies nicht erwünscht.</td>
<td><tt class="docutils literal">True</tt> oder <tt class="docutils literal">False</tt></td>
</tr>
<tr><td>visible</td>
<td>Legt die Sichtbarkeit eines Feldes
fest. Es ist ein Python-Dictionary,
das zu dem jeweiligen Anzeigemodus
die Sichtbarkeit als String angibt.
Mögliche Werte sind <tt class="docutils literal">visible</tt>,
<tt class="docutils literal">hidden</tt> (als verstecktes
HTML-Element angezeigt) und
<tt class="docutils literal">invisible</tt> (überhaupt nicht
dargestellt).</td>
<td>Zum Beispiel bedeutet
<tt class="docutils literal">{'view': 'visible',</tt>
<tt class="docutils literal">'edit': 'hidden' }</tt>
dass das Feld zwar in der
Standardansicht (base_view)
angezeigt wird, aber in der
Bearbeitungsansicht
(base_edit) versteckt wird.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="einige-beispiele-fur-feld-und-widget-kombinationen">
<h5>Einige Beispiele für Feld- und Widget-Kombinationen</h5>
<p>Dieser Abschnitt zeigt einige nützliche Kombinationen, die oft verwendet werden und als gute Beispiele dienen. In diesem Beispiel soll eine Auswahlliste von Früchten für ein Feld namens <em>fruit</em> zur Verfügung gestellt werden. Als Feldtyp wird hier  <tt class="docutils literal">StringField</tt> mit einem eigenen Vokabular genommen und als Widget das  <tt class="docutils literal">SelectionWidget</tt>, das für das Darstellen der Auswahlliste verantwortlich ist:</p>
<pre class="literal-block">
StringField('fruit'
    vocabulary = [&quot;Apple&quot;, &quot;Orange&quot;, &quot;Mango&quot;, &quot;Banana&quot;],
    widget = SelectionWidget(label = &quot;Favourite Fruit&quot;)
    )
</pre>
<p>Das  <tt class="docutils literal">ImageField</tt> ist sehr nützlich, um einen Inhaltstyp mit Bildern zu versehen. Hier ein einfaches Beispiel für ein ImageField:</p>
<pre class="literal-block">
ImageField('portrait',
    widget = ImageWidget(label = &quot;My picture&quot;),
    )
</pre>
<p>Das folgende Beispiel zeigt einen etwas komplizierteren Inhaltstyp. Die meisten Inhaltstypen haben ein Primärfeld, das Daten enthalten kann, wie zum Beispiel der Inhaltstyp <tt class="docutils literal">Document</tt>, wo <tt class="docutils literal">body</tt> das Primärfeld ist. Dieses Body-Feld ist der Haupttext des Inhaltstyps. Um dies zu erreichen, muss man nur wenige Attribute setzen.</p>
<p>Zuerst solte dieses Feld volltextindiziert sein, was durch das Setzen des <tt class="docutils literal">searchable</tt>-Attributs erreicht wird. Außerdem sollte dieses Feld bei FTP- und WebDAV-Anfragen zurückgegeben werden, deshalb setzen Sie hier das Attribut <tt class="docutils literal">primary</tt> auf <tt class="docutils literal">1</tt>. Lesen Sie mehr dazu im Kasten 'Das Primary Field: Marshaling und Behandeln von FTP- und WebDAV-Anfragen'. Wenn dieses Feld verschiedene Inhaltstypen akzeptieren soll, können Sie diese mit dem Attribut <tt class="docutils literal">allowable_content_types</tt> einstellen. (Zur Information:. allowable_content_types bezieht sich hier nicht auf Plone-Inhaltstypen, sondern auf mögliche MIME-Types des Textfeldes). Durch die Verwendung von <tt class="docutils literal">RichWidget</tt> wird außerdem das Editieren des Textes mit dem im System eingestellten WYSIWYG-Editor (Epoz oder Kupu) unterstützt.</p>
<pre class="literal-block">
TextField('body'
          searchable = 1,
          primary = 1,
          default_output_types = 'text/html',
          allowable_content_types = ('text/plain',
                                     'text/structured',
                                     'text/restructured',
                                     'text/html'),
          widget = RichWidget(label = 'Body'),
          )
</pre>
<div class="sidebar">
<p class="first sidebar-title">Das Primary Field: Marshaling und Behandeln von FTP- und WebDAV-Anfragen</p>
<!-- Plone is an object-oriented system where an object has many attributes and can't simply be represented as a plain file. Unfortunately, most the existing protocols such as FTP and WebDAV treat content in exactly this manner. So there needs to be some way to translate between the two, and the primary field does this. By setting the primary field on an object, this field will become the one that's sent and received by these protocols?rather than the entire object. -->
<p>Plone ist ein objektorientiertes System, in dem ein Objekt mehrere Attribute hat und nicht einfach als flache Datei repräsentiert werden kann. Aber die meisten Transportprotokolle, wie etwa FTP oder WebDAV, behandeln den Inhalt leider genau so. Deswegen benötigen wir eine Übersetzung zwischen diesen Welten, und der <tt class="docutils literal">PrimaryFieldMarshaller</tt>, der das Objekt auf sein primäres Feld reduziert, ist eine der Möglichkeiten. Durch Festlegen eines Primärfeldes für eine Objektklasse wird dieses Feld zu dem, was anstatt des gesamten Objekts von diesen Protokollen zurückgegeben wird.</p>
<!-- This is an imperfect solution to a tricky problem, of course. If you've used FTP or External Editor on a file, you'll know that it tries to solve this by placing a number of lines at the top of the page containing key/value pairs for metadata on the object. This is another attempt to solve the same problem. -->
<p>Dies ist eine pragmatische Lösung für ein kompliziertes Problem und als solche natürlich nicht perfekt, da die Metadaten verloren gehen. Wenn Sie schon einmal Plone-Dokumente über FTP oder mit dem External Editor geöffnet haben, werden Sie gesehen haben, dass er die Metadaten als durch Doppelpunkt getrennte Schlüssel/Wert-Paare am Beginn des Textdokuments platziert. Dies ist ein weiterer Ansatz, um dieses Probem zu lösen.</p>
<!-- To set up primary field marshaling, you need to add the following to your schema: *marshall=some_marshaller()*. Currently, there are only two marshalers: a *PrimaryFieldMarshaller*, which takes the whole content and places into your object, and an *RFC822Marshaller*. This second marshaler handles the content for field name/value pairs as used in e-mail. For this chapter's purposes, I'll use the *PrimaryFieldMarshaller* to handle content with External Editor. -->
<p class="last">Deswegen erlaubt Archetypes, pro Klasse einen so genannten <em>Marshaller</em> festzulegen, der die Übersetzung eines Objekts in eine flache Datei und umgekehrt durchführt. Archetypes enthält zwei Marshaller, nämlich  <tt class="docutils literal">PrimaryFieldMarshaller</tt>, der nur das Primärfeld eines Schemas behandelt, und den <tt class="docutils literal">RFC822Marshaller</tt>, der, wie gerade beschrieben, die Metadaten in den exportierten Text mit hineinpackt. Sie können sich aber selbstverständlich für eigene Bedürfnisse einen eigenen Marshaller schreiben, um z.B Objekte als XML zu exportieren. Um einen bestimmten Marshaller für eine Klasse festzulegen, muss man in der Schemadefinition folgende Zeile hinzufügen: <tt class="docutils literal">marshall=irgendeinMarshaller()</tt>, im Falle des PrimaryFieldMarshaller also: <tt class="docutils literal">marshall=PrimaryFieldMarshaller()</tt></p>
</div>
</div>
<div class="section" id="validieren-von-eingaben">
<h5>Validieren von Eingaben</h5>
<p>Obwohl die automatisch generierten Formulare von Archetypes das Editieren von Objekten korrekt behandeln und auch Fehler wie etwa das Nichtausfüllen von zwingenden Feldern abfangen, wird manchmal eine detailliertere Validierung von Feldeingaben benötigt. Um das zu erreichen, kann man pro Feld eine Kette von Validierern festlegen, die vor dem Speichern des Wertes der Reihe nach aufgerufen werden.</p>
<p>Um dies zu erreichen, ordnen Sie dem Feld den Validierer <tt class="docutils literal">IsInt</tt> mit dem <tt class="docutils literal">validators</tt> Parameter zu, wie das folgende Beispiel zeigt:</p>
<pre class="literal-block">
from Products.Archetypes.public import IntegerField
from Products.Archetypes.public import IntegerWidget
# a simple field for age
age = IntegerField('age',
      validators=(&quot;isInt&quot;),
      widget=IntegerWidget(label=&quot;Your age&quot;)
      )
</pre>
<p>Woher kommt <tt class="docutils literal">IsInt</tt>? <tt class="docutils literal">IsInt</tt> ist der Name des Validierers, der im <tt class="docutils literal">validation</tt>-Framework registriert ist; in diesem Paket ist ein Satz von vordefinierten Validierern enthalten, die in Tabelle 13.5 aufgelistet sind. Um mit den genaueren Details vertraut zu werden, empfiehlt es sich, den Sourcecode des Pakets in <tt class="docutils literal">Products/validation/validators/__init__.py</tt> zu studieren. Einige der Validierer sind spezifisch für die USA, so dass man sie im europäischen Markt nur bedingt einsetzen kann, wie zum Beispiel die Überprüfung, ob ein String eine gültige US-Sozialversicherungsnummer ist.</p>
<p>Tabelle 13.5. Verfügbare Validierer</p>
<table border="1" class="docutils">
<colgroup>
<col width="42%" />
<col width="58%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>isDecimal</td>
<td>Überprüft, ob der eingegebene Text einer
Dezimalzahl entspricht. Beinhaltet positive
und negative Zahlenliterale sowie die
Exponentialnotation.</td>
</tr>
<tr><td>isInt</td>
<td>Überprüft, ob der Text eine Ganzzahl ist.</td>
</tr>
<tr><td>isPrintable</td>
<td>Überprüft, ob ein Text druckbar ist, also
nur aus Buchstaben, Ziffern und dem
Dollarzeichen besteht.</td>
</tr>
<tr><td>isSSN</td>
<td>Prüft, ob ein Text eine gültige
US-amerikanische Sozialversicherungsnummer
ist.</td>
</tr>
<tr><td>isUSPhoneNumber</td>
<td>Prüft, ob ein Text eine gültige
US-amerikanische Telefonnummer ist.</td>
</tr>
<tr><td>isInternationalPhoneNumber</td>
<td>Prüft, ob ein Text eine gültige
internationale Telefonnummer ist.</td>
</tr>
<tr><td>isZipCode</td>
<td>Prüft, ob ein Text eine gültige
US-Postleitzahl mit 5 oder 9 Zeichen ist.</td>
</tr>
<tr><td>isURL</td>
<td>Prüft, ob ein Text mit <tt class="docutils literal"><span class="pre">http://</span></tt>,
<tt class="docutils literal"><span class="pre">ftp://</span></tt> oder <tt class="docutils literal"><span class="pre">https://</span></tt> beginnt.</td>
</tr>
<tr><td>isEmail</td>
<td>Prüft, ob ein Text eine korrekte
E-Mail-Adresse ist.</td>
</tr>
</tbody>
</table>
<p>Es ist auch möglich und nicht sehr kompliziert, eigene Validierer zu schreiben und zu registrieren. Ein Validierer ist eine einfache Klasse, die das <tt class="docutils literal">IValidator</tt>-Interface implementiert, einen einfachen Konstruktor hat und eine <tt class="docutils literal">__call__</tt>-Methode aufweist. Es gibt schon zwei vorbereitete Validiererklassen, die nur noch mit den richtigen Parametern instanziiert und registriert werden müssen:  <tt class="docutils literal">RegexValidator</tt> und  <tt class="docutils literal">RangeValidator</tt>. Um zum Beispiel einen Validierer zu definieren, der Altersangaben auf einen Wertebereich von 0 bis 150 Jahren verifiziert, müssen folgende Zeilen am Beginn Ihres Python-Moduls hinzugefügt werden:</p>
<pre class="literal-block">
from validation.validators.validator import RangeValidator
from validation import validation

# the RangeValidator takes a name for the validator
# and a start and end value
validAge = RangeValidator(&quot;validAge&quot;, 0, 150)
validation.register(validAge)
</pre>
<p>Anschließend muss noch der Validierer dem Feld zugeordnet werden:</p>
<pre class="literal-block">
validators=(&quot;isInt&quot;,&quot;validAge&quot;),
</pre>
<p>Zuerst überprüft der Code, dass Sie eine gültige Ganzzahl eingegeben haben, dann wird verifiziert, dass die Altersangabe plausibel ist. Wenn Sie einen gänzlich neuen Validierer schreiben wollen, müssen Sie eine Validiererklasse schreiben und eine Instanz davon im Validierersystem registrieren. In diesem Beispiel wird eine Validiererklasse geschrieben, die verifiziert, ob der Datumswert zwischen 2 angegebenen Zeitpunkten liegt. Mit diesem Validierer könnte man zum Beispiel prüfen, ob ein Urlaub innerhalb der Schulferien liegt.
Dazu muss gesagt werden, dass der <tt class="docutils literal">IsInt</tt>-Validierer nur der Vollständigkeit halber  erwähnt wird. Weiters muss darauf hingewiesen werden, dass dieses Codebeispiel erst mit Archetypes 1.3.2 funktioniert , da <tt class="docutils literal">RangeValidator</tt> in den vorigen Versionen einen Bug enthielt.</p>
<p>Dazu definieren Sie nun einen Validierer namens  <tt class="docutils literal">DateRangeValidator</tt> und registrieren ihn im Validation-Framework. Sie können dies in einem eigenen Python-Modul oder am Beginn des Moduls Ihres Inhaltstyps einfügen. Der  <tt class="docutils literal">DateRangeValidator</tt> arbeitet ähnlich wie der <tt class="docutils literal">RangeValidator</tt>, nur dass er mit Datumswerten der Klasse <tt class="docutils literal">DateTime</tt> arbeitet (mehr zur Klasse <tt class="docutils literal">DateTime</tt> finden Sie in Anhang A). Eine Validiererklasse muss drei Voraussetzungen erfüllen: Sie muss das Interface <tt class="docutils literal">ivalidator</tt> implementieren; sie muss ein Attribut namens <tt class="docutils literal">name</tt> besitzen, mit dem die Validiererklasse registriert wird; und sie muss aufrufbar sein, indem sie eine Methode namens <tt class="docutils literal">__call__</tt> besitzt, die im Moment der Validierung aufgerufen wird. Das folgende Beispiel zeigt die Validiererklasse  <tt class="docutils literal">DateRangeValidator</tt>:</p>
<pre class="literal-block">
from Products.validation.interfaces import ivalidator
from DateTime import DateTime

class DateRangeValidator:
   __implements__ = (ivalidator,)

   def __init__(self, name, begindate, enddate):
       self.name = name
       self.begindate=begindate
       self.enddate=enddate

   def __call__(self, value, *args, **kwargs):

       if not isinstance(value, DateTime):
           value = DateTime(value)

       if self.begindate &lt; value and value &lt; self.enddate:
           return True
       else:
           return &quot;the given date is not between %s and %s&quot;%(str(self.begindate), str(self.enddate))

christmas = DateRangeValidator(&quot;ChristmasHolidays&quot;,
    DateTime('2004-12-18 00:00:00'),
    DateTime('2005-01-09 00:00:00'),)
validation.register(christmas)
</pre>
<p>Anschließend muss in der Felddeklaration des Schemas noch das <tt class="docutils literal">validators</tt>-Attribut gesetzt werden:</p>
<pre class="literal-block">
validators=(&quot;ChristmasHolidays&quot;,)
</pre>
</div>
</div>
<div class="section" id="definition-von-ansichten-views-und-aktionen-actions-in-der-basisklasse">
<h4>Definition von Ansichten (Views) und Aktionen (Actions) in der Basisklasse</h4>
<p>Archetypes erzeugt für jeden Inhaltstyp die Standard-Ansichten und -Aktionen basierend auf Anforderungen, die den meisten Anwendungen gerecht werden. Diese Standard-Aktionen sind <em>view</em>, <em>edit</em> und <em>properties</em>. In Ihrem Produkt werden Sie keine Page Templates dazu finden, denn diese Views werden automatisch zum Zeitpunkt der Darstellung umgesetzt. Aber selbstverständlich können Sie für Ihre Klasse eigene Ansichten programmieren und anpassen.</p>
<p>Meistens ist es so, dass man für den Inhaltstyp eine eigene <em>view</em>-Ansicht programmieren will, denn die Standardansicht zählt einfach nur die Attribute des Schemas mit den dazugehörigen Werten auf und erhebt auch nicht den Anspruch, eine perfekte Seite zu sein, wohingegen die <em>edit</em>- und <em>properties</em>-(Metadaten-)Ansichten von Haus aus den meisten Ansprüchen gerecht werden.</p>
<p>Im Prinzip wird für jeden Inhaltstyp eine Klasse geschrieben, vergleichbar mit dem Schreiben von Klassen im letzten Kapitel, wobei pro Plone-Produkt beliebig viele solcher Klassen enthalten sein können. Jede Klasse, die einen Inhaltstyp beschreibt, erbt von einer Archetypes-spezifischen Basisklasse, in den meisten Fällen entweder von  <tt class="docutils literal">BaseContent</tt> oder <tt class="docutils literal">BaseFolder</tt>. Erstere Basisklasse wählt man für alle einfachen Inhaltstypen, wie zum Beispiel <tt class="docutils literal">Article</tt>, während <tt class="docutils literal">BaseFolder</tt> zum Einsatz kommt, wenn man einen Inhaltstyp schreiben will, der seinerseits andere Objekte enthalten soll (im Zope-Jargon <em>folderish object</em> genannt). In diesen Basisklassen ist alles enthalten, was vom Archetypes-Framework benötigt wird, so dass man sich in der eigenen Klasse auf das Wesentliche konzentrieren kann.</p>
<p>Wie ich jetzt zeigen werde, besteht dies im Wesentlichen aus zwei Teilen. Zuerst definieren wir eine Aktion, indem wir diese in der Factory-Type-Information (siehe letztes Kapitel) in Form eines Attributs namens <tt class="docutils literal">actions</tt> definieren, das eine Liste der einzelnen Aktionsdefinitionen enthält. Zum Beispiel:</p>
<pre class="literal-block">
from Products.Archetypes.public import BaseContent

class Article(BaseContent):
    # other stuff
    actions = ({ 'id': 'view',
                 'name': 'View',
                 'action': 'string:${object_url}/article_view',
                 'permissions': (CMFCorePermissions.View,)
                },)
</pre>
<p>Zweitens benötigen Sie ein PageTemplate für den View namens <tt class="docutils literal">article_view</tt>. Der Name des Templates muss zu der URL im <tt class="docutils literal">action</tt>-Parameter passen, denn unter diesem Namen wird das Page Template in der Plone-Skin gesucht. In diesem Fall wird das passende Page Template im <tt class="docutils literal">skins/archexample</tt>-Verzeichnis unseres Produkts abgelegt. Im Anhang B können Sie ein vollständiges Listing dieses Page Templates finden. Beim Schreiben dieses Views haben Sie volle Gestaltungsfreiheit.</p>
<p>Um die Änderungen wirksam werden zu lassen, muss Plone neu gestartet werden. In diesem Fall wurde eine Aktion neu definiert, die während des Installationsprozesses des Produkts im <em>portal_types</em>-Tool registriert wird. Deswegen muss das Produkt zusätzlich mit dem QuickInstaller reinstalliert werden, indem Sie in Plone auf <em>Plone konfigurieren</em> klicken.</p>
<p>Alle Elemente in der Factory-Type-Information (FTI) können als gleichnamige Attribute der Klasse überschrieben werden. Um zum Beispiel das <tt class="docutils literal">content_icon</tt>-Element der FTI zu überschreiben, können Sie in der Klasse einfach ein Attribut mit Namen <tt class="docutils literal">content_icon</tt> definieren. Sehen Sie sich dazu folgendes Beispiel an:</p>
<pre class="literal-block">
class SomeProduct(BaseContent):
          &quot;&quot;&quot; Some product &quot;&quot;&quot;
          content_icon = &quot;some_icon.gif&quot;
</pre>
</div>
<div class="section" id="uberschreiben-von-standardmethoden">
<h4>Überschreiben von Standardmethoden</h4>
<p>In Tabelle 13.2 habe ich die Möglichkeit des Überschreibens von Standardmethoden erwähnt, die in einem Inhaltstyp auftauchen können, etwa  <tt class="docutils literal">accessor</tt> oder <tt class="docutils literal">mutator</tt>. Dies ist eine Option für Fortgeschrittene, die es erlaubt,den Mechanismus zu beeinflussen, wie Felder editiert werden.</p>
<p>Wie im Sourcecode im letzten Kapitel greifen Sie auf die Attribute niemals direkt zu, sondern über die dazugehörigen Get- und Set-Methoden, die in Archetypes <tt class="docutils literal">accessors</tt> und <tt class="docutils literal">mutators</tt> genannt werden. Diese Zugriffsmethoden werden automatisch von Archetypes generiert. Im Falle eines Feldes namens <em>blurb</em> heißen diese beiden Methoden <tt class="docutils literal">getBlurb</tt> und <tt class="docutils literal">setBlurb</tt>, das Präfix <tt class="docutils literal">set</tt> oder <tt class="docutils literal">get</tt> wird dem großgeschriebenen Feldnamen vorangestellt.</p>
<p>Jedoch werden Sie vielleicht im Accessor oder Mutator etwas anderes machen wollen. Nehmen wir an, Sie wollen beim Speichern den Wert eines Feldes filtern und korrigieren, wie zum Beispiel die Schreibweise Ihrer Firma korrigieren oder beim Ändern eines Feldinhaltes auch andere Felder ändern. Dies können Sie erreichen, indem Sie die oben erwähnten Standardmethoden überschreiben. Im folgenden Beispiel werden Sie eine neue Methode namens  <tt class="docutils literal">getSpecialBlurb</tt> schreiben, die im <tt class="docutils literal">blurb</tt>-Wert den Text <tt class="docutils literal">Perl</tt> durch <tt class="docutils literal">Python</tt> ersetzt, bevor der Wert zurückgegeben wird.</p>
<pre class="literal-block">
class Article(BaseContent):

    def getSpecialBlurb(self):
        &quot;&quot;&quot; The view for an article &quot;&quot;&quot;
        blurb = self.getField('blurb').get(self)
        blurb = blurb.replace('Perl', 'Python')
        return blurb
</pre>
<p>Außerdem müssen Sie noch Ihr Feld ändern, damit es diese Methode verwendet:</p>
<pre class="literal-block">
StringField('blurb',
    searchable=1,
    widget=TextAreaWidget(),
    accessor=&quot;getSpecialBlurb&quot;,
),
</pre>
<p>In diesem Beispiel wird bei jedem lesenden Zugriff auf das <em>blurb</em>-Feld in einer View- oder Edit-Seite der Wert von <tt class="docutils literal">getSpecialBlurb</tt> zurückgegeben. Archetypes weiß, dass es diese Methode verwenden soll, weil deren Name im <tt class="docutils literal">accessor</tt>-Parameter der Felddeklaration festgelegt ist. Ein bisher noch nicht erwähnter Griff in die Trickkiste kommt hier zum Einsatz: Um von <tt class="docutils literal">getSpecialBlurb</tt> den Feldinhalt zu bekommen, muss man zuerst das Feldobjekt bekommen und dort die <tt class="docutils literal">get</tt>-Methode aufrufen, was man durch den etwas komplexen Ausdruck <tt class="docutils literal"><span class="pre">blurbself.getField('blurb').get(self)</span></tt> erreicht. Dieses Muster, ein Feldobjekt zu holen und dann eine Methode darauf aufzurufen, ist in Archetypes recht gebräuchlich.</p>
<p>Das praktische Ergebnis davon ist nun, dass, sobald jemand das Wort <tt class="docutils literal">Perl</tt> im Feld <em>blurb</em> eingibt, dieses durch <tt class="docutils literal">Python</tt> ersetzt wird.</p>
</div>
<div class="section" id="den-rest-des-inhaltstyps-zusammensetzen">
<h4>Den Rest des Inhaltstyps zusammensetzen</h4>
<p>Ich habe nun alle Hauptelemente des Inhaltstyps behandelt. Listing 13.1 zeigt den gesamten Code. Sie werden bemerken, dass der Rest des Programmcodes kompakter ist als der Python-Code im letzten Kapitel, da Archetypes sehr viel im Hintergrund für Sie erledigt.</p>
<p>Listing 13.1. <tt class="docutils literal">Article.py</tt></p>
<pre class="literal-block">
from Products.ArchExample.config import ARTICLE_GROUPS
from Products.Archetypes.public import BaseSchema, Schema
from Products.Archetypes.public import StringField, TextField
from Products.Archetypes.public import SelectionWidget, TextAreaWidget
from Products.Archetypes.public import RichWidget
from Products.Archetypes.public import BaseContent, registerType
from Products.Archetypes.Marshall import PrimaryFieldMarshaller
from Products.CMFCore import CMFCorePermissions
from config import PROJECTNAME

schema = BaseSchema +  Schema((
    StringField('group',
                vocabulary=ARTICLE_GROUPS,
                widget=SelectionWidget(),
                ),
    StringField('blurb',
                searchable=1,
                widget=TextAreaWidget(),
                ),
    TextField('body',
              searchable=1,
              required=1,
              primary=1,
              default_output_type='text/html',
              allowable_content_types=('text/plain',
                                       'text/structured',
                                       'text/restructured',
                                       'text/html',
                                       'application/msword'),
              widget=RichWidget(label='Body'),
              ),
           ),
     marshall=PrimaryFieldMarshaller(),
     )

class Article(BaseContent):
    &quot;&quot;&quot;This is a sample article; it has an overridden view for show,
    but this is purely optional
    &quot;&quot;&quot;

    schema = schema

    actions = ({
        'id': 'view',
        'name': 'View',
        'action': 'string:${object_url}/article_view',
        'permissions': (CMFCorePermissions.View,)
        },)

registerType(Article, PROJECTNAME)
</pre>
<p>Abgesehen von den <tt class="docutils literal">import</tt>-Anweisungen am Beginn und dem Schema, habe ich alle Elemente des Sourcecodes behandelt, mit der Ausnahme von  <tt class="docutils literal">registerType</tt>. Diese Funktion registriert die Klasse mit Ihrem Produkt im Archetypes-Framework. Jedes Produkt kann beliebig viele Inhaltstypen registrieren. So wird diese Funktion <tt class="docutils literal">registerType</tt> mit zwei Parametern aufgerufen: der Klasse und dem Namen des Produktes. Der zweite Parameter ist optional und wird nur in Ausnahmefällen benötigt, da die Funktion <tt class="docutils literal">registerType</tt> von der Klasse auf das Produkt schließen kann. In diesem Beispiel ist der Produktname in der Datei <tt class="docutils literal">config.py</tt> definiert; es ist allgemein üblich, Einstellungen an zentraler Stelle in einer <tt class="docutils literal">config.py</tt>-Datei innerhalb des Produkts vorzunehmen, wie zum Beispiel:</p>
<p>Listing 13.2. Die Konfigurationsdatei von ArchExample</p>
<pre class="literal-block">
from Products.CMFCore.CMFCorePermissions import AddPortalContent
from Products.Archetypes.public import DisplayList

ADD_CONTENT_PERMISSION = AddPortalContent
PROJECTNAME = &quot;ArchExample&quot;
SKINS_DIR = 'skins'

GLOBALS = globals()

ARTICLE_GROUPS = DisplayList((
    ('headline', 'Headline'),
    ('bulletin', 'Special Bulletin'),
    ('column', 'Weekly Column'),
    ('editorial', 'Editorial'),
    ('release', 'Press Release'),
    ))
</pre>
<p>Die Variable <tt class="docutils literal">ARTICLE_GROUPS</tt> ist eine Liste von Wertepaaren für das Vokabular des <em>group</em>-Feldes in <em>Article</em>, wobei jeweils der erste Wert bei Auswahl im Feld gespeichert wird und der zweite als zugehöriger Text in der Auswahlliste erscheint. Man kann allerdings auch eine einfache Liste von Werten als Vokabular angeben, wenn man nicht zwischen den Werten und deren Beschriftung unterscheiden muss. In Fall dieses Beispiels sieht der erzeugte HTML-Code für das <em>groups</em>-Feld folgendermaßen aus:</p>
<pre class="literal-block">
&lt;option value=&quot;headline&quot;&gt;Headline&lt;/option&gt;
&lt;option value=&quot;bulletin&quot;&gt;Special Bulletin&lt;/option&gt;
...
</pre>
<p>Ich möchte hier noch auf die Verwendung von  <tt class="docutils literal">globals()</tt> hinweisen. Dies ist eine eingebaute Python-Funktion, die alle Symbole aus dem globalen Namespace zurückgibt. Sie wird hier verwendet, um den Pfad zum <tt class="docutils literal">skins</tt>-Verzeichnis im Filesystem zu berechnen, so dass Sie ein File-System-Directory-View davon für das Skins-Tool erstellen können. Das Produktinitialisierungsmodul <tt class="docutils literal">__init__.py</tt> ist dadurch also wesentlich einfacher. Mit einem neuen Zusatz sehen die Funktionen  <tt class="docutils literal">process_types</tt> und <tt class="docutils literal">listTypes</tt> nun so aus:</p>
<pre class="literal-block">
from Products.Archetypes.public import process_types, listTypes
content_types, constructors, ftis = process_types(
    listTypes(PROJECTNAME),
    PROJECTNAME)
</pre>
<p>Die  <tt class="docutils literal">listTypes</tt>-Funktion aus dem Archetypes-Framework gibt alle Typdeklarationen zurück, die bisher im angegebenen Produkt registriert wurden. Diese Liste wird von <tt class="docutils literal">process_types</tt> noch weiter behandelt, die dann die Inhaltstypen, Konstruktoren und Factory-Type-Informationen (FTI) zurückgibt, die sodann beim CMF mit der Funktion <tt class="docutils literal">ContentInit</tt> registriert werden. Vieles ist ähnlich wie beim Schreiben von reinen Python-Produkten für das CMF, nur dass diese Funktionen das Ganze ein wenig vereinfachen.</p>
<p>Schließlich gibt es noch die <tt class="docutils literal">install</tt>-Funktion im Skript <tt class="docutils literal">Extensions/Install.py</tt>. Im Gegensatz zum <tt class="docutils literal">__init__.py</tt>-Modul, das aufgerufen wird, wenn das Produkt beim Hochstarten von Zope registriert wird, wird das Installationsskript aufgerufen, wenn ein Produkt in einer bestimmten Plone-Instanz zum Beispiel durch den QuickInstaller installiert wird. Dieses Skript vereinfacht sich durch Archetypes ebenfalls wesentlich, da die meiste Arbeit durch <tt class="docutils literal">installTypes</tt> und <tt class="docutils literal">install_subskin</tt> abgehandelt wird. Der Begriff  <em>Subskin</em> ist ein wenig irreführend - eigentlich ist damit ein Skin-Layer gemeint.</p>
<p>Listing 13.3. Installationsskript</p>
<pre class="literal-block">
from Products.Archetypes.public import listTypes
from Products.Archetypes.Extensions.utils import installTypes,
install_subskin
from Products.ArchExample.config import  PROJECTNAME, GLOBALS

from StringIO import StringIO

def install(self):
    out = StringIO()
    installTypes(self, out, listTypes(PROJECTNAME), PROJECTNAME)
    install_subskin(self, out, GLOBALS)
    out.write(&quot;Successfully installed %s.&quot; % PROJECTNAME)
    return out.getvalue()
</pre>
<p>Das vollständige Paket von ArchExample ist auf der Homepage des Autors des Plone-Buches unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a> zum Download verfügbar, Sie können es durch Auspacken in Ihrem Plone ausprobieren. Wie Sie gesehen haben, ist dieser Inhaltstyp schnell und einfach zu entwickeln und auch leicht zu ändern, ohne viel Code zu schreiben.</p>
</div>
</div>
<div class="section" id="id1">
<h3>Entwickeln mit Archetypes</h3>
<p>Dieser Abschnitt wird Sie in einige fortgeschrittene Programmiertechniken von Archetypes einführen, wie zum Beispiel Referenzen, Erstellen von eigenen Widgets und Transformationen.</p>
<p>Für diesen Abschnitt ist es wichtig zu sehen, wie die Änderungen, die Sie am Produkt vornehmen, in Plone wirksam werden. Wie Sie bisher gesehen haben, gibt es verschiedene Stadien in der Entwicklung des Produkts. Wenn Sie Ihr Produkt ändern, ist es nützlich, die notwendigen Schritte zu kennen.</p>
<p>Wenn Sie etwas in einer Skin ändern und Zope im Debug-Modus läuft, werden diese Änderungen sofort wirksam. Wenn Sie Dinge ändern, die im Installationsprozess involviert sind, wie etwa das <em>portal_actions</em>-Tool oder das <em>portal_types</em>-Tool, müssen Sie Plone neu starten und im QuickInstaller Ihr Produkt reinstallieren.</p>
<p>Wenn Sie das Schema ändern, reicht ein Neustart von Plone. Hingegen stellt sich die Frage, was dann mit bereits existierenden Objekten passiert. Archetypes bietet hier im <em>archetype-tool</em> ein  <em>Update Schema</em>-Werkzeug, das auf Wunsch über alle Archetypes-Objekte iteriert und sie mit dem neuen Schema abgleicht. Zu diesem Zweck klicken Sie im ZMI auf <em>archetype_tool</em> und wählen <em>Update Schema</em>. Nun können Sie die Klassen selektieren, deren Schema abgeglichen werden soll, und klicken sodann auf den <em>Update Schema</em>-Button; dies wird daraufhin alle Instanzen dieser Klassen mit dem neuen Schema abgleichen.</p>
<div class="section" id="verwendung-von-eindeutigen-schlusseln-unique-ids-uids">
<h4>Verwendung von eindeutigen Schlüsseln (Unique Ids, UIDs)</h4>
<p>Das Konzept von eindeutigen Schlüsseln ist einfach, aber hat in Zope bislang gefehlt. Ursprünglich glaubten Zope-Entwickler, dass der Pfad auf ein Objekt für dessen Adressierung ausreichend sei; unglücklicherweise hat sich das als unzureichend herausgestellt. Was passiert zum Beispiel, wenn ein Objekt verschoben wird? Alle Referenzen auf dieses Objekt würden nun ins Leere verweisen, da sich durch das Verschieben der Pfad ändert. Mit Hilfe der eindeutigen Schlüssel steht eine elegante Lösung des Problems zur Verfügung. Archetypes vergibt und speichert automatisch für jedes Objekt eine UID, die in einem speziellen Katalog namens  <tt class="docutils literal">uid_catalog</tt> indiziert wird.</p>
<p>Sie können diesen <em>uid_cataog</em> im ZMI ansehen. Dieser ist dem <em>portal_catalog</em> ziemlich ähnlich, nur dass er ausschließlich die für die Verwaltung und Suche von UIDs nötigen Indizes enthält. Es ist ziemlich einfach, ein Objekt aufgrund seiner UID über den <em>uid_catalog</em> zu erhalten. Sie müssen lediglich über den UID-Index nach der entsprechenden UID suchen; bei jedem Objekt können Sie dessen UID mit Hilfe der Funktion <tt class="docutils literal">UID()</tt> abfragen. Das nachfolgende Python-Script kann jedes Objekt aus dem Katalog suchen, vorausgesetzt, Sie kennen dessen <em>UID</em>.</p>
<pre class="literal-block">
##paramaters=objectId
results = context.uid_catalog(UID=objectId)
return results[0].getObject()
</pre>
<p>Wirklich nützlich sind die eindeutigen Schlüssel bei der Verwaltung von Referenzen zwischen Objekten. Nehmen wir an, Sie wollen von Ihrem Artikel aus verschiedene Bilder referenzieren, die sich in Ihrem Portal befinden. Wenn diese Bilder Archetypes-Ojekte sind, können Sie wie folgt ein <tt class="docutils literal">ReferenceField</tt> zu Ihrem Schema hinzufügen:</p>
<pre class="literal-block">
ReferenceField(&quot;images&quot;
    allowed_types=(&quot;Archetype Images&quot;,),
    multivalued=1,
    ),
</pre>
<p>Der User bekommt nun eine Dropdown-Liste mit den Titeln aller <em>Archetype Image</em>-Objekte und kann eines auswählen. Im Hintergrund werden beim Speichern Referenzen zu den Zielobjekten angelegt. Verantwortlich für die Referenzlogik in Archetypes ist die <em>ReferenceEngine</em>; der interessierte Leser möge hierfür den Code in <tt class="docutils literal">Archetypes/ReferenceEngine.py</tt> studieren. Im Archetypes-Subversion-Archiv unter <a class="reference external" href="http://svn.plone.org">http://svn.plone.org</a> gibt es das Produkt <em>ACME</em>, das viele der fortgeschrittenen Programmiertechniken wie die Referenzen demonstriert. Die Installation ist aber aufgrund von weiteren Produkten, die benötigt werden, nicht ganz einfach.</p>
</div>
<div class="section" id="anpassen-von-widgets">
<h4>Anpassen von Widgets</h4>
<p>Eine häufig gestellte Frage ist: &quot;Warum macht dieses Widget das eigentlich?&quot; oder: &quot;Warum sieht dieses Widget so aus?&quot; Oft lautet die Antwort: &quot;Weil es so geschrieben ist - Sie können es sich ja anpassen.&quot; Da die Widgets das sind, was der Kunde sieht, ändern sich die Anforderungen oft, so dass dem Entwickler hier die Flexibilität von Archetypes zugute kommt.</p>
<p>Alle Widgets bestehen einerseits aus einer Python-Klasse - die vorgegebenen Widgets kann man in <tt class="docutils literal">Archetypes/Widget.py</tt> sehen - und die dazugehörigen Page Templates in <tt class="docutils literal">Archetypes/skins/archetypes/widgets</tt>. Wenn Sie im ZMI auf <em>portal_skins</em> klicken und dort <em>archetypes/widgets</em> wählen, sehen Sie alle mit Archetypes gelieferten Widgets. Unglücklicherweise hat das <em>portal_skins</em>-Tool von CMF einen kleinen Bug. Weil die Widgets in einem Unterverzeichnis sind, kann man leider nicht einfach eines der Templates ändern. Das ist aber nicht so schlimm, Sie können in Ihrem Produkt einen eigenen Skin-Layer definieren und dann in der Widget-Deklaration des Schemas spezifizieren, dass das Widget mit einem anderen Makro dargestellt werden soll. Jedes Widget-Objekt hat ein  <tt class="docutils literal">macro</tt>-Attribut, das definiert, welches Makro das Widget darstellen soll, und das als Pfad-Ausdruck zum Widget <em>Page Template</em> spezifiziert wird.</p>
<p>Nun ist der Begriff <em>macro</em> irreführend, denn das Attribut verweist auf ein Page Template, das seinerseits drei Makros enthält:  <tt class="docutils literal">view</tt>, <tt class="docutils literal">edit</tt> und <tt class="docutils literal">search</tt>. Diese Makros sind für das Anzeigen Ihres Widgets in den verschiedenen Situationen zuständig.</p>
<p>Das <tt class="docutils literal">view</tt>-Makro ist eine benutzerfreundliche Ansicht mit rein lesendem Zugriff und wird zum Beispiel in der <tt class="docutils literal">base_view</tt>-Ansicht pro Feld angezeigt. Das <tt class="docutils literal">edit</tt>-Makro wird in der Bearbeitungsansicht angezeigt und ist das Makro, mit dem der Benutzer die Daten für das einzelne Feld eingibt. Es kommt zum Beispiel in der <tt class="docutils literal">base_edit</tt>-Ansicht zum Einsatz. Das <tt class="docutils literal">search</tt>-Makro wird verwendet, wenn Sie eine Suchmaske für Ihren Inhaltstyp zusammenstellen wollen; es sieht häufig wie das <tt class="docutils literal">edit</tt>-Makro aus, muss aber nicht unbedingt so aussehen . Ein Stringfeld kann zum Beispiel in einem <tt class="docutils literal">RichWidget</tt> eingegeben werden, aber der Suchbegriff dazu könnte mit einem <tt class="docutils literal">StringWidget</tt> eingegeben werden.</p>
<p>Nun, für unser Beispielprodukt wollen wir ein <tt class="docutils literal">StringField</tt> für die E-Mail-Adresse einer Person verwenden, die in der <tt class="docutils literal">view</tt>-Ansicht als klickbarer E-Mail-Link dargestellt wird. Dafür benötigen wir ein neues <tt class="docutils literal">view</tt>-Makro; die Makros für <tt class="docutils literal">edit</tt> und <tt class="docutils literal">search</tt> können wir vom <tt class="docutils literal">StringWidget</tt> nehmen. Dazu erstellen Sie ein neues Page Template namens <tt class="docutils literal">email_widget.pt</tt> in der Skin Ihres Produkts. Für <tt class="docutils literal">edit</tt> und <tt class="docutils literal">search</tt> werden einfach die entsprechenden Makros des <tt class="docutils literal">StringWidgets</tt> aufgerufen:</p>
<pre class="literal-block">
&lt;html xmlns:tal=&quot;http://xml.zope.org/namespaces/tal&quot;
      xmlns:metal=&quot;http://xml.zope.org/namespaces/metal&quot;
      i18n:domain=&quot;plone&quot;&gt;

  &lt;body&gt;
    &lt;div metal:define-macro=&quot;edit&quot;&gt;
      &lt;div metal:use-macro=&quot;here/widgets/string/macros/edit&quot; /&gt;
    &lt;/div&gt;

    &lt;div metal:define-macro=&quot;search&quot;&gt;
      &lt;div metal:use-macro=&quot;here/widgets/string/macros/search&quot; /&gt;
    &lt;/div&gt;
</pre>
<p>Für <tt class="docutils literal">view</tt> erzeugen Sie einen <tt class="docutils literal">mailto:</tt>-Link, den Sie durch folgende kleine Änderung erreichen:</p>
<pre class="literal-block">
    &lt;div class=&quot;field&quot; metal:define-macro=&quot;view&quot;&gt;
        &lt;a href=&quot;#&quot; tal:attributes=&quot;href string:mailto:${accessor}&quot;
          tal:content=&quot;accessor&quot;&gt;E-Mail&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Nach dem Definieren des neuen Page Templates mit Ihrem eigenen Code können Sie einfach dessen Namen in der Widget-Deklaration als <tt class="docutils literal">macro</tt>-Parameter angeben. Im folgenden Codebeispiel definieren Sie das E-Mail-Feld als <tt class="docutils literal">StringField</tt> mit zugehörigem <tt class="docutils literal">StringWidget</tt> wie üblich, nur dass Sie noch das zu verwendende Makro explizit angeben:</p>
<pre class="literal-block">
StringField('E-Mail',
    validators = ('isEmail',),
    widget = StringWidget(
        label='E-Mail',
        macro='email_template'
    )
)
</pre>
<p>Bis jetzt haben Sie lediglich das Makro eines bereits vorhandenen Widgets geändert. Ein ganz neues Widget zu erstellen erfordert das Schreiben einer eigenen Widget-Klasse und deren Registrierung. Alle Widgets haben eine gemeinsame Basisklasse. Im folgenden Beispiel schreiben wir ein eigenes Modul namens <tt class="docutils literal">EmailWidget.py</tt> und speichern es in das <tt class="docutils literal">ArchExample</tt>-Verzeichnis. Es enthält die neue Widget-Klasse und deren Registrierung. Beachten Sie, dass die <tt class="docutils literal">macro</tt>-Property des Widgets auf das <tt class="docutils literal">email_template</tt> voreingestellt ist:</p>
<pre class="literal-block">
from Products.Archetypes.Widget import TypesWidget
from Products.Archetypes.Registry import registerWidget

class EmailWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
       'macro' : &quot;email_template&quot;,
       'size' : '30',
       'maxlength' : '255',
       })

registerWidget(EmailWidget,
    title='String',
    description='Renders a clickable E-Mail field',
    used_for=('Products.Archetypes.Field.StringField',)
)
</pre>
<p>Um das neue Widget in Ihrer Article-Klasse zu verwenden, importieren Sie <tt class="docutils literal">EmailWidget</tt> und geben es wie folgt im <tt class="docutils literal">widget</tt>-Parameter zum Feld an:</p>
<pre class="literal-block">
from EmailWidget import EmailWidget

StringField('E-Mail',
    validators = ('isEmail',),
    widget = EmailWidget(
        label='E-Mail',

    )
)
</pre>
</div>
<div class="section" id="das-entwickeln-von-ordnerartigen-objekten-folderish-objects">
<h4>Das Entwickeln von ordnerartigen Objekten (Folderish Objects)</h4>
<p>Sie haben vermutlich schon oft mit ordnerartigen Objekten in Plone gearbeitet, ohne sich dessen bewusst zu sein. Ein ordnerartiges Objekt ist eines, das ähnliche Eigenschaften wie ein Ordner aufweist, nämlich dass es seinerseits Unterobjekte enthalten kann. Das Erstellen eines ordnerartigen Objekts ist nichts Besonderes - die Klasse muss lediglich von einer besonderen Basisklasse erben, um die entsprechenden Fähigkeiten zur Verfügung zu stellen, und schon kann man zu unserem Objekt neue Unterobjekte hinzufügen.</p>
<p>Ordnerartige Objekte sind aus verschiedenen Gründen nützlich. Sie stellen einen einfachen Weg dar, um Sammlungen von einzelnen Objekten zu erzeugen. Sie erlauben außerdem, dass Plone-Benutzer ihre speziellen Inhaltstypen über die gewohnte Plone-Oberfläche verwalten können, ohne dass Sie dafür speziell programmieren müssen. Generell ist es am besten, den Ordner einfach zu halten, und die Logik sollte in Ihren Objekten und in deren Workflow enthalten sein. Natürlich gibt es Ausnahmen dazu, und ein klassisches Beispiel ist die Familie der Bugtracker,  <em>CMFCollector</em> und  <em>PloneCollectorNG</em>, die beide komplexe ordnerartige Objekte sind, die einiges an Logik für die einzelnen Einträge bieten.</p>
<p>Der einfachste Weg, um ordnerartige Objekte zu erstellen, ist wiederum die Verwendung von Archetypes. Sie haben in den vorigen Beispielen gesehen, dass <tt class="docutils literal">BaseContent</tt> alle nötigen Standardarbeiten für &quot;normale&quot; Objekte erledigt. Nun gibt es auch eine Klasse  <tt class="docutils literal">BaseFolder</tt>, die die entsprechenden Aufgaben für ordnerartige Objekte übernimmt. Außerdem besitzen Ordner ein eigenes Basisschema <tt class="docutils literal">BaseFolderSchema</tt>. Auf den Punkt gebracht: Um einen ordnerartigen Inhaltstyp zu erstellen, ändern Sie die Basisklasse auf <tt class="docutils literal">BaseFolder</tt> und fügen sie zum Schema <tt class="docutils literal">BaseFolderSchema</tt> hinzu. Ein einfaches Beispiel für einen ordnerartigen Inhaltstyp sehen Sie hier:</p>
<pre class="literal-block">
from Products.Archetypes.public import BaseFolder, BaseFolderSchema

schema = BaseFolderSchema

class SimpleFolder(BaseFolder):
    &quot;&quot;&quot;A simple folderish archetype&quot;&quot;&quot;
    schema = schema

registerType(SimpleFolder)
</pre>
<p>Wenn Sie beabsichtigen, eine große Menge von Unterobjekten in einem Ordner zu speichern, dann ist die Klasse <tt class="docutils literal">BaseFolder</tt> weniger geeignet, da diese Klasse von der Zope-Basisklasse <tt class="docutils literal">Folder</tt> erbt, die nicht auf große Datenmengen ausgelegt ist. Für große Mengen von Unterobjekten empfiehlt sich die Verwendung von Binary-Tree-Ordnern, die die Unterobjekte intern in einem Binärbaum effizienter speichern statt in einem Python-Dictionary, wie dies bei <tt class="docutils literal">Folder</tt> der Fall ist. In diesem Fall lassen Sie Ihre Klasse von  <tt class="docutils literal">BaseBTreeFolder</tt> erben und nehmen als Basisschema  <tt class="docutils literal">BaseBTreeFolderSchema</tt>. Was die Produktentwicklung betrifft, sind die beiden gleich zu programmieren, nur dass auch bei einer Anzahl von mehr als 100.000 Objekten der  <tt class="docutils literal">BtreeFolder</tt> gut skaliert.</p>
</div>
<div class="section" id="arbeiten-mit-microsoft-office-dateien">
<h4>Arbeiten mit Microsoft Office-Dateien</h4>
<p>Der Umgang mit Microsoft-Office-Dateien wie zum Beispiel Word oder Excel ist ein Fluch, mit dem jedes Content-Management-System irgendwann einmal konfrontiert wird. Aber dies ist der Fall mit mit verschiedenen Dateiformaten - Microsoft Office, OpenOffice.org, Portable Document Format (PDF), Imagedateien usw. Der Umgang mit diesen Dateiformaten auf Webseiten verursacht normalerweise einige Probleme; dies ist natürlich wohlbekannt. Das Editieren ist umständlich, da durch das Anklicken das Dokument heruntergeladen wird oder - noch schlimmer - in Ihrem Webbrowser geöffnet wird. Wenn Sie es fertig editiert haben, müssen Sie das Dokument abermals auf die Website hochladen, was wiederum eine Runde des Herumklickens bedeutet. Wenn das Dokument endlich online ist, ist dieses meistens nicht suchbar, weil Binärdateien nicht indiziert werden. Außerdem können Sie das Dokument nicht online betrachten, weil der Webbrowser das jeweilige Datenformat ohne spezielle Plugins nicht darstellen kann.</p>
<p>Es gibt einige Lösungen, um das Problem des Editierens von Inhalt zu lösen. Wenn Sie weiter annehmen, dass die meisten Ihrer Anwender Windows verwenden, dann kann die Verwendung von WebDAV mit Komplikationen verbunden sein, da Microsofts Implementation des WebDAV-Clients (Web Folders) von fraglicher Qualität ist. Hingegen erfüllt der External Editor diese Funktion recht gut. Wenn Plone dem External Editor mitteilen kann, dass es sich bei dem editierten Objekt zum Beispiel um eine Word-Datei handelt, wird der External Editor das Dokument mit Word öffnen.</p>
<p>Und für den Rest enthält Archetypes ein Transformationspaket namens <em>Portal Transforms</em>, das die Transformation von Inhaltstypen beherrscht. Dies kann ein Dokument in einem bestimmten Datenformat in HTML transformieren, wonach es katalogisiert werden kann, und als HTML kann es auch im Webbrowser dargestellt werden. Dies funktioniert durch die Verwendung eines externen Transformationsprozesses, um die Daten umzuwandeln und das Ergebnis auszulesen und darzustellen. Wenn Sie zum Beispiel Plone unter Windows (auf dem Server) verwenden, dann nimmt Portal Transforms ein auf dem Server installiertes Microsoft Office als COM-Server, um die Word-Datei in HTML umzuwandeln.</p>
<p>All dies passiert im Hintergrund. Sie müssen lediglich sicherstellen, dass die für die Transformationen benötigten Komponenten installiert sind und funktionieren. Es gibt verschiedene Typen von Transformationen, und oft gibt es mehr als eine Möglichkeit, Daten zu transformieren.</p>
<p>Das OpenOffice-Paket stellt hervorragende Transformationen von Microsoft Office-Daten zur Verfügung (oft sogar besser als MS Office selbst!) und bietet somit auch auf Nicht-Microsoft-Plattformen die Möglichkeit, Office-Daten zu verarbeiten. Ich habe auch schon  <em>wvWare</em> (<a class="reference external" href="http://wvware.sourceforge.net/">http://wvware.sourceforge.net/</a>) verwendet, ein Kommandozeilen-Werkzeug, das es ermöglicht, verschiedene Microsoft-Formate in HTML zu übersetzen. All diese Optionen stehen Ihnen zur Verfügung; oft sind die Komponenten aber nicht einfach zu installieren. Nach dem Aufsetzen der Komponenten berichten Benutzer, dass die besten Ergebnisse mit Microsoft Office und Openoffice.org als Backend der Transformationen erzielt werden.</p>
<p>Ich empfehle, zuerst genau zu überlegen, was transformiert werden soll, und dann einen Blick in den Quellcode von PortalTransforms zu werfen, um zu sehen, ob Sie eine geeignete Transformation für Ihre Zwecke finden.</p>
<div class="section" id="einrichten-eines-inhaltstyps">
<h5>Einrichten eines Inhaltstyps</h5>
<p>Sie werden nun einen einfachen Inhaltstyp erstellen, der Microsoft Office Word-Dokumente verarbeiten kann. Word ist sicher der Dokumenttyp, den Sie am häufigsten verarbeiten werden. In diesem Beispiel habe ich die Transformation unter Windows durchgeführt. Wenn Sie dies hingegen auf einem alternativen System wie zum Beispiel Linux aufsetzen, ändert sich am Code Ihres Inhaltstyps nichts, es kommt lediglich eine andere Transformation vom Dokumenttyp <tt class="docutils literal">application/msword</tt> nach HTML zum Tragen. Welche Transformationen in einem Portal zur Verfügung stehen, wird im Werkzeug <em>portal_transforms</em> hinterlegt. In diesem Beispiel ist es am einfachsten, Ihre Plone-Instanz unter Windows aufzusetzen, wo Microsoft Office schon installiert ist. Der Plone Installer richtet eine Plone-Instanz mit allen notwendigen Win32-API-Modulen ein. Dann erstellen Sie einen Inhaltstyp für dieses Beispiel mit Namen  <tt class="docutils literal">WordExample</tt>. Im Wesentlichen ist ein Feld in diesem Schema für die Speicherung von Word-Dokumenten zuständig, so dass sich folgendes einfaches Schema ergibt:</p>
<pre class="literal-block">
schema = BaseSchema +  Schema((
    TextField('body',
              searchable=1,
              required=1,
              primary=1,
              default_output_type='text/html',
              allowable_content_types=('application/msword',),
              widget=RichWidget(label='Body'),
              ),
    ),
    marshall=PrimaryFieldMarshaller(),
    )
</pre>
<p>Der einzige Unterschied zu einem normalen TextField ist die Angabe des Multipurpose Internet Mail Extensions-(MIME-)Typ für Microsoft Word im <tt class="docutils literal">allowable_content_types</tt>-Attribut. Für jeden Dokumenttyp, den dieses Feld zur Transformation akzeptieren soll, geben Sie den entsprechenden MIME-Typ im <tt class="docutils literal">allowable_content_types</tt>-Attribut an. Wenn Sie beispielsweise Microsoft Office- und PDF-Dateien verarbeiten wollen, geben Sie folgende Zeile an:</p>
<pre class="literal-block">
allowable_content_types=('application/msword','application/pdf'),
</pre>
<p>Dieser Inhaltstyp ist das einfachstmögliche Beispiel in diesem Moment. Aber Sie können diesen Inhaltstyp erweitern wie jeden anderen, zum Beispiel um ein Beschreibungsfeld und andere Attribute. Eine sinnvolle, aber arbeitsintensive Erweiterung wäre zum Beispiel, die Metadaten des Word-Dokuments in die Metadaten des Plone-Objekts zu speichern.</p>
<p>Wenn Sie bereits Archetypes 1.3 verwenden, empfiehlt sich hier die Installation der <em>ATContentTypes</em> (<a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>), die die klassischen Inhaltstypen von Plone (Document, File,...) durch kompatible Archetypes-basierende Klassen ersetzen und spätestens ab Plone 2.1 standardmäßig mit Plone installiert werden. Der <em>Document</em>-Inhaltstyp verwendet hier bereits <tt class="docutils literal">PortalTransforms</tt>, um alle möglichen Transformationen durchzuführen, so dass Sie hier keinen eigenen Inhaltstyp mehr schreiben müssen, um in den Genuss des Transformations-Frameworks von Archetypes zu kommen.</p>
</div>
<div class="section" id="transformationen-unter-windows-einrichten">
<h5>Transformationen unter Windows einrichten</h5>
<p>Die Transformation sollte automatisch durch den Installationsprozess im Tool <em>portal_transforms</em> registriert sein. Hier gibt es nicht viel Arbeit an dem Tool; es liefert im Wesentlichen eine Liste von verfügbaren Transformationen. Eine Transformation hat eine Liste von eingehenden MIME-Typen (wie zum Beispiel <tt class="docutils literal">application/msword</tt>), die sie transformieren kann, und einen ausgehenden MIME-Typ, der produziert wird (so wie in den meisten Fällen <tt class="docutils literal">text/html</tt>). Jede Transformation ist ein Python-Modul im Dateisystem; in diesem Fall finden Sie den Code in <tt class="docutils literal">PortalTransforms/transforms/word_to_html</tt>.</p>
<p>Um diese Transformation unter Windows zum Laufen zu bringen, müssen Sie ein Kommandozeilen-Tool aufrufen, das die COM-Bindungen für Python generiert. Um dies zu tun, starten Sie  <em>PythonWin</em> aus der <em>Plone</em>-Gruppe in Ihrem Startmenü und selektieren <em>Tools - COM Makepy utility</em>. Dieses listet alle verfügbaren COM-Interfaces auf; wenn Sie Office installiert haben, werden Sie hier einen Eintrag für Microsoft Office in der Liste finden. Auf meinem Windows-Server lautete dieser Eintrag  <em>Microsoft Word 9.0 Object Library (8.1)</em>. Wählen Sie diesen aus, und klicken Sie auf <em>OK</em>. PythonWin wird nun die entsprechende Information generieren. Wenn Sie dies durchgeführt haben, bekommen Sie die Meldung 'Generating to ...'.</p>
<p>Sie können nun PythonWin schließen und Plone neu starten.</p>
</div>
<div class="section" id="testen-des-inhaltstyps">
<h5>Testen des Inhaltstyps</h5>
<p>Um den Inhaltstyp zu testen, starten Sie Plone neu, stellen sicher, dass Ihr Produkt wie üblich registriert ist, und installieren es in Ihrem Portal mit <em>Produkte hinzufügen/löschen</em>. Nun legen Sie eine Instanz Ihres neuen Inhaltstyps an.</p>
<p>Nun selektieren Sie eine Word-Datei aus Ihrem Dateisystem und klicken <em>Save</em>, um sie zu Plone hochzuladen. Anschließend wird diese Datei hochgeladen, und Sie können das Resultat sehen. Verlieren Sie hier nicht die Geduld, denn dieser Vorgang kann länger dauern, denn zusätzlich zum normalen Upload der Daten muss Word als externes Programm aufgerufen werden, um die HTML-Datei mit eventuell eingebetteten Bildern zu erzeugen. Wenn alles richtig arbeitet, sollten Sie Ihr Word-Dokument als HTML etwa wie in Abbildung 13.4 sehen.</p>
<a class="reference external image-reference" href="img/13-04.png/image_view_fullscreen"><img alt="img/13-04.png" class="original" src="img/13-04.png" /></a>
<p>Abbildung 13.4. Die hochgeladene Datei</p>
<p>Nun da Sie das Dokument im Plone haben, wollen Sie es auch editieren können. Hierzu empfiehlt sich der Einsatz von External Editor. Wenn Sie External Editor auf Ihrem Arbeitsplatz installiert haben, klicken Sie auf das Bleistiftsymbol in der oberen rechten Ecke, und die Datei wird in Word geöffnet, wie in Abbildung 13.5 zu sehen ist. Sie können nun das Dokument ändern und editieren; jedes Mal, wenn Sie die Datei speichern, wird diese im Hintergrund zu Plone hochgeladen und wiederum zu HTML transformiert.</p>
<a class="reference external image-reference" href="img/13-05.png/image_view_fullscreen"><img alt="img/13-05.png" class="original" src="img/13-05.png" /></a>
<p>Abbildung 13.5. Das Dokument in Word geöffnet</p>
</div>
<div class="section" id="alternative-transformationen-von-office-dokumenten">
<h5>Alternative Transformationen von Office-Dokumenten</h5>
<p>Die Verwendung von Microsoft Office setzt nicht nur Windows auf dem Plone Server voraus, sondern auch ein dort installiertes Microsoft Office nebst dazu gültigen Lizenzen.</p>
<p>Die Installation von wvWare unter Linux gestaltet sich recht einfach, so dass Sie hier relativ schnell zu einer Word-Transformation kommen, da die Verfügbarkeit von wvWare von PortalTransforms automatisch erkannt wird. Dazu besorgen Sie sich das Paket von <a class="reference external" href="http://wvware.sourceforge.net">http://wvware.sourceforge.net</a> und folgen den (etwas veralteten) Installationsanleitungen. Für Debian-Anwender gibt es das vorkonfigurierte Paket <em>wv</em>, das im Handumdrehen ohne manuellen Eingriff installiert ist. wvWare steht dadurch als Kommandozeilenbefehl zur Verfügung und wird von PortalTransforms automatisch erkannt. Allerdings ist die Qualität des Resultats nicht mit der Microsoft Office- oder OpenOffice.org-Transformation vergleichbar.</p>
<p><em>OpenOffice.org</em> bietet vergleichbar mit Microsofts COM eine applikationsübergreifende Programmierschnittstelle namens UNO, zu der es auch ein Python-Binding mit Namen PyUNO gibt. Um OpenOffice innerhalb von Plone als Transformation-Backend verfügbar zu machen, muss die PyUNO-Schnittstelle in der Python-Installation, die von Plone verwendet wird, installiert werden, was jedoch einige manuelle Schritte erfordert.</p>
<p>Derzeit entsteht ein weiteres Produkt namens <em>BlueDCS</em> im Entstehen, das vielleicht bei Drucklegung dieses Buches als Betaversion verfügbar sein wird. Es teilt die OfficeTransformation auf zwei Serverprozesse auf: in einen von Plone unabhängigen XMLRPC-Server, der sich um die Kommunikation mit OpenOffice kümmert, und in ein sehr einfach zu installierendes Plone-Produkt, das die Transformationsanforderung an jenen Server delegiert. Dies bringt den Vorteil, dass die Arbeit des Einrichtens von OpenOffice an zentraler Stelle nur einmal durchgeführt werden muss und etwaige Inkompatibilitäten zwischen der von Plone verwendeten Python-Version und PyUNO ausgeschlossen sind, da der XMLRPC-Server einfach und ohne großen Installationsaufwand mit der von OpenOffice mitinstallierten Python-Distribution gestartet wird. Der Installationsaufwand innerhalb von Plone beschränkt sich auf das Aktivieren des Produktes und auf die Angabe der Adresse des <em>BueDCS</em>-Servers.</p>
<div class="admonition-tipp admonition">
<p class="first admonition-title">Tipp</p>
<p class="last">Sie können Portal Transforms vom Archetypes CVS unter <a class="reference external" href="http://sf.net/projects/archetypes">http://sf.net/projects/archetypes</a> herunter laden. Sie werden im Quellcode von Portal Transforms Dokumentation vorfinden und es gibt auch eine Online-Version unter <a class="reference external" href="http://www.logilab.org/projects/portaltransforms/documentation">http://www.logilab.org/projects/portaltransforms/documentation</a> .</p>
</div>
</div>
</div>
<div class="section" id="fortgeschrittenes-entwickeln-erstellen-von-inhaltstypen-mit-uml">
<h4>Fortgeschrittenes Entwickeln: Erstellen von Inhaltstypen mit UML</h4>
<p>Nun haben Sie ein Produkt mit mehreren komplizierten Inhaltstypen und finden die Idee, diese manuell zu schreiben, langweilig? Nun, keine Angst, denn ArchGenXML ist hier! Dies ist in hilfreiches Produkt, das es Ihnen erlaubt, Ihre Plone-Applikation mit UML in einem UML-Modellierungstool Ihrer Wahl zu modellieren und aus diesem Modell ein lauffähiges Plone-Produkt zu generieren.</p>
<p>Philipp Auersperg hat das ArchGenXML-Projekt begonnen, und Jens Klein hat vieles an Code und Dokumentation dazu beigetragen. Ich werde in diesem Abschnitt einige der Fähigkeiten von ArchGenXML, das zur Drucklegung dieses Buches in der Version 1.2 vorliegt, vorstellen.</p>
<p>Um mit ArchGenXML zusammenarbeiten zu können, muss ein UML-Tool den Export des Modells als  <em>XMI</em> (XML-Anwendung zum Austausch von Modell-Metadaten) unterstützen. ArchGenXML wurde erfolgreich mit den folgenden Produkten getestet:</p>
<ul class="simple">
<li>ObjectDomain (kommerziell, die Demoversion erlaubt das Editieren von bis zu 30 Klassen), erhältlich unter <a class="reference external" href="http://www.objectdomain.com">http://www.objectdomain.com</a></li>
<li>ArgoUML (frei), erhältlich unter <a class="reference external" href="http://argouml.tigris.org">http://argouml.tigris.org</a></li>
<li>Poseidon (kommerziell, basierend auf ArgoUML, kostenlose Community-Edition verfügbar), erhältlich unter <a class="reference external" href="http://www.gentleware.com">http://www.gentleware.com</a></li>
<li>Sybase Powerdesigner (kommerziell, zeitlich limitierte Demoversion verfügbar), erhältlich unter <a class="reference external" href="http://www.sybase.com">http://www.sybase.com</a></li>
</ul>
<p>Der Export von XMI-Modelldateien wird von den verschiedenen UML Tools leicht unterschiedlich gehandhabt (zum Beispiel exportiert ObjectDomain keine Statemachines). Aber wenn Sie einen Blick auf die ArchGenXML-Beispiele werfen, sehen Sie einige Beispielmodelle, die die Funktionalität von ArchGenXML demonstrieren. Abbildung 13.6 zeigt ein einfaches Modell mit einigen Klassen und Beziehungen.</p>
<a class="reference external image-reference" href="img/13-06.png/image_view_fullscreen"><img alt="img/13-06.png" class="original" src="img/13-06.png" /></a>
<p>Abbildung 13.6. Ein komplexes Beispiel eines Datenmodells</p>
<a class="reference external image-reference" href="img/13-07.png/image_view_fullscreen"><img alt="img/13-07.png" class="original" src="img/13-07.png" /></a>
<p>Abbildung 13.7. Zustandsdiagramm für die Klasse <tt class="docutils literal">Project</tt></p>
<p>Der einfachste Weg, um daraus ein lauffähiges Plone-Produkt zu generieren, besteht darin, in das Verzeichnis <tt class="docutils literal">samples/PloneBook</tt> in Ihrem ArchGenXML-Verzeichnis zu wechseln und den unten stehenden Befehl aufzurufen. Damit wird aus dem ArgoUML-Modell ein lauffähiges Plone-Produkt namens <em>Project</em> erzeugt.</p>
<pre class="literal-block">
python ../../ArchGenXML.py product.zargo Product
</pre>
<p>Sie müssen sodann das Produkt in Ihren <tt class="docutils literal">Products</tt>-Ordner Ihrer Plone-Installation kopieren und Zope neu starten. Wenn Sie einen Blick in das eben generierte Produkt werfen, werden Sie sehen, dass alles Notwendige für Sie generiert wurde: die Klassendateien, die Modulinitialisationsdatei <tt class="docutils literal">__init__.py</tt> und die Skripten zum Installieren des Produktes in Ihre Plone-Instanz im <tt class="docutils literal">Extensions</tt>-Verzeichnis. Im neu gestarteten Plone müssen Sie nun lediglich <em>Plone konfigurieren</em> wählen und das neu verfügbare Produkt installieren, und schon können Sie mit den neuen Inhaltstypen experimentieren. Sie werden die Attribute, die im Datenmodell für die Klassen vergeben wurden, im Edit-Schirm der Inhaltstypen wiedererkennen.</p>
<a class="reference external image-reference" href="img/13-08.png/image_view_fullscreen"><img alt="img/13-08.png" class="original" src="img/13-08.png" /></a>
<p>Abbildung 13.8. Das frisch generierte Produkt im Einsatz</p>
<p>Was passiert mit den manuellen Änderungen, die Sie an Ihrem Produkt-Quellcode vornehmen, wenn Sie mit ArchGenXML noch einmal das Modell über Ihr modifiziertes Produkt generieren? Nun, ArchGenXML stellt zwar kein Roundtripping zur Verfügung, aber es behält bestimmte Änderungen bei, die Sie im Quellcode vornehmen. So werden Methoden, die Sie in der Klassendefinition modifiziert oder hinzugefügt haben, beibehalten; weiters werden Sie im Quellcode geschützte Codebereiche finden (mit <tt class="docutils literal"><span class="pre">#code-section</span></tt> markiert), die beim Generieren beibehalten werden. Ebenso werden sämtliche Page Templates beibehalten, die innerhalb des <tt class="docutils literal">skins</tt>-Ordners generiert wurden.</p>
<p>Welche UML-Konzepte werden von ArchGenXML unterstützt? ArchGenXML stellt Ihnen eine Reihe von Konzepten aus der UML-Spezifikation für die Plone-Entwicklung zur Verfügung, ich möchte sie hier kurz anhand des oben erwähnten Beispiels anführen:</p>
<ul class="simple">
<li><strong>Klassen</strong>: ArchGenXML generiert aus jeder Klasse im Modell den Code für genau einen Inhaltstyp. Klassen, die als Abstrakt markiert wurden, werden als nicht instanziierbare Inhaltstypen generiert (z.B. <em>Resource</em>).</li>
<li><strong>Methoden</strong>: Jede Methode im Modell entspricht einer Methode innerhalb der Klassendefinition des Inhaltstyps. Methoden, die den Stereotyp <em>view</em> oder <em>form</em> besitzen, werden hingegen als Page Template mit einer dazugehörigen Action generiert (z.B <em>project_overview</em> in der Klasse <tt class="docutils literal">Project</tt>). Methoden, die als Stereotyp <em>action</em> besitzen, werden nur als Actions generiert.</li>
<li><strong>Attribute</strong> werden als Feld in der Schemadefinition generiert.</li>
<li><strong>Interfaces</strong> werden als Python-Interface-Deklarationen generiert. Klassen, die eine Realisationsbeziehung zu einem Interface besitzen, bekommen das in der Zope-Entwicklung übliche <tt class="docutils literal">__implements__</tt>-Statement verpasst.</li>
<li><strong>Vererbung</strong>: Generalisierungsbeziehungen zwischen Klassen werden auch in Python als Vererbungsbeziehung generiert; Mehrfachvererbung wird unterstützt. Beispiel: <tt class="docutils literal">Resource</tt> und <tt class="docutils literal">Person</tt> bzw. <tt class="docutils literal">Room</tt>.</li>
<li><strong>Aggregation und Composite</strong>: Diese beiden Beziehungen drücken sich in ordnerartigen Objekten aus. Die Klasse, die andere enthalten kann, wird als ordnerartiges Objekt generiert, das den Clientteil der Beziehung enthalten kann (mit Hilfe von <em>allowed_content_types</em>). Beispiel: <em>Projekt-Resource</em> für eine Aggregation oder <em>Projekt-Task</em> für ein Composite.</li>
<li><strong>Assoziationen</strong> werden als <em>ReferenceFields</em> generiert, wobei hier zwischen den Kardinalitäten <em>0..1</em> und <em>0..n</em> unterschieden wird. Beispiel: <em>Projekt-Person</em> oder <em>Task-Resource</em>.</li>
<li><strong>Zustandsmodelle (Statemachines)</strong> werden als Workflow-Definition generiert, die automatisch beim Installieren des Produkts im Workflow-Tool eingerichtet und der entsprechenden Klasse zugeordnet wird. Beispiel: Das Zustandsdiagramm der Klasse <tt class="docutils literal">Project</tt></li>
</ul>
<p>Das ist alles über das Rapid Development, was im Rahmen dieses Buches angeführt werden kann. Es gibt noch eine Reihe von Optionen in ArchGenXML. Viele Feineinstellungen können über Stereotypen und so genannte <em>Tagged Values</em> sowie Kommandozeilenparameter eingestellt werden. Eine umfangreiche und ständig wachsende Online-Dokumentation wurde von Jens Klein initiiert und ist auf plone.org unter <a class="reference external" href="http://plone.org/documentation/tutorial/archgenxml-getting-started">http://plone.org/documentation/tutorial/archgenxml-getting-started</a> zu finden.</p>
</div>
<div class="section" id="daten-in-einer-sql-datenbank-speichern">
<h4>Daten in einer SQL-Datenbank speichern</h4>
<p>Für fast alles, was in diesem Buch gezeigt wurde, sind die Daten in der Zope Object Database (ZODB) gespeichert worden. Ich habe gezeigt, wie Daten im Dateisystem gespeichert werden, aber eine häufig gestellt Frage ist, wie Daten in einer relationalen Datenbank gespeichert werden können. Eine Speicherung in einer relationalen Datenbank lohnt sich vor allem dann, wenn folgende Tatsachen zutreffen:</p>
<ul class="simple">
<li>Ihre Datenstrukturen sind starr, und das Schema ändert sich selten (obwohl Archetypes diesen Aspekt abschwächt).</li>
<li>Sie haben andere Applikationen, die Zugriff auf die relationale Datenbank haben oder benötigen.</li>
<li>Sie haben Tools und andere Anforderungen, die bereits von einer relationalen Datenbank erfüllt werden.</li>
<li>Sie haben eine große Menge an Daten, die oft verändert werden.</li>
</ul>
<p>In einer eher traditionellen CGI-Umgebung werden Sie wahrscheinlich einige SQL-Statements schreiben, um die Daten aus einer relationalen Datenbank zu beziehen. Ähnliches erreichen Sie in Zope durch die Verwendung von ZSQL-Methoden. Diese sind in vielen Situationen nützlich und werden sehr ausführlich im Online-Zope-Buch auf zope.org und einigen anderen Büchern behandelt, aber sie bieten kein richtiges objektrelationales (OR) Mapping. ZSQL-Methoden speichern als Template das SQL-Statement und führen Abfragen aus, indem dieses SQL-Statement an die Datenbank abgesetzt wird. Dies ist recht nützlich, um einfache Abfragen durchzuführen, aber es eignet sich nicht, um ganze Objekte transparent in einer relationalen Datenbank abzuspeichern, und entspricht somit nicht der Gedankenwelt von Plone.</p>
<p><tt class="docutils literal">SQLStorage</tt> macht sich die Tatsache zunutze, dass Archetypes eine eigene konfigurierbare Schicht für das Speichern von Attributen bereithält. Für Zope gibt es eine Menge von Datenbankadaptoren, die <tt class="docutils literal">SQLStorage</tt> verwendet, um Attribute abzuspeichern. Derzeit sind es zwei Datenbankadaptoren, MySQL und Postgres, die von <tt class="docutils literal">SQLStorage</tt> direkt unterstützt werden. Allerdings ist es mit vertretbarem Aufwand verbunden, <tt class="docutils literal">SQLStorage</tt> auch auf andere Datenbankadaptoren anzupassen, da hier hauptsächlich einige SQL-Statements zum Anlegen von Tabellen und Spalten ausgetauscht werden müssten. Aber wie immer steckt der Teufel auch hier sicher im Detail.</p>
<div class="section" id="wie-mache-ich-meine-daten-in-einer-relationalen-datenbank-persistent">
<h5>Wie mache ich meine Daten in einer relationalen Datenbank persistent?</h5>
<p>Um Ihre Daten in einer relationalen Datenbank persistent zu machen, müssen Sie eine Zope-Datenbankverbindung einrichten. In diesem Fall verwende ich Postgres, weil ich Erfahrung damit habe und es allgemein eine universelle Datenbank ist, die für alle Schichten von Anwendern geeignet ist. Um Zope mit Postgres zu verbinden, ist das Gespann aus <em>psycopg</em> und <em>ZPsycopgDA</em> die richtige Wahl. Sie finden Pakete für Debian und Windows unter <a class="reference external" href="http://initd.org/software/psycopg">http://initd.org/software/psycopg</a>.</p>
<p>Installieren Sie Ihren Datenbankadapter, und richten Sie eine Datenbankverbindung zu Ihrer relationalen Datenbank ein. Dies ist wie gesagt im Zope-Buch im Detail unter <a class="reference external" href="http://zope.org/Documentation/Books/ZopeBook/2_6Edition/RelationalDatabases.stx">http://zope.org/Documentation/Books/ZopeBook/2_6Edition/RelationalDatabases.stx</a> beschrieben. Aber die kurze Antwort ist, dass Sie das ZMI öffnen und von der Liste der verfügbaren Objekte eine Datenbankverbindung auswählen und anschließend die Verbindungsparameter eingeben. Stellen Sie sicher, dass der Benutzer, als der Sie sich bei der Datenbank anmelden, die Berechtigung besitzt, Tabellen anzulegen, zu verändern und zu löschen (<tt class="docutils literal">create table</tt>, <tt class="docutils literal">alter table</tt> und <tt class="docutils literal">drop table</tt>). Jene Berechtigungen benötigen Sie nur während der Entwicklungs- und Installationsphase, da <tt class="docutils literal">SQLStorage</tt> automatisch die Tabellen und ihre Spalten anlegt. Im Betrieb, wenn sich am Schema nichts mehr ändert, werden diese Berechtigungen nicht mehr benötigt.</p>
<p>Anschließend klicken Sie im ZMI auf <em>archetypes_tool</em> in Ihrer Plone-Instanz und wählen <em>Connections</em>. Dies erlaubt Ihnen, die Inhaltstypen einzelnen Datenbankverbindungen zuzuordnen. Der einfachste und in den meisten Fällen sinnvollste Weg ist es, die Defaultverbindung anzugeben, die dann für alle Inhaltstypen gilt, die noch nicht zugeordnet sind. Nun müssen Sie im Schema den einzelnen Feldern die neue Storage zuordnen, wofür Sie die passende Storage-Klasse von <tt class="docutils literal">SQLStorage</tt> importieren (in diesem Fall ist dies <tt class="docutils literal">PostgreSQLStorage</tt>).</p>
<p>Dann müssen Sie den <tt class="docutils literal">storage</tt>-Parameter bei den Feldern angeben, die Sie in der Datenbank speichern wollen. Sie werden vielleicht bemerkt haben, dass Sie die Felder einzelnen verschiedenen Datenbanken zuordnen können. Sie können alle Felder in die relationale Datenbank speichern oder auch nur einzelne. Aber auch, wenn Sie alle Felder der relationalen Datenbank zuordnen, wird immer der Kopf des Objekts mit den Attributen <tt class="docutils literal">id</tt> und <tt class="docutils literal">uid</tt> in der ZODB abgespeichert. Im vorliegenden Beispiel werden zwei Felder in der SQL-Datenbank gespeichert: eine Ganzzahl (<tt class="docutils literal">age</tt>) und ein String (<tt class="docutils literal"><span class="pre">E-Mail</span></tt>), wie in Listing 13.4 gezeigt wird:</p>
<p>Listing 13.4. Ein Inhaltstyp, der <tt class="docutils literal">SQLStorage</tt> verwendet</p>
<pre class="literal-block">
from Products.Archetypes.public import BaseSchema, Schema
from Products.Archetypes.public import BaseContent, registerType
from Products.Archetypes.public import IntegerField, StringField
from Products.Archetypes.public import IntegerWidget, StringWidget
from Products.Archetypes.SQLStorage import PostgreSQLStorage
from config import PROJECTNAME

schema = BaseSchema + Schema((

  IntegerField('age',
      validators=(&quot;isInt&quot;,),
      storage = PostgreSQLStorage(),
      widget=IntegerWidget(label=&quot;Your age&quot;),

      ),

  StringField('E-Mail',
      validators = (&quot;isEmail&quot;,),
      index = &quot;TextIndex&quot;,
      storage = PostgresSQLStorage(),
      widget = StringWidget(label='E-Mail',)
      ),

    ))

class PersonSQL(BaseContent):
    &quot;&quot;&quot;Our person object&quot;&quot;&quot;
    schema = schema

registerType(PersonSQL, PROJECTNAME)
</pre>
<p>Schließlich starten Sie Plone neu und registrieren Ihr Produkt in Ihrem Plone-Portal. Nun können Sie Ihren Inhaltstyp testen. Wenn Sie ein  <tt class="docutils literal">PersonSQL</tt>-Objekt erzeugen, verhält sich auf der Oberfläche alles ganz normal. Der wirkliche Test findet statt, wenn Sie sich die Datenbank ansehen.</p>
<p>Sie werden sehen, dass in der Datenbank eine neue Tabelle namens  <tt class="docutils literal">personsql</tt> angelegt wurde und dass diese Tabelle vier Spalten enthält: <tt class="docutils literal">uid</tt>, <tt class="docutils literal">parentuid</tt>, <tt class="docutils literal">age</tt> und <tt class="docutils literal"><span class="pre">E-Mail</span></tt>. Im Kommandozeilen-Client von Postgres <em>psqk</em> sollte sich dann Folgendes zeigen:</p>
<pre class="literal-block">
db=# \d
                  List of relations
 Schema |           Name           |   Type   | Owner
--------+--------------------------+----------+-------
 public | personsql                | table    | www-data
...
db=# \d personsql
  Table &quot;public.personsql&quot;
  Column   | Type | Modifiers
-----------+------+-----------
 uid       | text | not null
 parentuid | text |
 age       | int  |
 E-Mail    | text |
Indexes: personsql_pkey primary key btree (uid)
</pre>
<p>Die Spalte für <tt class="docutils literal">age</tt> wurde als <tt class="docutils literal">int</tt> und die Spalte für <tt class="docutils literal"><span class="pre">E-Mail</span></tt> wurde mit dem Datentyp <tt class="docutils literal">text</tt> angelegt. Innerhalb von <tt class="docutils literal">SQLStorage</tt> existiert eine Zuordnung von Archetypes-Feldtypen zu SQL-Datentypen, diese Zuordnung kann bei Bedarf auch angepasst werden. Die  <tt class="docutils literal">uid</tt>-Spalte entspricht der eindeutigen ID <tt class="docutils literal">UID</tt> in Plone, die bereits vorher in diesem Kapitel behandelt wurde. Die Spalte  <tt class="docutils literal">parentuid</tt> enthält die eindeutige ID des übergeordneten Objekts.</p>
<pre class="literal-block">
db=# SELECT * FROM personsql;
          uid              | parentuid | age | E-Mail
---------------------------+-----------+-----+-------------------
 PersonSQL.2003-07-23.4935 |           | 30  | andy&#64;clearwind.ca
</pre>
<p>Das war's, Ihre Daten sind persistent innerhalb Ihrer relationalen Datenbank. Kein SQL muss manuell geschrieben werden, und Sie können trotzdem einige Vorteile von relationalen Datenbanken nutzen. Joel Burton hat eine vorzügliche Anleitung zu <tt class="docutils literal">SQLStorage</tt> geschrieben, die Sie unter <a class="reference external" href="http://plone.sourceforge.net/archetypes/sqlstorage-howto.html">http://plone.sourceforge.net/archetypes/sqlstorage-howto.html</a> finden. Mit seiner freundlichen Erlaubnis basieren Teile dieses Abschnitts auf Joels Dokument.</p>
<p>Ein anderer Ansatz ist, eine ganze Plone-Instanz in einer relationalen Datenbank abzuspeichern und direkt an der Persistenzschicht der ZODB anzusetzen. Dieser Ansatz wird von der <em>Adaptable Persistence Engine</em>, kurz <em>APE</em>, verfolgt, die von der Zope Corporation entwickelt wurde und nun in der Version 1.0 vorliegt. Sie können sie unter folgender Adresse herunterladen: <a class="reference external" href="http://hathawaymix.org/Software/Ape">http://hathawaymix.org/Software/Ape</a>.
APE ermöglicht es, einen ganzen Teil der ZODB in einer relationalen Datenbank abzuspeichern, und speichert somit alle Objekte unabhängig von deren Klasse in diese Datenbank. Ein Artikel auf plone.org beschäftigt sich mit der Integration von APE und Archetypes: <a class="reference external" href="http://plone.org/events/sprints/castlesprint/wiki/ApeSupport">http://plone.org/events/sprints/castlesprint/wiki/ApeSupport</a>.
Zurzeit gibt es noch keine praktische Erfahrung mit Plone und APE, um es für den produktiven Einsatz zu empfehlen, doch kann sich das in absehbarer Zeit ändern.</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch14.rst">
    <title>14. Administration und Skalierung von Plone</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/ch14.rst</link>
    <description>In diesem Kapitel geht es um Aufgaben, um die Sie sich kümmern müssen, nachdem Sie Ihre Site aufgebaut haben und sie benutzen. Ich beginne mit der Administration einer Plone-Site, die prinzipiell ziemlich einfach ist. Danach beschreibe ich, wann und von welchen Dateien Sicherheitskopien angelegt werden sollten. Außerdem behandle ich Aktualisierungen von Plone.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Administration und Skalierung von Plone</h2>
<p>In diesem Kapitel geht es um Aufgaben, um die Sie sich kümmern müssen, nachdem Sie Ihre Site aufgebaut haben und sie benutzen. Ich beginne mit der Administration einer Plone-Site, die prinzipiell ziemlich einfach ist. Danach beschreibe ich, wann und von welchen Dateien Sicherheitskopien angelegt werden sollten. Außerdem behandle ich Aktualisierungen von Plone.</p>
<p>Anschließend geht es um Performance-Fragen und Techniken zum Auffinden von Schwachstellen. Nachdem Sie diese gefunden haben, beschreibe ich häufig auftretende Probleme. Dann vertiefe ich das Thema Caching als wesentliche Technik dafür, Ihre Plone-Site wirklich schnell und skalierbar zu machen. Wenn es um Performance geht, müssen Sie definitiv wissen, wie Ihr Server mit mehreren Prozessen nach außen skalieren kann und wie man rechenintensive Anfragen behandelt. Dazu benötigen Sie <em>Zope Enterprise Objects</em> (ZEO), was am Ende dieses Kapitels behandelt wird.</p>
<div class="section" id="administration-einer-plone-site">
<h3>Administration einer Plone-Site</h3>
<p>Wie sich herausstellt, ist die Administration einer Plone-Site ziemlich einfach: Sie müssen lediglich ein paar Aufgaben erledigen, die für alle Dienste gleich sind. Gemeint ist Folgendes:</p>
<ul class="simple">
<li>Sie sollten regelmäßig ein Backup von der Datenbank machen.</li>
<li>Sie sollten die Datenbank regelmäßig komprimieren.</li>
<li>Sie sollten rotierende Backups der Protokolldateien anlegen.</li>
</ul>
<p>Diese Aufgaben sollten Sie regelmäßig bei der Wartung Ihrer Website erledigen. In einer Firma haben Sie oftmals standardisierte Werkzeuge für Backups und rotierende Protokolldateien. Diese Werkzeuge lassen sich alle leicht integrieren, da Plone-Daten alle in Form von Dateien im Dateisystem vorliegen.</p>
<div class="section" id="backups-ihrer-plone-site-durchfuhren">
<h4>Backups Ihrer Plone-Site durchführen</h4>
<p>Von einer Plone-Site sollten Sie regelmäßig Backups anfertigen. Die meisten Leute erstellen Backups in der Nacht. Ein Backup-Plan sollte aus den Anforderungen Ihrer Anwendung abgeleitet werden. Wenn große Datenmengen geschrieben werden, sind vielleicht häufigere Backups notwendig. Bei einer kleinen Site mit weniger Inhalten mögen weniger häufige Backups, z.B. einmal pro Woche, besser geeignet sein.</p>
<p>Bei einer normalen Plone-Site muss nur von einer Datei ein Backup erstellt werden: von der Zope-Datenbank, in der alle Inhalte der Plone-Site stecken. Diese Datei finden Sie, indem Sie auf das Control Panel im Zope Management Interface (ZMI) zugreifen, dort <em>Database Management</em> wählen und dann auf <em>main</em> klicken. Diese Seite zeigt die Größe und den Ort der Datenbank an. Die Datei heißt <tt class="docutils literal">Data.fs</tt>, und Sie finden sie im Verzeichnis <tt class="docutils literal">var</tt> Ihrer Instanzwurzel. Um das Backup auszuführen, kopieren Sie diese Datei lokal. Dann können Sie sie sicher auf einen externen Speicher kopieren, z.B. auf eine andere Festplatte, einen Server, ein Bandlaufwerk oder was Sie auch immer sonst für ein Backup-System haben. Dieses Backup können Sie sogar dann durchführen, während Plone läuft.</p>
<p>Zum Anlegen von Backups können Sie Ihre eigenen Skripten und Werkzeuge oder ein Werkzeug aus Zope benutzen. Als Beispiel für die erste Möglichkeit zeigt Listing 14.1 ein Linux-Bash-Skript, das ich für das Backup einer Zope-Site benutze.</p>
<p>Listing 14.1. Bash-Skript für ein Backup</p>
<pre class="literal-block">
#!/bin/bash
# script to copy, gzip, and then copy Zope databases
# to remote server
# make up a filename
fn=`uuidgen`.fs
# copy it locally, you'll want to change the
# path
cp /var/zope.test/var/Data.fs /tmp/$fn
# gzip the file up
gzip /tmp/$fn
# scp over to my backup server and then remove
# the temporary file
# change the destination file
scp /tmp/$fn.gz backup&#64;backups.agmweb.ca:~/Zope
rm /tmp/$fn.gz
</pre>
<p>Als zweite Möglichkeit steht Ihnen ein Python-Skript namens <tt class="docutils literal">repozo.py</tt> in der Zope-Objektdatenbank (ZODB) zum Erstellen von Backups zur Verfügung. Sie finden dieses Skript online unter <a class="reference external" href="http://cvs.zope.org/ZODB3/Tools/repozo.py">http://cvs.zope.org/ZODB3/Tools/repozo.py</a>. Es funktioniert ziemlich gut unter Windows und Linux und kann eine ganze Menge, z.B. vollständige und inkrementelle Backups erstellen und die Datenbank wiederherstellen.</p>
<p>Um mit diesem Skript ein Backup einer Datenbank anzufertigen, müssen Sie zuerst ein Verzeichnis anlegen, das das Backup aufnimmt. In den folgenden Beispielen lautet dieses Verzeichnis <tt class="docutils literal">/home/backups</tt>, aber Sie haben die freie Wahl dabei. Führen Sie Folgendes aus, um ein vollständiges Backup einer Datenbank zu erstellen:</p>
<pre class="literal-block">
$ python repozo.py -B -F -v -r /home/backups -f /var/zope.test/var/Data.fs
looking for files b/w last full backup and 2003-11-21-18-33-17...
no files found
doing a full backup
writing full backup: 3601549 bytes to /home/backups/2003-11-21-18-33-17.fs
</pre>
<p>Um ein inkrementelles Backup anzufertigen, lassen Sie einfach die Option <tt class="docutils literal"><span class="pre">-F</span></tt> (für engl. <em>full</em>) weg. Das Skript vergleicht dann die aktuelle ZODB mit dem letzten Backup und kopiert lediglich die Unterschiede. Wenn keine Aktualisierungen stattgefunden haben, erfolgt kein Backup. Folgendes ist ein Beispiel-Backup nach einer Änderung in Plone:</p>
<pre class="literal-block">
$ python repozo.py -B -v -r /home/backups -f /var/zope.test/var/Data.fs
looking for files b/w last full backup and 2003-11-21-18-39-09...
files needed to recover state as of 2003-11-21-18-39-09:
        /home/backups/2003-11-21-18-33-17.fs
repository state: 3601549 bytes, md5: ab9e46bcdf52641ad6f71db62a9da333
current state   : 3624968 bytes, md5: 73c871bbe2528e152342abea9e25ab27
backed up state : 3601549 bytes, md5: ab9e46bcdf52641ad6f71db62a9da333
doing incremental, starting at: 3601549
writing incremental: 23419 bytes to /home/backups/2003-11-21-18-39-11.deltafs
</pre>
<p>An diesem Punkt haben Sie ein vollständiges und ein inkrementelles Backup. Dasselbe Skript kann diese Daten nun auch wiederherstellen. Dazu geben Sie die Option <tt class="docutils literal"><span class="pre">-R</span></tt> (für engl. <em>recovery</em>) und mit <tt class="docutils literal"><span class="pre">-o</span></tt> die Ausgabedatei wie folgt an:</p>
<pre class="literal-block">
$ python repozo.py -R -v -r /home/backups -o /var/zope.test/var/Data.fs
looking for files b/w last full backup and 2003-11-21-18-50-21...
files needed to recover state as of 2003-11-21-18-50-21:
 /home/backups/2003-11-21-18-33-17.fs
 /home/backups/2003-11-21-18-39-11.deltafs
Recovering file to /var/zope.test/var/Data.fs
Recovered 3624968 bytes, md5: 73c871bbe2528e152342abea9e25ab27
</pre>
<p>Eine vollständige Liste aller Optionen erhalten Sie, wenn Sie <tt class="docutils literal">repozo.py</tt> mit <tt class="docutils literal"><span class="pre">-h</span></tt> ausführen, was alle vorhandenen Optionen ausgibt.</p>
<p>Die Protokolldateien befinden sich standardmäßig im <tt class="docutils literal">log</tt>-Verzeichnis Ihrer Instanzwurzel. Dort gibt es zwei Dateien: eine für Zugriffe und eine für Ereignisse. Den Ort dieser Protokolldatei können Sie in der Konfigurationsdatei angeben, die Sie in Kapitel 2 kennen gelernt haben. In <tt class="docutils literal">z2.log</tt> werden alle ankommenden Anfragen protokolliert und in <tt class="docutils literal">event.log</tt> alle Fehler. Von diesen Dateien sollte regelmäßig ein Backup gemacht werden, zusammen mit eventuell vorhandenen Protokolldateien auf Proxy-Servern, z.B. solchen, die Apache oder IIS (Internet Information Services) produzieren.</p>
<p>Von Code, Templates und eigenen Produkten außerhalb der ZODB sollten Sie ebenfalls regelmäßige Backups anfertigen. Auch dann, wenn Sie diese in einem Versionsverwaltungsprogramm wie CVS (Concurrent Versioning System) aufbewahren, kann es nicht schaden, einen gültigen Schnappschuss Ihrer Installation zu machen.</p>
<p>Wenn Sie Inhalte, andere Datenbanken oder andere Daten haben, die nicht in der ZODB sind, sollten auch diese Teil des Backup-Plans sein, je nachdem, wie oft sie sich ändern. Das könnten Daten in relationalen Datenbanken und Inhalte im Dateisystem sein. All diese Daten werden vom Site-Entwickler erzeugt und kommen in einer normalen &quot;taufrischen&quot; Plone-Site nicht vor. Wenn Sie Zope oder Plone aktualisieren, kann es sinnvoll sein, ein Backup aller beteiligten Dateien zu machen, inklusive Zope und Plone, damit bei einer womöglich fehlerhaften Aktualisierung eine vollständige Wiederherstellung möglich ist.</p>
</div>
<div class="section" id="die-zodb-komprimieren">
<h4>Die ZODB komprimieren</h4>
<p>Die ZODB speichert alle Änderungen an allen Objekten im System. Jedes Mal, wenn sich ein Objekt ändert, wird eine neue Kopie ans Ende der ZODB-Datei angehängt. Dies ist die Datei <tt class="docutils literal">Data.fs</tt>, die ich im vorigen Abschnitt beschrieben habe. Falls die Datenbank große Teilinhalte oder eine große Anzahl von Änderungen enthält, kann das dazu führun, dass die ZODB merklich anwächst.</p>
<p>Eine große ZODB als solche ist kein Problem, auch sie funktioniert einwandfrei, und die Zeiten beim Hochfahren sind vergleichbar (es sei denn, der Index wurde entfernt). Die Zeiten für die Komprimierung werden länger, je größer die Datenbank ist. Daher macht es Sinn, diese alten Kopien von Objekten, die nicht mehr benötigt werden, gelegentlich zu entfernen, um die Datenbank kleiner zu machen. Es ist ganz wesentlich zu wissen, dass Sie beim Komprimieren lediglich Ihre vorhandene Datenbank säubern und einige alte Kopien wegwerfen.</p>
<div class="sidebar">
<p class="first sidebar-title">Die alte 2-Gbyte-Grenze bei Datenbanken</p>
<!-- A problem exists with older versions of Python (before Python 2.1 on Unix and before Python 2.2 on Windows), which weren't capable of large file support. When the ZODB reaches 2 gigabytes (GB), the Plone site dies and can't be restarted. To test if you're running a Python version that has large file support, open a file in Python and see if its size is reported as an integer or a long, like so: -->
<p>Mit älteren Versionen von Python (vor Python 2.1 unter Unix und vor Python 2.2 unter Windows) gibt es ein Problem, da diese große Dateien nicht unterstützen. Wenn die ZODB dann zwei Gigabyte erreicht, bleibt die Plone-Site stehen und kann nicht neu gestartet werden. Um zu testen, ob Sie eine Python-Version verwenden, die große Dateien unterstützt, öffnen Sie eine Datei in Python und sehen wie folgt nach, ob deren Größe als Integer oder als Long angegeben wird:</p>
<pre class="literal-block">
&gt;&gt;&gt; import os
&gt;&gt;&gt; from stat import ST_SIZE
&gt;&gt;&gt; type(os.stat('/tmp/test.txt')[ST_SIZE])
&lt;type 'long'&gt;
</pre>
<!-- This Python has large file support enabled and can support files larger than 2GB. If an integer is reported, then you'll need to upgrade your Python version or recompile with large file support enabled (again, enabled in new version by default). If you try to compile Plone with a version of Zope that doesn't have large file support, you'll get an error, like so: -->
<p>Diese Python-Version unterstützt große Dateien und solche, die über 2 Gbyte groß sind. Falls ein Integer zurückgegeben wird, dann müssen Sie Ihre Python-Version aktualisieren oder mit Unterstützung für große Dateien neu kompilieren (in neueren Versionen ist diese Unterstützung automatisch vorhanden). Wenn Sie versuchen, Plone mit einer Zope-Version zu kompilieren, die große Dateien nicht unterstützt, erhalten Sie folgenden Fehler:</p>
<pre class="literal-block">
andy&#64;thorin:/tmp/Zope-2.7.0-b3$ ./configure
Configuring Zope installation
...

This Python interpreter does not have have 'large file support' enabled.
</pre>
<!-- If this is the case, then you'll need to go and fix your Python installation. You can find more details about this at http://www.python.org/doc/current/lib/posix-large-files.html. If you're happy with just limiting to 2GB, then you can pass the *- -ignore-largefile* option to the configure script. If you're limited to a 2GB database, then you'll need to pack more regularly. -->
<p class="last">Wenn das der Fall ist, dann müssen Sie Ihre Python-Installation auf Vordermann bringen. Weitere Details dazu finden Sie unter <a class="reference external" href="http://www.python.org/doc/current/lib/posix-large-files.html">http://www.python.org/doc/current/lib/posix-large-files.html</a>. Wenn Ihnen die Beschränkung auf 2 Gbyte nichts ausmacht, können Sie beim Konfigurationsskript die Option <tt class="docutils literal"><span class="pre">--ignore-largefile</span></tt> angeben. Mit einer Beschränkung auf eine 2 Gbyte große Datenbank werden Sie häufiger eine Komprimierung vornehmen müssen.</p>
</div>
<div class="section" id="komprimieren-heiszt-in-der-datenbank-aufraumen">
<h5>Komprimieren heißt in der Datenbank aufräumen</h5>
<p>Eine Komprimierung kann aufwendig sein, und wenn dieser Prozess ausgeführt wird, passiert das in einem separaten Thread, d.h., auch wenn die Geschwindigkeit einer Site dadurch beeinflusst wird, kann sie weiterhin auf Anfragen reagieren. Wie Sie Sites komprimieren, während Plone trotzdem mit maximaler Performance läuft, wird im Abschnitt &quot;ZEO-Clients benutzen&quot; später in diesem Kapitel erklärt. Um eine Komprimierung durchzuführen, gehen Sie ins Control Panel des ZMI, wählen <em>Database Management</em> und klicken auf <em>main</em>.</p>
<a class="reference external image-reference" href="img/14-01.png/image_view_fullscreen"><img alt="img/14-01.png" class="original" src="img/14-01.png" /></a>
<p>Figure 14.1. Komprimieren einer Datenbank</p>
<p>Geben Sie die Anzahl von Tagen an, für die Sie Objekte behalten möchten, und klicken Sie auf <em>Pack</em>. Wenn Sie z.B. die Anzahl von Tagen auf null setzen (die Voreinstellung), werden alle Versionen der Objekte entfernt. Noch einmal: Nicht das Objekt selbst wird gelöscht, sondern nur diese alten Kopien davon. Eine üblichere Einstellung ist ein Wert um sieben herum, wobei dann Versionen gelöscht werden, die älter als eine Woche sind. Durch die Wahl einer mit Ihrem Backup-Plan verträglichen Einstellung können Sie garantieren, dass Sie von jedem Objekt eine Kopie behalten. Die Komprimierung wird je nach Größe Ihrer ZODB einige Zeit und Verarbeitungskapazität in Anspruch nehmen. Plone läuft währenddessen weiter, wenn auch etwas langsamer, d.h., eventuell möchten Sie ZEO dafür benutzen.</p>
</div>
</div>
<div class="section" id="plone-aktualisieren">
<h4>Plone aktualisieren</h4>
<p>Plone wird kontinuierlich weiterentwickelt und verbessert, d.h., es kommen ziemlich regelmäßig neue Versionen von Plone heraus. Bevor Sie auf eine neue Version von Plone aktualisieren, sollten Sie aber nachprüfen, ob Sie diese wirklich brauchen. Zwischen den einzelnen Releases gibt es recht häufig nur kleine, möglicherweise wenig relevante Änderungen. Zu jedem Release gibt es eine Liste von Änderungen auf der Seite, wo man es herunterladen kann. Es empfiehlt sich immer ein Blick auf diese Liste, um zu sehen, ob sich die Aktualisierung lohnt.</p>
<p>Machen Sie zuerst ein Backup, und laden Sie dann die Aktualisierung herunter. Am leichtesten führen Sie die Aktualisierung vermutlich durch, indem Sie die gleichen Schritte wie bei der Installation wiederholen. Wenn Sie die Installation mit dem Windows-Installationsprogramm durchgeführt haben, laden Sie das neue Installationsprogramm herunter und wiederholen die Installation. Wenn Sie eine Installation aus den Quellen oder einem Debian-Paket durchgeführt haben, wiederholen Sie diese Schritte. Die Aktualisierung umfasst folgende Schritte:</p>
<ol class="arabic simple">
<li>Die entsprechende Aktualisierung herunterladen</li>
<li>Plone anhalten</li>
<li>Ein Backup durchführen (wie zuvor beschrieben)</li>
<li>Die Aktualisierung installieren</li>
<li>Plone starten</li>
</ol>
<p>An dieser Stelle möchte ich empfehlen, Plone im Debug-Modus zu starten. Unter Windows können Sie das tun, indem Sie <em>Start - Plone - Plone Debug</em> wählen. Unter Linux machen Sie das mit dem Skript <tt class="docutils literal">runzope</tt> im <tt class="docutils literal">bin</tt>-Verzeichnis Ihrer Instanzwurzel wie folgt:</p>
<pre class="literal-block">
bin/runzope -X &quot;debug-mode=on&quot;
</pre>
<p>Durch die Ausführung im Debug-Modus können Sie direkt eventuelle Fehler sehen, die bei der Aktualisierung auf die neue Version aufgetreten sind. Wenn Sie damit zufrieden sind, können Sie mit dem nächsten Schritt, der Migration, weitermachen.</p>
<p>Gehen Sie bei all Ihren Plone-Sites ins ZMI, und greifen Sie auf das Werkzeug <tt class="docutils literal">portal_migration</tt> in Ihrer Plone-Site zu. Es wird ein hellrotes Ausrufezeichen daneben haben, was angibt, das die Site nicht auf dem neusten Stand ist. Nun müssen Sie eine Migration von Plone auf die aktuelle Version vornehmen. Eine Migration ist notwendig, da Sie das Dateisystem aktualisiert haben, aber Ihre Datenbank nicht mit dem Dateisystem synchronisiert ist. Es könnte Änderungen an den Inhaltstypen, den Werkzeugen oder anderen Teilen Ihrer Site geben, die vorgenommen werden müssen.</p>
<p>Die Migration wird versuchen, diese Änderungen für Sie durchzuführen. Solange Sie diese Migration nicht durchführen, könnte es sein, dass Ihre Plone-Site defekt ist. Je nachdem, was bei der Migration gemacht werden muss, kann das einige Zeit brauchen. Befolgen Sie diese Schritte, um eine Migration durchzuführen:</p>
<ol class="arabic simple">
<li>Klicken Sie unter <em>portal_migration</em> auf den <em>Migrate</em>-Reiter.</li>
<li>Klicken Sie auf den <em>Upgrade</em>-Button. Dieser Upgrade-Vorgang kann eine Weile dauern, besonders bei großen Sites oder wenn eine große Aktualisierung notwendig ist.</li>
<li>Das Ergebnis der Migration, eine ziemlich lange Meldung, wird auf dem Bildschirm angezeigt. Die Migration war erfolgreich, wenn die letzte Meldung &quot;End of upgrade path, migration has finished&quot; lautet. Eventuelle Fehlermeldungen werden in Rot hervorgehoben.</li>
</ol>
<p>Wiederholen Sie diesen Vorgang für alle Plone-Sites in Ihrer Zope-Instanz. Wenn Sie mit der migrierten Site zufrieden sind, halten Sie das im Debug-Modus laufende Plone an. Starten Sie Plone wie üblich neu, und machen Sie ganz normal weiter.</p>
</div>
</div>
<div class="section" id="die-performance-von-plone-verbessern">
<h3>Die Performance von Plone verbessern</h3>
<p>Nun haben Sie also eine wunderbare Website, Millionen von Besuchern schauen vorbei, aber sie zeigt nicht ganz die gewünschte Performance. Nun, Plone wurde von vornherein als System entworfen, das reich an Möglichkeiten, aber nicht unbedingt sehr schnell ist, weil die Geschwindigkeit sehr stark von der jeweiligen Anwendung abhängt. Man kann Plone allerdings mit vielen Techniken sehr schnell machen, und es skaliert sehr leicht. In den folgenden Abschnitten behandle ich, wie Sie die langsamen Teile Ihrer Site ausfindig machen, und zeige Ihnen dann Methoden, wie Sie sie verbessern können.</p>
<div class="section" id="benchmarks-einer-plone-site-erstellen">
<h4>Benchmarks einer Plone-Site erstellen</h4>
<p>Bevor Sie versuchen, eine Site zu optimieren, besteht die wesentliche Aufgabe darin, sich einen Zahlenwert für die Performance der Site zu verschaffen. Die Benutzer geben oftmals Feedback der Art &quot;sie ist zu langsam&quot; oder &quot;es braucht zu lange, bis sie geladen wird&quot;. Für einen Entwickler sind diese Kommentare nahezu nutzlos. Sie müssen die Geschwindigkeit quantitativ angeben können, um zu wissen, wie schnell die Site jetzt ist und wie schnell sie gemacht werden muss. Erst dann können Sie mit der Optimierung beginnen.</p>
<p>Um einen Benchmark zu erhalten, können Sie ein Werkzeug namens <em>ab</em> bzw. Apache Bench benutzen. Dieses Werkzeug ist Teil des Apache-Servers. Falls Sie Apache 1.3 oder höher unter Linux installiert haben, ist <em>ab</em> bereits enthalten. Unter Windows ist es ab der Version 2 von Apache enthalten. Man kann <em>ab</em> sehr einfach ausführen: Geben Sie einfach die URL (Uniform Resource Locator) an, die Sie testen möchten, z.B. so:</p>
<pre class="literal-block">
ab http://localhost/
</pre>
<p>Das Werkzeug <em>ab</em> gibt dann wie folgt zuerst einige Daten über die getestete Site aus:</p>
<pre class="literal-block">
Benchmarking localhost (be patient).....done
Server Software:        Zope/(unreleased
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        20594 bytes
</pre>
<p>Anschließend gibt es wie folgt einige zusammenfassende Statistiken aus:</p>
<pre class="literal-block">
Concurrency Level:      1
Time taken for tests:   0.771151 seconds
Complete requests:      1
Failed requests:        0
Write errors:           0
Total transferred:      20933 bytes
HTML transferred:       20594 bytes
Requests per second:    1.30 [#/sec] (mean)
Time per request:       771.151 [ms] (mean)
Time per request:       771.151 [ms] (mean, across all concurrent requests)
Transfer rate:          25.94 [Kbytes/sec] received
</pre>
<p>Das sagt Ihnen, wie lange die Anfrage bearbeitet wurde, wie viele Fehler aufgetreten sind, und wie lang es dauerte, bis eine Anfrage abgearbeitet wurde, was vermutlich die wichtigste Angabe ist. Der nützlichste Wert, den man angeben kann, ist normalerweise <tt class="docutils literal">Requests per second</tt>, d.h. Anfragen pro Sekunde - in diesem Beispiel also <tt class="docutils literal">1.30 <span class="pre">[#/sec]</span></tt>. Weitere Statistiken, die das Werkzeug <tt class="docutils literal">ab</tt> bietet, geben Informationen darüber an, wie lange es dauert, bis eine Verbindung erstellt wird, wie lange sie bearbeitet wird bzw. wie lange es braucht, bis ein Ergebnis für eine Anfrage vorliegt. Beispiel:</p>
<pre class="literal-block">
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   770  770   0.0    770     770
Waiting:      766  766   0.0    766     766
Total:        770  770   0.0    770     770
</pre>
<p>Diese letzte Information ist hilfreich und beinhaltet die Zeit für den Aufbau einer Verbindung. Da mein Server auf dem gleichen Rechner wie der Client läuft, ist sie hier ziemlich kurz. Dieser Test zeigt, dass es 1,30 Sekunden dauert, eine Anfrage abzuarbeiten. Der Server wurde dabei natürlich so gut wie gar nicht getestet. Um die reale Welt besser zu simulieren, möchten Sie den Server beim Testen vermutlich mit einigen Anfragen gleichzeitig belasten. Das können Sie tun, indem Sie die Anzahl der Anfragen insgesamt mit der Option <tt class="docutils literal"><span class="pre">-n</span></tt> und die Anzahl der gleichzeitigen Anfragen (Thread) mit <tt class="docutils literal"><span class="pre">-c</span></tt> angeben. Beispiel:</p>
<pre class="literal-block">
ab -n 20 -c 4 http://localhost/
</pre>
<p>Hierbei werden insgesamt 20 Anfragen über vier parallele Threads geschickt. Das Endergebnis ist eine leicht andere Anzahl von Anfragen pro Sekunde, nämlich 1.78. Weitere Angaben zu allen verfügbaren Optionen lesen Sie bitte im Handbuch zu Apache Bench unter <a class="reference external" href="http://httpd.apache.org/docs/programs/ab.html">http://httpd.apache.org/docs/programs/ab.html</a> nach.</p>
<p>Einer der Vorteile von <em>ab</em> besteht darin, dass Sie nicht wirklich die Seiten auf dem Client zusammensetzen. Stattdessen werden sie nur heruntergeladen und dann weggeworfen. Wenn Sie eine Seite mit einer Menge an Skripten oder mit großen Bildern haben, wird die Zeit nicht mitgerechnet, die ein Client braucht, um diese Seite zu etwas zusammenzusetzen, was für den Benutzer Sinn macht. Ein  klassisches Beispiel hierfür ist der alte Netscape-Browser, der durch eine große Zahl von Tabellen stark verlangsamt wird oder sogar abstürzen kann. Mit <em>ab</em> wäre das nicht festzustellen, denn es gibt Ihnen eine unabhängigere Zahl, mit der Sie arbeiten können.</p>
</div>
<div class="section" id="lugen-schlimme-lugen-und-benchmark-zahlen">
<h4>Lügen, schlimme Lügen und Benchmark-Zahlen</h4>
<p>An dieser Stelle sind Sie vermutlich über diese Zahlen besorgt. Sie scheinen für eine sehr langsame Site zu sprechen. Der Rechner in diesen Beispielen ist mein Toshiba-Laptop mit einem 1,8-GHz-Celeron-Prozessor, 256 Mbyte RAM, Red Hat Linux 9.0 und einer Beta-Version von Plone 2. Außerdem läuft Plone im Debug-Modus gleichzeitig mit KDE, OpenOffice.org, Instant Messenger und mehreren anderen Entwicklerwerkzeugen, inklusive des eigentlichen Benchmark-Werkzeugs. Das heißt, weder ist Plone irgendwie optimiert noch läuft es in einer idealen Umgebung. Ein ähnlicher Test auf einem schnelleren Server ergab einen Wert von etwa 20 Anfragen pro Sekunde.</p>
<p>Der wesentliche Punkt ist, dass Sie mit Hilfe einer objektiven Zahl für die Site-Performance den Erfolg Ihrer Optimierungen messen können. Entwickler können Tricks und Kniffe anwenden und diese testen, um die Zahlen &quot;davor&quot; und &quot;danach&quot; zu vergleichen. Wenn möglich, sollten Sie die Performance-Tests auf einem Rechner durchführen, der möglichst ähnlich zu dem Server in Produktion ist, um vernünftige Zahlen zu erhalten. In diesem Kapitel ist es nicht wichtig, dass die Site X Anfragen pro Sekunde schafft, sondern es ist wichtig, dass eine Änderung eine signifikante Verbesserung der Performance bewirkt.</p>
<p>Denken Sie außerdem daran, dass Zahlen darüber, wie schnell ein bestimmter Teil Ihrer Site ist, für sich allein genommen ziemlich nutzlos sind. Sie müssen verschiedene andere Dinge ebenfalls bedenken, z.B. wie oft die Seite besucht wird, die Erwartungen der Benutzer sowie realistische Anforderungen. Die penibel genaue Messung eines Teils einer Site kann nützlich sein, um ein bestimmtes Problem zu finden, macht Ihre Site aber eventuell kaum schneller. Wie bei den meisten Dingen brauchen Sie auch hier einen vernünftigen Ansatz bei Ihrer Optimierung.</p>
</div>
<div class="section" id="produktions-vs-debug-modus">
<h4>Produktions- vs. Debug-Modus</h4>
<p>Eines der Dinge, die am meisten Geschwindigkeit fressen, ist, wenn Sie Plone im Debug-Modus betreiben. Im Debug-Modus werden alle Templates, Skripten und Objekte im Werkzeug <tt class="docutils literal">portal_skins</tt> mit dem Dateisystem verglichen, um zu überprüfen, ob sie aktuell sind. Diese Überprüfung erfolgt bei jeder einzelnen Anfrage, was beim Testen hilfreich ist, aber einen riesigen Performance-Einbruch bedeutet. Auf meinem Testserver z.B. erhöht sich nach dem Abschalten des Debug-Modes die Geschwindigkeit von 1.30 auf 2.41 Anfragen pro Sekunde, eine signifikante Verbesserung.</p>
<p>Um herauszufinden, ob Ihre Site im Debug-Modus läuft, gehen Sie im ZMI zum Objekt <tt class="docutils literal">portal_migration</tt> Ihrer Plone-Site. Am unteren Rand dieser Seite steht eine Liste von Informationen, darunter auch der Status des Debug-Modus. Um ihn zu ändern, ändern Sie die Konfigurationsdatei, wie in Kapitel 2 beschrieben.</p>
</div>
<div class="section" id="andere-grunde-fur-geringe-performance">
<h4>Andere Gründe für geringe Performance</h4>
<p>Ein Server kann aus verschiedenen Gründen, die nichts mit Plone zu tun haben, langsam laufen. Wenn Sie eine Optimierung durchführen, sollten Sie immer zuerst einen Blick darauf werfen, da Sie hierdurch mit kleinem Aufwand schnell Verbesserungen bei der Geschwindigkeit erreichen können.</p>
<div class="section" id="prozessorauslastung">
<h5>Prozessorauslastung</h5>
<p>Wenn Sie eine große Anzahl von Anwendungen laufen haben oder nur wenige sehr aufwendige, wird die für Plone verfügbare Prozessorzeit dadurch beschränkt. Das Zusammensetzen von Seiten in Plone kann eine Menge CPU-Zeit in Anspruch nehmen. Eine Anwendung, die durch die verfügbare CPU-Zeit beschränkt ist, heißt auch <em>CPU-beschränkt</em>.</p>
<p>Um herauszufinden, unter welcher Last ein Server unter Linux ist, benutzen Sie den Befehl <em>top</em>. Unter Windows erhalten Sie eine ähnliche Statistik vom Task Manager (den Sie mit der Tastenkombination <strong>Strg+Alt+Lösch</strong> erreichen). Die empfohlene Geschwindigkeit für Ihre CPU hängt von der Größe und Last des Verkehrs auf Ihrem Plone-Server ab, aber ein 2-GHz-Prozessor ist eine gute Ausgangsbasis.</p>
</div>
<div class="section" id="grosze-des-hauptspeichers">
<h5>Größe des Hauptspeichers</h5>
<p>Zope benutzt gern einen großen Teil des Hauptspeichers, wenn Objekte aus der ZODB geladen werden. Von allen wesentlichen Eigenschaften eines Zope-Servers hat ein größerer Hauptspeicher vermutlich den größten Effekt. Eine Anwendung, die durch den verfügbaren Hauptspeicher beschränkt ist, heißt auch <em>speicherbeschränkt</em>. Sehr wahrscheinlich wird Ihr Server den Geist aufgeben, wenn er auf diese Grenze stößt, während der Swap-Speicher auf die Festplatte ausgelagert wird.</p>
<p>Um herauszufinden, unter welcher Last ein Server unter Linux ist, benutzen Sie den Befehl <em>top</em>. Unter Windows erhalten Sie eine ähnliche Statistik vom Task Manager (den Sie mit der Tastenkombination <strong>Strg+Alt+Lösch</strong> erreichen). Die empfohlene Größe des Hauptspeichers hängt von der Größe und Last des Verkehrs auf Ihrem Plone-Server ab, aber 512 Mbyte sind ein guter Anfang. Wenn Sie sich mehr leisten können, empfiehlt sich auch mehr.</p>
<p>An den Hauptspeicherparametern können Sie in Plone etwas drehen, indem Sie die Anzahl der Objekte im Cache erhöhen. Per Voreinstellung hat Plone 400 Objekte im Cache. Das könnten Sie bei einer Site auf 5000 erhöhen, wie in Abbildung 14.2 gezeigt ist. Obwohl sich der Hauptspeicherverbrauch erhöht, steigt dabei aber <em>auch</em> die Performance. Die so gewonnene Performance kann man jedoch unmöglich vorhersagen, weil Objekte naturgemäß unterschiedlich groß sind. Der beste Ansatz ist vermutlich der, diesen Wert heraufzusetzen und dann einige Anfragen zu testen, um zu sehen, ob sich dieser Kompromiss lohnt.</p>
<a class="reference external image-reference" href="img/14-02.png/image_view_fullscreen"><img alt="img/14-02.png" class="original" src="img/14-02.png" /></a>
<p>Abbildung 14.2. Die Cache-Parameter im Control Panel ändern</p>
<p>Je weniger Threads Zope außerdem benutzt, desto weniger Hauptspeicher wird verbraucht. Zwar ist Zope multithread-fähig, aber meistens wird nur ein Zope-Thread wirklich benutzt. Eine Reduktion der Thread-Anzahl auf drei ergibt einen speichereffizienteren Server. Anstatt zu versuchen, eine große Zahl von Threads laufen zu lassen, wird empfohlen, mit Hilfe von ZEO-Clients mehr Anfragen zu bedienen. Der Abschnitt &quot;Zope Enterprise Objects verwenden&quot; behandelt das etwas detaillierter.</p>
</div>
<div class="section" id="netzwerkverbindung">
<h5>Netzwerkverbindung</h5>
<p>Die Netzwerkverbindung kann von entscheidender Bedeutung für die Performance einer Anwendung sein. Sie können nicht schneller sein als die langsamste Verbindung zwischen Ihnen und dem Client. Beachten Sie beim Optimieren einer Plone-Site daher auch die Verbindungszeit. Wenn eine Verbindung zwei Sekunden braucht, ist die Optimierung des Codes ziemlich sinnlos.</p>
<p>Auch hierbei kann das Werkzeug <em>ab</em> helfen. Wenn Sie einen Benchmark-Test von Plone.org von Britisch-Kolumbien, Kanada, aus durchführen (der Server befindet sich in Texas), können Sie in der folgenden Ausgabe sehen, dass der Median der Wartezeit für Verbindungen über das Netzwerk 125 Millisekunden betrug:</p>
<pre class="literal-block">
            Connection Times (ms)
            min  mean[+/-sd] median   max
Connect:       90  133  40.2    125     211
Processing:   511 1103 400.2   1113    1846
Waiting:      202  310 110.3    293     565
Total:        601 1236 411.2   1211    2043
</pre>
<p>Eventuell hat der Server auch eine Beschränkung bezüglich der Anzahl der Verbindungen oder bei der Reise durch interne Firewalls. Wenn ein Prozess derart durch die Zeit beschränkt ist, die er für eine Input/Output-Operation benötigt (I/O), heißt er auch <em>I/O-beschränkt</em>. Was Sie an dieser Stelle machen können, hängt davon ab, über welche Möglichkeiten Sie im Netzwerk verfügen. Wenn Ihr Client sehr weit von Ihrem Server entfernt ist und die Verbindung sehr langsam ist, dann könnten Caches in der Nähe des Clients eine geeignete Möglichkeit sein.</p>
</div>
<div class="section" id="ihre-anwendung">
<h5>Ihre Anwendung</h5>
<p>Es könnte natürlich auch sein, dass Ihre Anwendung Ursache für die geringe Performance ist. Es gibt zahlreiche (und vermutlich übertriebene) Beispiele von Dienstleistungsfirmen für Kunden mit Problemen. Zu den bekannteren Beispielen gehören die folgenden:</p>
<ul class="simple">
<li>Kopierter Code von einer Website, der einen tief im System verborgenen <em>sleep</em>-Aufruf enthielt, wodurch das Skript einige Sekunden pausiert. Eine von jemandem durchgeführte Code-Inspektion hat das zu Tage gebracht, und die Zeile wurde entfernt.</li>
<li>Mehrfache Abfragen in relationalen Datenbanken, z.B. über ein Dutzend auf einer Seite. Mit einem intelligenteren Design wurden die Abfragen kombiniert, was auch ein Caching erlaubte.</li>
<li>Ein Skript, das Informationen aus der ZODB herausholte, indem alle Objekte in der Datenbank aufgeweckt wurden. Durch die Benutzung des Katalogs (siehe Kapitel 10) wurde die Performance viel besser.</li>
<li>Eine Abfrage, die alle Datensätze einer Datenbank holt, dann aber nur jeweils 100 auf einer Seite anzeigt und die anderen 99900 wegwirft. Das wurde mit effizienteren SQL-Anweisungen gelöst.</li>
</ul>
<p>Bevor Sie voreilige Schlussfolgerungen über die Ursache des Problems ziehen, lohnt es sich, Laufzeitmessungen auf der Site vorzunehmen, um festzustellen, wo das Nadelöhr liegt.</p>
</div>
</div>
<div class="section" id="laufzeitmessungen-mit-plone">
<h4>Laufzeitmessungen mit Plone</h4>
<p>Da Sie die Zeit, die zum Erzeugen von Seiten benötigt wird, quantitativ bestimmen können, können Sie nun versuchen zu optimieren. Das erste Problem besteht allerdings darin, die zu optimierende Stelle zu finden. Wie in früheren Kapiteln gezeigt wurde, ist die Hauptseite eine Sammlung von Templates, Skripten und anderem Code, der zusammengesetzt wird, um eine Seite zu erstellen. Wo sollte ein Entwickler in diesem Berg von Code nachschauen? Um bei der Suche nach Performance-Schwachstellen zu helfen, können Sie so genannte Profiler für Laufzeitmessungen von Aufrufen, Page Templates und Python selbst benutzen. Jeder dieser Profiler kümmert sich um ein Element der Site und berichtet, wie viel Zeit beim Erstellen der Seite und bei jeder dieser drei Komponenten verbraucht wurde.</p>
<p>Beachten Sie bitte, dass Sie beim Aktivieren aller drei Werkzeuge feststellen werden, dass Ihre Plone-Site wirklich langsam wird (und zwar um eine signifikante Größenordnung). Jedes dieser Werkzeuge bringt einen Verlust an Performance mit sich. Sie sollten deren Installation immer rückgängig machen oder sie deaktivieren, nachdem Sie sie benutzt haben, um sicherzugehen, dass Ihre Site mit maximaler Effizienz läuft. Außerdem gilt beim gleichzeitigen Einsatz aller Profiler, dass Sie dann auch die Laufzeit dieser Profiler mitmessen, was dann wirklich verwirrend wird. Ich empfehle Ihnen, mit dem Aufruf-Profiler zu beginnen. Schalten Sie dann so lange jeweils einen der anderen Profiler ein, nachdem Sie den vorherigen deaktiviert haben, bis Sie genügend Informationen haben.</p>
<div class="section" id="call-profiler">
<h5>Call Profiler</h5>
<p>Dieses Zope-Produkt nimmt eine Anfrage, z.B. nach der Hauptseite, und gibt an, welche Objekte bei ihrer Abarbeitung benutzt wurden und wie viel Zeit jedes davon benötigt hat. Den Call Profiler finden Sie unter <a class="reference external" href="http://zope.org/Members/richard/CallProfiler">http://zope.org/Members/richard/CallProfiler</a>. Trotz der Kommentare auf der Seite ist das Produkt nicht in Zope 2.6 integriert. Installieren Sie das Produkt auf die übliche Weise, und starten Sie dann Ihr Zope neu.</p>
<p>Um den Call Profiler zu aktivieren, gehen Sie im ZMI zum Control Panel und wählen <em>Call Profiler</em>. Das Produkt funktioniert derart, dass es Hooks in ein Objekt installiert, damit beim Zugriff auf das Objekt die Zeit für die Wiedergabe des Objekts gemessen werden kann. Das heißt, dass Call Profiler nur bei den Objekten aktiviert wird, die Sie überwachen möchten, wie in Abbildung 14.3 zu sehen ist. Bei einer normalen Plone-Installation müssen Sie das Filesystem Script (Python)- und das Filesystem-Page Template überwachen. Der Call Profiler merkt sich diese Einstellungen nicht nach einem Neustart von Zope, d.h., dass bei einem einfachen Neustart die Hooks deaktiviert werden und Sie bereit für die Auslieferung sind.</p>
<a class="reference external image-reference" href="img/14-03.png/image_view_fullscreen"><img alt="img/14-03.png" class="original" src="img/14-03.png" /></a>
<p>Figure 14.3. Call Profiler mit ausgewählten Dateisystem-Hooks</p>
<p>Nachdem die zu überwachenden Objekte ausgewählt wurden, greifen Sie auf die gewünschte URL zu. Am einfachsten greift man auf die zu testende URL zu, indem man das zuvor erwähnte Werkzeug <em>ab</em> ausführt, aber einen Webbrowser zu benutzen funktioniert genauso gut. Wenn Sie in diesem Fall die Hauptseite auf <em>localhost</em> messen, starten Sie Folgendes:</p>
<pre class="literal-block">
ab -n 20 -c 4 http://localhost/
</pre>
<p>Hierdurch werden 20 Anfragen an Plone gesendet. Sobald sie abgearbeitet sind, können Sie auf die Messungen dieser Anfragen zugreifen. Zurück in der Schnittstelle von Call Profiler sehen Sie oben drei Reiter: <em>Results</em>, <em>Results by URL</em> und <em>Aggregates</em>. Da mehrere Anfragen ausgeführt wurden, wählen Sie den <em>Aggregates</em>-Reiter, der am leichtesten zu verstehen ist. In der Liste der aufgerufenen Seiten wird die getestete URL sein. Klicken Sie auf diesen Link, um die Ergebnisse zu dieser URL zu sehen. Nun sollten Sie etwas wie in Abbildung 14.4 sehen.</p>
<a class="reference external image-reference" href="img/14-04.png/image_view_fullscreen"><img alt="img/14-04.png" class="original" src="img/14-04.png" /></a>
<p>Abbildung 14.4. Ergebnisse der Laufzeitmessung</p>
<p>In diesem Beispiel sehen Sie die Elemente, die der Call Profiler erkennen kann. Leider sind die Ergebnisse manchmal etwas kompliziert zu entziffern. Auf den ersten Blick addieren sich die Ergebnisse zu mehr als 100 %. In diesem Fall nimmt <tt class="docutils literal">document_view</tt> 71,1 % der verbrauchten Zeit ein. Das ist allerdings irreführend, weil sich die Werte unter dieser Abbildung auf <tt class="docutils literal">document_view</tt> und nicht auf die ganze Seite beziehen. In diesem Beispiel für die gesamte Seite nimmt alles vor <tt class="docutils literal">browserDefault</tt> 19,9 % der Anfrage in Anspruch. Dann geht sie zu <tt class="docutils literal">document_view</tt> über, wofür Sie auch den Prozentanteil sehen können. In diesem Fall macht der Übergang von <tt class="docutils literal">toLocalizedTime</tt> nach <tt class="docutils literal">getPreviousMonth</tt> 23,3 % der Zeit zur Darstellung von <tt class="docutils literal">document_view</tt> aus.</p>
</div>
<div class="section" id="page-template-profiler">
<h5>Page Template Profiler</h5>
<p>Der Page Template Profiler funktioniert nur mit dem Page Templates-System von Zope. Ähnlich wie der Call Profiler gibt er an, wie viel Zeit bei jedem Aufruf in einem Page Template verbraucht wird. Da Sie im vorigen Beispiel gesehen haben, dass die meiste Zeit in einem Page Template verbraucht wird (<tt class="docutils literal">document_view</tt>), finden Sie es vermutlich interessant zu wissen, wo die Zeit in diesem Template genau verbraucht wird.</p>
<p>Den Page Template Profiler finden Sie unter <a class="reference external" href="http://zope.org/Members/guido_w/PTProfiler">http://zope.org/Members/guido_w/PTProfiler</a>. Installieren Sie das Produkt, und starten Sie dann Zope neu. Um die Installation rückgängig zu machen, müssen Sie  den Page Template Profiler aus Ihrem <tt class="docutils literal">Products</tt>-Verzeichnis entfernen, wenn Sie mit der Messung fertig sind.</p>
<p>Gehen Sie nach der Installation im ZMI zur Zope-Wurzel, und wählen Sie <em>PT Profile Viewer</em> im Dropdown-Menü <em>Add</em>. Füllen Sie das <em>Erstellen</em>-Formular aus, indem Sie als ID einen eindeutigen Wert eingeben (geben Sie z.B. <em>PTProfiler</em> ein), und klicken Sie dann auf <em>Add</em>. Wiederholen Sie nun den Aufruf der zu messenden Seite, indem Sie das Werkzeug <em>ab</em> starten oder in einem Browser auf die Seite zugreifen. Greifen Sie auf das gerade hinzugefügte Page Template Profiler-Objekt zu, und Sie werden ein Ergebnis für die gerade ausgeführte Anfrage sehen. Klicken Sie darauf, um weitere Details zu sehen (vergleiche Abbildung 14.5).</p>
<a class="reference external image-reference" href="img/14-05.png/image_view_fullscreen"><img alt="img/14-05.png" class="original" src="img/14-05.png" /></a>
<p>Abbildung 14.5. Ergebnisse des Page Template Profilers</p>
<p>In diesem Fall sehen Sie, dass auf meiner Site <tt class="docutils literal">calendarBox</tt> jedes Mal 0,7321 Sekunden benötigt, wenn es aufgerufen wird. Da die gesamte Seite 1,9 Sekunden benötigt, können Sie davon ausgehen, dass dies ein Bereich ist, den ich optimieren könnte.</p>
</div>
<div class="section" id="python-profiler">
<h5>Python Profiler</h5>
<p>Der Python Profiler bietet sehr fein aufgelöste Messwerte und wird normalerweise bei der komplexeren Fehlersuche im darunter liegenden Code verwendet. Er liefert einen detaillierten Bericht der Zeiten, die in verschiedenen Bereichen des Python-Code verbracht werden. Bei den Messungen einer Site würden Sie so etwas normalerweise nicht benutzen. Aber der Vollständigkeit halber möchte ich es in diesem Abschnitt beschreiben.</p>
<p>Um den Python Profiler zu aktivieren, müssen Sie eine Variable zur Konfigurationsdatei hinzufügen. Schalten Sie in der Datei <tt class="docutils literal">zope.conf</tt> Ihres <tt class="docutils literal">etc</tt>-Verzeichnisses den Befehl <tt class="docutils literal"><span class="pre">publisher-profile-file</span></tt> ein. Dazu definieren Sie eine Datei, in die er schreiben wird. Unter Windows könnte das <tt class="docutils literal"><span class="pre">C:\zope.output</span></tt> sein, unter Linux ist es <tt class="docutils literal">/tmp/zope.output</tt>. Fügen Sie die folgende Zeile unter Linux hinzu:</p>
<pre class="literal-block">
publisher-profile-file /tmp/zope.output
</pre>
<p>Dann starten Sie Plone neu, das dann sehr langsam laufen wird. Wenn Sie eine große Anzahl von Anfragen abarbeiten und die Ergebnisse untersuchen möchten, dann enthält die in der Umgebungsvariable angegebene Datei die Ausgabe der Daten. Rufen Sie, wie in den vorherigen Beispielen, die zu messende Seite mit dem Werkzeug <em>ab</em> oder einem Webbrowser auf. Dann gehen Sie im ZMI zum Control Panel, wählen <em>Debug Info</em> und wählen dann den <em>Profiling</em>-Reiter. Sie erhalten dann eine Ausgabe des Python Profilers, wie sie in Abbildung 14.6 zu sehen ist.</p>
<a class="reference external image-reference" href="img/14-06.png/image_view_fullscreen"><img alt="img/14-06.png" class="original" src="img/14-06.png" /></a>
<p>Abbildung 14.6. Ergebnisse des Python Profilers</p>
<p>Wie Sie in Abbildung 14.6 sehen können, zeigt die Ausgabe Details von allem, was Zeit verbraucht. Ich musste das nur selten benutzen.</p>
</div>
</div>
<div class="section" id="einfache-optimierungstricks">
<h4>Einfache Optimierungstricks</h4>
<p>Nach einem sehr detaillierten Blick auf Plone hat das Plone-Entwicklerteam die folgenden Optimierungstricks gefunden.</p>
<div class="section" id="die-namenssuche-einschranken">
<h5>Die Namenssuche einschränken</h5>
<p>Die übertriebene Suche nach Namen ist ein häufiger Fehler, dessen Lösung in der Definition einer lokalen Variable besteht. Im folgenden Beispiel muss Plone bei jeder Wiederholung der Schleife eine Suche nach <tt class="docutils literal">portal_url</tt> durchführen:</p>
<pre class="literal-block">
&lt;tal:block
 tal:repeat=&quot;result here/portal_catalog&quot;&gt;
   &lt;a href=&quot;&quot;
      tal:attributes=&quot;href here/portal_url/getPortalUrl&quot;&gt;Home&lt;/a&gt;
   ...
&lt;/tal:block&gt;
</pre>
<p>Dabei wäre es schneller, ein <tt class="docutils literal">tal:define</tt> wie folgt zu benutzen:</p>
<pre class="literal-block">
&lt;tal:block
 tal:repeat=&quot;result here/portal_catalog&quot;
 tal:define=&quot;url here/portal_url/getPortalUrl&gt;
    &lt;a href=&quot;&quot;
       tal:attributes=&quot;href url&quot;&gt;Home&lt;/a&gt;
       ...
&lt;/tal:block&gt;
</pre>
<p>Wie schon gesagt, Plone bietet eine große Zahl von globalen Defines. Durch die Verwendung dieser Definitionen kann ein Entwickler die Anzahl der Traversierungen reduzieren. Eine vollständige Liste all dieser Defines finden Sie in Anhang A.</p>
</div>
<div class="section" id="sicherheitsuberprufungen-und-traversierung">
<h5>Sicherheitsüberprüfungen und Traversierung</h5>
<p>Immer, wenn auf ein Objekt, ein Objektattribut oder eine Methode eines Objekts zugegriffen wird, wird eine Sicherheitsüberprüfung durchgeführt. Auch, wenn jede einzelne nicht so kostspielig ist, können sich viele Sicherheitsüberprüfungen schnell aufaddieren.</p>
<p>Das gilt besonders dann, wenn Sie ein Objekt traversieren, z.B. in <tt class="docutils literal">here/ordnerA/ordnerB/objekt</tt>. In diesem Fall führt Zope Sicherheitsüberprüfungen auf allen einzelnen Ordnern und dann auf dem Objekt durch. Wenn auf die Information jedes Mal ohne diese Traversierung zugegriffen werden kann, werden Sie einen Performance-Gewinn feststellen. Eine andere Methode, Sicherheitsüberprüfungen zu vermeiden, besteht darin, Code im Dateisystem unter <tt class="docutils literal">Products</tt> zu schreiben. Der Code unter <tt class="docutils literal">Products</tt> wird als vertrauenswürdiger Code betrachtet, wird weniger geprüft und daher schneller ausgeführt.</p>
</div>
<div class="section" id="der-zcatalog">
<h5>Der ZCatalog</h5>
<p>Der ZCatalog ist ein effizienter Binärbaum mit Daten über Objekte. Sie sollten ihn (in den meisten Situationen) dann benutzen, wenn Sie eine Liste von Objekten erstellen, z.B. Suchergebnisse, bei Zusammenfassungen, beim Finden von Objekten usw. Wenn der Katalog eine Reihe von Ergebnissen zurückgibt, die auf eine Liste von leichtgewichtigen Objekten (so genannten Brains) zugreifen, findet beim Zugriff auf diese Brains weder eine Traversierung noch eine Sicherheitsüberprüfung auf dem Objekt statt.</p>
</div>
<div class="section" id="zu-viele-features">
<h5>Zu viele Features</h5>
<p>Das mag offensichtlich erscheinen, aber Plone wird mit einer Menge von Features ausgeliefert, die Sie nicht unbedingt benötigen. So benötigen z.B. sowohl das Kalender- als auch das Navigations-Portlet beide große Ressourcen, haben aber einen allgemein eher beschränkten Nutzen. Wenn Sie diese Features nicht benötigen und sie abschalten, steigt dadurch die Performance.</p>
</div>
<div class="section" id="lohnt-sich-die-optimierung">
<h5>Lohnt sich die Optimierung?</h5>
<p>Bevor Sie irgendeine Optimierung durchführen, sollten Sie eine einfache Kosten/Nutzen-Analyse erstellen, um zu sehen, ob es sich lohnt, die Optimierung vorzunehmen.</p>
<p>Angenommen, Sie haben z.B. eine Seite, die in 0,5 Sekunden erstellt wird. Auf dieser Seite verbraucht ein Skript 10 % dieser Zeit. Wenn Sie dieses eine Skript doppelt so schnell ausführen können, gewinnen Sie dadurch 0,025 Sekunden bei dieser Seite. In diesem Fall ist der Nutzen einer solchen Optimierung gering, weil es einige fixe Kosten gibt, z.B. die eines Entwicklers, der die Analyse durchführt, die Kosten beim Testen dieser Optimierung und möglicherweise die einer Änderung der Dokumentation.</p>
<p>Diese Arbeiten durchzuführen bedeutet auch ein substanzielles Risiko. Beim Ändern von Code können neue Fehler in der Anwendung entstehen. Mit Hilfe von Methoden wie agiler Programmierung könnten diese aber minimiert werden. Außerdem schafft es ein Programmierer vielleicht nicht, die Anwendung schneller zu machen, oder macht sie sogar langsamer.</p>
<p>Es gibt auch Alternativen zur Optimierung des Codes, z.B. könnten Sie mehr Hauptspeicher oder Hardware installieren, falls die Anwendung durch einen dieser Faktoren beschränkt wird. Auch wenn viele Programmierer der Meinung sind, dass es faul ist, Probleme durch mehr Hardware zu lösen, kann das eine sehr kostengünstige Lösung sein. Neue Hardware stellt ein geringes Risiko dar, kann einen hohen Geschwindigkeitszuwachs bringen und kostet oftmals weniger als ein Programmierer.</p>
<p>Außerdem können Sie Ihren Server wirklich durch Caching oder das Hinzufügen weiterer Rechner skalieren, auf die Sie die Last verteilen. Diese Techniken werden im Rest dieses Kapitels behandelt.</p>
</div>
</div>
<div class="section" id="inhalte-cachen">
<h4>Inhalte cachen</h4>
<p>Nun, da Sie die langsamen Teile Ihrer Anwendung gefunden haben, werden Sie das wichtigste Werkzeug zur Steigerung der Performance kennen lernen: das Caching. Unter Caching versteht man die Zwischenspeicherung von Daten, die wieder benutzt werden sollen, ohne sie neu zu berechnen. Es gibt viele, viele Arten von Cache-Speichern und sie können auf vielerlei Weisen benutzt werden.</p>
<p>Mit dem Begriff Caching meine ich zwei Dinge, die man cachen kann: Inhalte und Skins. Inhalte sind Daten, die der Benutzer in Inhaltstypen eingegeben hat. Skins beziehen sich auf alles in <tt class="docutils literal">portal_skins</tt> und können Templates, Skripten, Bilder oder Dateien sein. Diese zwei Dinge werden im Cache unterschiedlich gespeichert.</p>
<p>Ich denke beim Caching gern in Begriffen, die mit dem Grad an Einflussmöglichkeit zu tun haben, die ich über den Caching-Mechanismus habe. Mit anderen Worten: Je näher das Caching beim Client durchgeführt wird, desto schneller wird die Antwort sein, aber desto weniger Einfluss habe ich auch auf diesen Cache. Das beinhaltet auch die Möglichkeit, dass es überhaupt keinen Cache-Speicher gibt. Abbildung 14.7 illustriert das Caching zwischen einem Client und einem Server.</p>
<a class="reference external image-reference" href="img/14-07.png/image_view_fullscreen"><img alt="img/14-07.png" class="original" src="img/14-07.png" /></a>
<p>Abbildung 14.7. Cache-Speicher zwischen einem Client und einem Server</p>
<p>Der Browser-Cache des Benutzers ist der schnellste Platz, an dem man etwas cachen kann, aber Sie können nicht wissen, ob ein Benutzer in seinem Browser das Caching überhaupt eingeschaltet hat. Danach kommen die zwischengelagerten Cache-Speicher von Proxy-Servern. Denken Sie daran, dass das Ihr Proxy-Server sein könnte (der unter Ihrer Kontrolle sein sollte) oder der Proxy eines Internet Service Providers (ISP). Und schließlich gibt es die Möglichkeit von Cache-Speichern auf dem Server.</p>
<p>In den folgenden Abschnitten erörtere ich die folgenden Caching-Mechanismen:</p>
<ul class="simple">
<li>Caching von Skin-Elementen mit dem <em>Accelerated HTTP Cache Manager</em></li>
<li>Caching von Code mit dem <em>RAM Cache Manager</em></li>
<li>Caching von benutzererstellten Inhalten mit dem <em>Caching Policy Manager</em></li>
</ul>
<p>Dann werde ich erläutern, wie man Apache und Squid benutzt, zwei gern verwendete externe Server, die über eine Menge an Konfigurationsoptionen für eine hohe Performance verfügen.</p>
</div>
<div class="section" id="skins-cachen">
<h4>Skins cachen</h4>
<p>Im Hypertext Transfer Protocol (HTTP) können Sie HTTP-Header für das Caching setzen. Wenn eine Antwort mit solchen Headern zurückgegeben wird, ist es die Aufgabe der Proxies zwischen dem Client und dem Server, das Objekt gemäß dieser Header zu cachen. In Abbildung 14.7 könnte das ein beliebiger Cache-Speicher ab dem Server-Cache oder darunter sein. Dieser Proxy kann ein Webserver sein, über den Sie auf dem Server verfügen, z.B. Apache, oder ein Proxy im Einflussbereich des ISPs. Wie ich noch zeigen werde, ergibt das ein mächtiges Werkzeug, wenn es mit Apache oder Squid kombiniert wird.</p>
<p>Zu diesem Caching kann auch der Browser beitragen, wenn er so eingestellt ist, dass er Caching verwendet (beim Internet Explorer ist das standardmäßig der Fall). Wenn ein Browser allerdings eine Seite aktualisiert, sendet er den Header <tt class="docutils literal">Pragma: <span class="pre">no-cache</span></tt>, was auch die Proxies zwingt, ihre Kopie erneut zu laden.</p>
<p>Diese Art von Caching betrifft die gesamte Antwort, d.h., es kann riskant sein, wenn Sie versuchen, das auf eine ganze Seite anzuwenden. Am meisten wird das mit Bildern, Stylesheets, JavaScript oder Seiten benutzt, die sich nicht oft verändern. Bilder, die auf Ihren Seiten oft wiederholt werden, um hübsche Elemente zu erzeugen, z.B. abgerundete Ecken oder Hintergrundbilder, sind hierfür ideal.</p>
<p>Plone erzeugt standardmäßig einen Accelerated HTTP Cache Manager namens <em>HTTPCache</em> in der Wurzel Ihrer Plone-Site. Beim Zugriff auf dieses Objekt über das ZMI erscheinen die Management-Optionen für den Cache-Speicher. Das Folgende sind alles vernünftige Voreinstellungen, und zu Beginn muss nichts verändert werden:</p>
<ul class="simple">
<li><strong>Title</strong>: Der Titel des Cache Managers, optional.</li>
<li><strong>Interval</strong>: Die Zeit (in Sekunden), die das Objekt im Cache bleibt.</li>
<li><strong>Cache anonymous connections only</strong>: Hiermit wird nur bei anonymen Benutzern gecacht.</li>
<li><strong>Notify URLs</strong>: Das sind URLs von Proxies zum Herunterladen, die geleert werden müssen, wenn sich das Objekt verändert. Weitere Details dazu finden Sie im Abschnitt &quot;Squid verwenden&quot;.</li>
</ul>
<p>Um zu sehen, wie der Accelerated HTTP Cache Manager funktioniert, basiert das folgende Beispiel auf einem Testobjekt, einem Bild namens <tt class="docutils literal">test.gif</tt>. Um zu sehen, welche Header zurückgegeben werden, müssen Sie diese Header testen. Dazu können Sie ein einfaches Python-Skript namens <tt class="docutils literal">header.py</tt> benutzen. Dieses Skript finden Sie in Anhang B. Unter Linux macht das Programm <em>wget</em> das Gleiche, wenn Sie die Option <tt class="docutils literal"><span class="pre">-S</span></tt> angeben, obwohl es die Datei immer noch für Sie herunterlädt. Beispiel:</p>
<pre class="literal-block">
wget -S http://www.agmweb.ca
</pre>
<p>Es folgen zuerst einmal die Header, die für <tt class="docutils literal">test.gif</tt> zurückgegeben werden, <em>bevor</em> Sie das zum Cache-Manager hinzugefügt haben:</p>
<pre class="literal-block">
[andy&#64;basil scripts]$ ./header.py http://localhost:8080/test.gif GET
Accept-Ranges: bytes
Connection: close
Content-Length: 2541
Content-Type: image/gif
Date: Wed, 03 Sep 2003 23:55:38 GMT
Etag:
Last-Modified: Wed, 03 Sep 2003 23:54:27 GMT
Server: Zope/(unreleased version, python 2.2.2, linux2) ZServer/1.1
</pre>
<p>Nach dem Hinzufügen des Bildes zum Cache prüfen Sie die HTTP-Header mit dem Skript erneut. Sie werden zwei neue Header finden. Beispiel:</p>
<pre class="literal-block">
[andy&#64;basil scripts]$ ./header.py http://localhost:8080/test.gif GET
...
Cache-Control: max-age=3600
Expires: Thu, 04 Sep 2003 00:56:03 GMT on 2.2.2, linux2) Zserver/1.1
</pre>
<div class="admonition-hinweis admonition">
<p class="first admonition-title">Hinweis</p>
<p class="last">Leider hält sich Zope 2 nicht an das RFC (Request for Comments) für <tt class="docutils literal">HEAD</tt>-Anfragen. Anstatt bei einer <tt class="docutils literal">HEAD</tt>-Anfrage den vollen Satz an Headern zu schicken, fehlen die Werte vom Cache Manager. Beim Testen sollten Sie immer <tt class="docutils literal">GET</tt>-Anfragen schicken.</p>
</div>
<p>Weitere Informationen zu den HTTP-Headern und den Zusammenhang mit dem Caching finden Sie im RFC 2616 unter <a class="reference external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a>.</p>
<p>Der Accelerated HTTP Cache Manager speichert eine ganze Antwort im Cache, was mit statischen Elementen gut funktioniert. Eine normale Plone-Seite besteht aber aus personalisierten Elementen, z.B. dem Kalender, der persönlichen Navigationsleiste usw. In dieser Situation müssen Sie nur einen Teil einer Seite cachen können, und hier kommt der RAM Cache Manager ins Spiel.</p>
<p>Der RAM Cache Manager speichert die Ausgabe eines Objekts im RAM, d.h., dass es beim nächsten Auftreten dieses Skripts aus dem Cache geholt wird. Wiederholte Aufrufe dieses Objekts führen dazu, dass die Ausgabe so lange aus dem Cache geholt wird, bis der Cache abläuft. Der springende Punkt bei diesem Manager ist der, dass Sie es vermeiden, jedes Mal komplizierte oder umfangreiche Berechnungen durchzuführen. Stattdessen speichern Sie das Ergebnis und benutzen es erneut. Dieser Cache Manager speichert keine Bilder oder Dateien. Er hält die Benutzer nicht vom Versuch ab, den Cache dahingehend zu konfigurieren, aber er hat keinen Effekt auf diese Objekte.</p>
<p>Plone erstellt standardmäßig einen RAM Manager namens <em>RAMCache</em> in der Wurzel Ihrer Plone-Site. Beim Zugriff auf dieses Objekt über das ZMI werden die Management-Optionen für den Cache aufgerufen. Die folgenden Standardwerte sind alle vernünftig, und zu Beginn muss nichts geändert werden:</p>
<ul class="simple">
<li><strong>Title</strong>: Der Titel des Cache Managers, optional.</li>
<li><strong>REQUEST variables</strong>: Variablen, die die Cache-Bedingung ausmachen. Das ist eine mächtige Option, die die Bedingungen für das Zwischenspeichern von Benutzervariablen darstellt. Wenn z.B. ein zu cachendes Element verlangt, dass es für jeden Benutzer anders oder in verschiedenen Sprachen gecacht werden soll, können Sie hier die REQUEST-Variablen angeben, die Sie cachen möchten.</li>
<li><strong>Threshold entries</strong>: Die maximale Anzahl von Einträgen, die im Cache gespeichert werden kann. Verringern Sie diesen Wert, wenn der Cache zu viel RAM verbraucht.</li>
<li><strong>Maximum age of a cache entry</strong>: Die Zeit (in Sekunden), die das Objekt im Cache bleibt.</li>
<li><strong>Cleanup interval</strong>: Gibt an, wie oft der Cache gesäubert wird.</li>
</ul>
<p>Da die Anfragen nach dem Objekt bis zu Zope durchkommen, reduziert sich der Netzwerkverkehr dadurch keineswegs. Zope stellt das Ergebnis lediglich schneller dar. Nach der Wahl des <em>Statistics</em>-Reiters im ZMI wird eine Statistik über die genaue vom Cache zurückgegebene Trefferanzahl ausgegeben und darüber, wie viele Zugriffe an das Objekt weitergegeben wurden. Wenn zu viele Zugriffe ans Objekt weitergereicht werden, möchten Sie eventuell die Cache-Konfiguration ändern und weniger <tt class="docutils literal">REQUEST</tt>-Variablen angeben oder die Verweilzeit im Cache erhöhen.</p>
</div>
<div class="section" id="caches-zuweisen">
<h4>Caches zuweisen</h4>
<p>Um ein Objekt im Dateisystem zum Cache hinzuzufügen, geben Sie einfach für dieses Objekt den Namen des Caches in der <tt class="docutils literal">.metadata</tt>-Datei an (<tt class="docutils literal">.metadata</tt>-Dateien wurden in Kapitel 7 erläutert). Plone macht das schon für eine Reihe von Bildern, bei CSS und JavaScript. Die Datei <tt class="docutils literal">plone_skins /plone_images/pdf_icon.gif.metadata</tt> lautet z.B. wie folgt:</p>
<pre class="literal-block">
[default]
title=Pdf icon
cache=HTTPCache
</pre>
<p>Das bedeutet, dass das Bild mit <em>HTTPCache</em> gecacht wird. Die meisten Objekte im Dateisystem eignen sich mehr für den <em>HTTPCache</em> als für den <em>RAMCache</em>.</p>
</div>
<div class="section" id="inhaltstypen-cachen">
<h4>Inhaltstypen cachen</h4>
<p>Das Cachen von Inhaltstypen ist ein wenig schwieriger und verlangt den Einsatz des <em>Caching Policy Managers</em>. Plone installiert dieses Werkzeug standardmäßig, und Sie finden es in der Wurzel der Plone-Instanz unter der ID <tt class="docutils literal">caching_policy_manager</tt>.</p>
<p>Bevor Sie irgendeinen Inhalt cachen können, müssen Sie die Cache-Einstellungen für die Templates in Plone ändern. Per Voreinstellung gibt Plone Header für Inhalte aus, die das Caching völlig ausschalten. Wenn Sie Folgendes nicht machen, funktioniert der Rest dieses Abschnitts nicht. Wenn Sie <em>portal_skins</em> und dann <em>plone_templates</em> anklicken, finden Sie das Page Template <tt class="docutils literal">global_cache_settings</tt>. Es wird auf allen Seiten benutzt, die das Plone-Haupt-Template benutzen. Dieses Template sieht zurzeit wie folgt aus:</p>
<pre class="literal-block">
&lt;metal:cacheheaders define-macro=&quot;cacheheaders&quot;&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
'Content-Type', 'text/html;;charset=%s' % charset)&quot; /&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
('Content-Language', lang)&quot; /&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
('Expires', 'Sat, 1 Jan 2000 00:00:00 GMT')&quot; /&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
('Pragma', 'no-cache')&quot; /&gt;
&lt;/metal:cacheheaders&gt;
</pre>
<p>Das bedeutet, das nichts gecacht wird, weil die HTTP-Header <tt class="docutils literal">Pragma: <span class="pre">no-cache</span></tt> und <tt class="docutils literal">Expires</tt> gesetzt wurden. Um das auszuschalten und um sicherzustellen, dass Sie etwas Bestimmtes cachen können, passen Sie dieses Template an und entfernen die Direktiven <tt class="docutils literal">Pragma</tt> und <tt class="docutils literal">Expires</tt>. Ihr Template sollte nun wie folgt aussehen:</p>
<pre class="literal-block">
&lt;metal:cacheheaders define-macro=&quot;cacheheaders&quot;&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
'Content-Type', 'text/html;;charset=%s' % charset)&quot; /&gt;
    &lt;metal:block tal:define=&quot;dummy python:request.RESPONSE.setHeader
('Content-Language', lang)&quot; /&gt;
&lt;/metal:cacheheaders&gt;
</pre>
<p>Nach diesem Schritt können Sie weiterhin bestimmte Dinge mit dem <tt class="docutils literal">caching_policy_manager</tt> gezielt cachen. Gehen Sie im ZMI zu diesem Werkzeug, und Sie werden die folgenden Optionen sehen:</p>
<ul class="simple">
<li><strong>Policy ID</strong>: Eine eindeutige ID für eine Policy, wird nur intern benutzt.</li>
<li><strong>Predicate</strong>: Ein TALES-Ausdruck zum Filtern der Inhalte. Die Variable <tt class="docutils literal">content</tt> enthält das gerade dargestellte Objekt.</li>
<li><strong>Mod. Time</strong>: Ein TALES-Ausdruck, der ausgewertet wird und einen Wert aus dem Objekt für die Berechnung des Änderungszeitpunkts zurückgibt. Die Variable <tt class="docutils literal">content</tt> ist das gerade dargestellte Objekt.</li>
<li><strong>Max age (secs)</strong>: Gibt an, wie lange der Cache-Header dafür gesetzt werden soll.</li>
<li><strong>Vary</strong>: Variiert den zu sendenden Header (mehr darüber erfahren Sie später im Abschnitt &quot;Squid verwenden&quot;).</li>
<li><strong>No-cache</strong>: Sendet den HTTP-Header <tt class="docutils literal"><span class="pre">no-cache</span></tt>.</li>
<li><strong>No-store</strong>: Sendet den HTTP-Header <tt class="docutils literal"><span class="pre">no-store</span></tt>.</li>
<li><strong>Must-revalidate</strong>: Sendet den HTTP-Header <tt class="docutils literal"><span class="pre">must-revalidate</span></tt>.</li>
</ul>
<p>Das Folgende ist eine Beispiel-Policy, die alle Bilder auf der Site cachen würde:</p>
<ul class="simple">
<li>Policy ID: <tt class="docutils literal">Images</tt></li>
<li>Predicate: <tt class="docutils literal"><span class="pre">python:content.portal_type=='Image'</span></tt></li>
<li>Max age (secs): <tt class="docutils literal">3600</tt></li>
</ul>
<p>Lassen Sie alle anderen Felder leer, und wählen Sie <em>Add</em>, um diese Policy hinzuzufügen. Der <tt class="docutils literal">caching_policy_manager</tt> sieht nun ungefähr wie in Abbildung 14.8 aus.</p>
<a class="reference external image-reference" href="img/14-08.png/image_view_fullscreen"><img alt="img/14-08.png" class="original" src="img/14-08.png" /></a>
<p>Abbildung 14.8. Der <tt class="docutils literal">caching_policy_manager</tt> mit hinzugefügter Bilder-Policy</p>
<p>Um das richtig zu testen, müssen Sie über die Plone-Schnittstelle ein Bild zu Ihrer Site hinzufügen. Die Bilder werden mit den passenden Headern gesendet, wenn Sie die Aktion <em>Anzeigen des Objekts</em> aufrufen. Ich hoffe, dass man diese Aktion in späteren Versionen dieses Werkzeugs konfigurieren kann. Sie werden das mit <tt class="docutils literal">test.gif</tt> testen, einem Bild, das von einem Site-Mitglied über die Plone-Schnittstelle hinzugefügt wurde, und zwar wie folgt:</p>
<pre class="literal-block">
~/header.py http://localhost/test.gif/view GET
Cache-Control: max-age=3600
Connection: close
Content-Language:
Content-Length: 19810
Content-Type: text/html;charset=utf-8
Date: Fri, 05 Sep 2003 18:42:44 GMT
Etag:
Expires: Fri, 05 Sep 2003 19:42:44 GMT
Last-Modified: Fri, 05 Sep 2003 18:33:41 GMT
Pragma: no-cache
Server: Zope/(unreleased version, python 2.2.2, linux2) ZServer/1.1
</pre>
<p>Wie erwartet werden nun die Header <tt class="docutils literal"><span class="pre">Last-Modified</span></tt> und <tt class="docutils literal">Expires</tt> gesendet. Durch die Änderung von Prädikaten und das Hinzufügen mehrerer Policies können Sie ein recht ausgefeiltes Caching-System erstellen. Kompliziertere Regeln können Sie natürlich an ein Script (Python)-Objekt abgeben, wenn Sie das wünschen. Wenn das Prädikat z.B. wie folgt lautet:</p>
<pre class="literal-block">
python: here.myCachingRules(content)
</pre>
<p>dann fügen Sie ein Script (Python)-Objekt namens <tt class="docutils literal">myCachingRules</tt> hinzu, um diese Regeln zu berechnen. Beispiel:</p>
<pre class="literal-block">
##parameters=content
# cache all files, images and anything
# thats published
if content.portal_type in ['File', 'Image']:
    return 1
if content.review_state in ['published',]:
    return 1
</pre>
<p>In diesem Skript cachen Sie alle Dateien und Bilder sowie alles, was im Zustand <em>Veröffentlicht</em> ist, indem Sie die HTTP-Header über den Caching Policy Manager setzen.</p>
</div>
<div class="section" id="beispiel-caching-auf-zopezen-org">
<h4>Beispiel: Caching auf ZopeZen.org</h4>
<p>Bei der Entwicklung der Site <a class="reference external" href="http://www.zopezen.org">http://www.zopezen.org</a> gab es ein Hauptproblem. Die Erstellung der Hauptseite von ZopeZen, auf der die Nachrichten und die Anzahl der Antworten aufgelistet sind, ist sehr aufwendig. In Plone gibt es keinen einfachen Weg, um aus dem Katalog die Anzahl der Diskussionsbeiträge zu einem Element zu berechnen.</p>
<p>Das ist eine ideale Situation für den RAM Cache Manager. Da der Verkehr durch neue Elemente oder Nachrichten ziemlich gering ist (vielleicht ein oder zwei pro Tag), erscheint die Annahme vernünftig, dass sich die Hauptseite während einer Zeitdauer von 30 Minuten nicht stark verändert. Die Funktion, die die Nachrichten und Antworten holt, heißt <tt class="docutils literal">getNewsAndReplies</tt>, und sie erledigt die Aufgabe, alle für das Template <tt class="docutils literal">index_html</tt> benötigte Daten zu holen.</p>
<p>Das Template <tt class="docutils literal">index_html</tt> verfügt über benutzerspezifische Elemente. Der Anmeldekasten links zeigt z.B. den Benutzern, welche Optionen sie haben. Das heißt, dass es nicht funktionieren würde, einen Accelerated HTTP Cache Manager zu benutzen oder das ganze Template mit dem RAM Cache Manager zu cachen. Das würde dazu führen, dass die Benutzer die Optionen anderer Benutzer sehen könnten.</p>
<p>Die ZopeZen-Skin cacht stattdessen das Script (Python)-Objekt <tt class="docutils literal">getNewsAndReplies</tt>, indem sie dieses zum RAM Cache Manager hinzufügt. Dadurch wird garantiert, dass der Großteil der aufwendigen Arbeit der Seitendarstellung gecacht wird. Da die einzelnen Nachrichten für alle Benutzer gleich sind, macht ein Cachen auf Basis der <tt class="docutils literal">REQUEST</tt>-Variablen keinen Sinn. Daher wurde <tt class="docutils literal">AUTHENTICATED_USER</tt> aus der Liste der <tt class="docutils literal">REQUEST</tt>-Variablen für den Cache entfernt. Eine Laufzeitmessung auf der Hauptseite ergibt ohne Cache 1,06 Anfragen pro Sekunde, während mit Cache 4,96 Anfragen pro Sekunde bearbeitet werden, das ist ein signifikanter Unterschied für eine kleine Änderung.</p>
</div>
<div class="section" id="cache-server-verwenden">
<h4>Cache-Server verwenden</h4>
<p>Nun, da Sie Cache-Header nach ausgefeilten Regeln senden können, können Sie jetzt einen anderen Server benutzen, um Anfragen an Plone zu cachen. So schnell Zope auch ist, es kann nie schneller als Apache, Squid oder IIS sein, wenn es um die Ausgabe von Inhalten geht. Diese Server können statischen und gecachten Inhalt einfach und schnell ausgeben. Zum Teil liegt das daran, dass diese Server in C geschrieben sind, aber auch daran, dass sie bei jeder Anfrage weniger Arbeit haben. So gibt es keine Sicherheitsüberprüfungen, Datenbankabfragen oder Verhandlungen über die zu verwendende Sprache. Und als Sie Kapitel 10 gelesen haben, haben Sie außerdem schon einen Proxy-Server installiert.</p>
<div class="section" id="apache-verwenden">
<h5>Apache verwenden</h5>
<p>Apache ist der Open-Source-Standard-Webserver. Die folgenden Abschnitte beschreiben Techniken für Apache 2.0 Server unter Linux. Mit nur geringen Änderungen an der Syntax funktionieren die meisten dieser Tipps auch unter Apache 1.3. Weitere Informationen zu verschiedenen Apache-Servern und -Plattformen finden Sie in der exzellenten Apache-Dokumentation unter <a class="reference external" href="http://www.apache.org">http://www.apache.org</a>.</p>
<p><strong>Inhalte komprimieren</strong></p>
<p>Die Möglichkeit, Ihre Seiten zu komprimieren, spart Bandbreite. Bevor eine Seite vom Server gesendet wird, wird sie schnell noch komprimiert, um vom Client wieder dekomprimiert zu werden. Damit lassen sich Seiten schneller herunterladen, und es fallen beim Besitzer der Site weniger Kosten wegen der verwendeten Bandbreite an, da die Dateien kleiner sind. Dazu aktivieren Sie zuerst das Modul <em>mod_deflate</em>. Das hängt von Ihren genauen Einstellungen ab. Unter Linux machen Sie z.B. Folgendes:</p>
<pre class="literal-block">
LoadModule cache_module modules/mod_deflate
</pre>
<p>Als Zweites fügen Sie Folgendes zu Ihrer Server-Konfiguration hinzu, um Texte in Hypertext Markup Language (HTML), Extensible Markup Language (XML) und einfache Texte zu dekomprimieren:</p>
<pre class="literal-block">
AddOutputFilterByType DEFLATE text/html text/xml text/plain
</pre>
<p>Manche Clients behandeln die Dekomprimierung etwas anders, d.h., es lohnt sich, die Dokumentation zu <em>mod_deflate</em> zu lesen, um detailliertere Beispiele zu sehen (<a class="reference external" href="http://httpd.apache.org/docs-2.0/mod/mod_deflate.html">http://httpd.apache.org/docs-2.0/mod/mod_deflate.html</a>).</p>
<p><strong>Ablauf-Header setzen</strong></p>
<p>In den vorangegangenen Abschnitten haben Sie gesehen, wie Sie Ablauf-Header senden können, indem Sie Plone-Werkzeuge manipulieren. Mit Apache kann man diese Header ebenfalls leicht mit der Direktive <tt class="docutils literal">ExpiresActive</tt> senden. Das ist eine Alternative zur Verwendung der verschiedenen Plone-Werkzeuge. Um z.B. für alle Bilder die Ablauf-Header auf 24 Stunden ab jetzt zu setzen, können Sie Folgendes zu Ihrer Apache-Site-Konfiguration hinzufügen:</p>
<pre class="literal-block">
ExpiresActive On
ExpiresByType image/gif &quot;access plus 1 day&quot;
ExpiresByType image/png &quot;access plus 1 day&quot;
ExpiresByType image/jpeg &quot;access plus 1 day&quot;
</pre>
<p>Weitere Informationen zu <em>mod_expires</em> finden Sie unter <a class="reference external" href="http://httpd.apache.org/docs-2.1/mod/mod_expires.html">http://httpd.apache.org/docs-2.1/mod/mod_expires.html</a>.</p>
<p><strong>Caching in Apache</strong></p>
<p>Apache enthält mehrere Systeme, die Caching-Aufgaben für Sie erledigen können. Das Apache-Standardmodul <em>mod_cache</em> hat zwei Caching-Modi: <em>Memory</em> und <em>Disk</em>. Dabei werden alle Seitenanfragen gemäß eines Satzes von Parametern für eine bestimmte Zeit gecacht. Um einen Cache auf der Platte im Ordner <tt class="docutils literal">/tmp/apache_cache</tt> einzurichten, fügen Sie Folgendes zur Site-Konfiguration hinzu:</p>
<pre class="literal-block">
CacheRoot /tmp/apache_cache
CacheEnable disk /
CacheSize 256
CacheDirLevels 5
CacheDirLength 3
</pre>
<p>Leider kann es etwas schwierig sein, den Beweis zu erbringen, dass Apache den Inhalt wirklich im Cache speichert. Der einfachste Ansatz ist vielleicht der, es so zu testen, indem <tt class="docutils literal">z2.log</tt> in Plone überwacht wird, um zu sehen, ob es getroffen wird. Weitere Informationen zu <em>mod_cache</em> finden Sie unter <a class="reference external" href="http://httpd.apache.org/docs-2.0/mod/mod_cache.html">http://httpd.apache.org/docs-2.0/mod/mod_cache.html</a>.</p>
</div>
<div class="section" id="squid-verwenden">
<h5>Squid verwenden</h5>
<p>Squid ist ein Open-Source-Proxy-Server, der sehr oft zusammen mit Zope benutzt wird. Damit können Sie Zope beschleunigen, indem Sie Inhalte cachen, die in Squid erstellt werden, damit mehrere Anfragen von Squid und nicht von Zope bearbeitet werden. Auch hier gilt, dass Squid keine dynamischen Inhalte darstellt und in C geschrieben ist, d.h., es antwortet wesentlich schneller. In Kapitel 10 habe ich die Installation von Squid und dessen Einsatz als Proxy beschrieben. Wenn Sie Squid zur Beschleunigung von Plone benutzen möchten, lesen Sie bitte in diesem Kapitel die Angaben zur Einrichtung von Squid als Proxy-Server nach.</p>
<p>Wie Sie weiter oben in diesem Kapitel gesehen haben, können Sie mit dem Caching Policy Manager und dem Accelerated HTTP Cache Manager fast beliebige Informationen in HTTP-Headern unterbringen. Squid fungiert nun in ähnlicher Weise als Browser-Cache. Wenn eine Anfrage nach einer Seite kommt und diese Cache-Header vorhanden sind, wird Squid die Seite cachen. Wiederholte Treffer führen dazu, dass Squid und nicht Plone die Seite zurückgibt.</p>
<p>Ob eine Seite gecacht wurde, kann man relativ einfach sagen, da Squid den Header <tt class="docutils literal"><span class="pre">X-Cache</span></tt> zur Antwort hinzufügt. Mit dem Skript <tt class="docutils literal">header.py</tt> können Sie sehen, ob die Seite erfolgreich gecacht wurde. Ein <tt class="docutils literal">HIT</tt> bedeutet, dass Squid eine Kopie im Cache gefunden und zurückgegeben hat. Wenn keine Kopie gefunden und Plone gefragt wurde, wird ein <tt class="docutils literal">MISS</tt> zurückgegeben. Beispiel:</p>
<pre class="literal-block">
X-Cache: HIT from http://www.agmweb.ca
</pre>
<p>Beim Testen in der Entwicklungsumgebung zeigt Squid beeindruckende Zahlen und beschleunigt die Anzeige einer Plone-Seite im Cache von etwa zwei Anfragen pro Sekunde auf mehr als 25 pro Sekunde. Benutzer haben berichtet, dass auf schnellen Servern relativ leicht Werte von 200 Anfragen pro Sekunde erreicht werden.</p>
<p><strong>Squid-Caches säubern</strong></p>
<p>Wenn ein Benutzer ein Objekt bearbeitet, ändert es sich in Plone. Da dieses Objekt aber in einem früheren Zustand gecacht ist, enthält der Cache eine alte Version. Benutzer, die auf die Site zugreifen, erhalten dann eine alte Version und nicht die neue. Wenn Sie die Caches beeinflussen können (wie bei Squid), können Sie <tt class="docutils literal">PURGE</tt>-Befehle an den Caching-Server schicken, damit er Objekte aus dem Cache entfernt.</p>
<p>Beim <em>Accelerated HTTP Cache Manager</em> fügen Sie die URLs der Caches zu <tt class="docutils literal">Notify URLs (via PURGE)</tt> hinzu. Hier ein Beispiel dafür:</p>
<pre class="literal-block">
http://192.168.1.1:80/example.org
</pre>
<p>In diesem Beispiel ist die IP-Adresse die Adresse des Caches, und die Domain ist die zu löschende Site. Damit Squid die <tt class="docutils literal">PURGE</tt>-Direktive ausführt, müssen Sie sicherstellen, dass Squid konfiguriert ist. Falls Squid auf <em>localhost</em> läuft, sähe das wie folgt aus:</p>
<pre class="literal-block">
acl PURGE method purge
http_access allow localhost
http_access allow purge localhost
http_access deny purge
http_access deny all
</pre>
<p>Der Caching Policy Manager verfügt im Moment über keinen <em>PURGE</em>-Mechanismus, obwohl Sie ein Script (Python)-Objekt zum Workflow hinzufügen könnten, um das zu erreichen. Sie könnten den Python-Code in Listing 14.2 als externe Methode speichern und sie bei Bedarf im Workflow ausführen.</p>
<p>Listing 14.2. Ein Skript zum Löschen des Squid-Caches</p>
<pre class="literal-block">
import urllib
import urlparse
import httplib

URLs = [
    # enter the URLs you would like
    # to purge here
    'http://localhost:8080',
]

def purge(objectURL):
    for url in URLs:
        if not url:
            continue
        assert url[:4] == 'http', &quot;No protocol specified&quot;

        url = urlparse.urljoin(url, objectURL)
        parsed = urlparse.urlparse(url)
        host = parsed[1]
        path = parsed[2]

        h = httplib.HTTP(host)
        h.putrequest('PURGE', path)
        h.endheaders()
        errcode, errmsg, headers = h.getreply()
        h.getfile.read()

if __name__ == '__main__':
    print purge('/')
</pre>
<p>Das Collective-Projekt enthält ein neues Werkzeug namens <tt class="docutils literal">CMFSquidTool</tt>, das Ihnen diese Arbeit abnimmt. Es beobachtet Änderungen am Inhalt, und wenn es eine entdeckt, sendet es für Sie ein Purge an den Squid-Cache. Ich habe es noch nicht ausprobiert, aber Sie sollten sich dieses Werkzeug definitiv anschauen, wenn Sie mit Squid arbeiten.</p>
<p><strong>Säuberungen in Squid-Caches vermeiden</strong></p>
<p>Säuberungen in Caches vermeidet man am besten durch selektiveres Caching. Sowohl der Caching Policy Manager als auch der RAM Cache Manager bieten Möglichkeiten zur selektiven Angabe dessen, was vom Cache zurückgegeben werden soll.</p>
<p><strong>Vary</strong></p>
<p>Der Caching Policy Manager und Squid unterstützen beide den <tt class="docutils literal">Vary</tt>-Tag. Falls ein <tt class="docutils literal">Vary</tt>-Tag angegeben wird, extrahiert Squid aus der Anfrage die im <tt class="docutils literal">Vary</tt>-Tag angegebenen Header. Diese werden dann mit dem Cache verglichen. Wenn sie übereinstimmen, wird die Seite aus dem Cache zurückgegeben. Ansonsten wird die Anfrage an Plone weitergegeben.</p>
<p>Wenn der <tt class="docutils literal">Vary</tt>-Tag im Caching Policy Manager z.B. den Wert <tt class="docutils literal"><span class="pre">Accept-Language</span></tt> hat und eine Anfrage an Squid kommt, wird die Seite gemäß der Einstellung <tt class="docutils literal"><span class="pre">Accept-Language</span></tt> in diesem Anfrage-Header gecacht. Wenn ein Benutzer eine Seite mit einer anderen Einstellung verlangt, wird eine neue Seite zurückgegeben. Das heißt, Sie können die Seiten nach Sprachen cachen.</p>
<p>Der am wenigsten aggressive Wert von <tt class="docutils literal">Vary</tt> ist <tt class="docutils literal">*</tt>, bei dem alle Anfragen gecacht werden, die mit anderen Anfragen identisch sind. Unterschiedliche Anfragen werden direkt an Plone weitergegeben. Auch wenn dies das am wenigsten  aggressive Caching-System ist, garantiert es doch, dass der Benutzer nur aktuelle Inhalte sieht.</p>
<p><strong>REQUEST-Methoden</strong></p>
<p>Die <tt class="docutils literal">REQUEST</tt>-Methoden des RAM Cache Managers verfolgen das gleiche Konzept wie <tt class="docutils literal">Vary</tt>, außer dass das Werkzeug eine Liste von Zope-Anfragevariablen akzeptiert. Das Ergebnis einer Suche im Cache basiert dann auf diesen Variablen. Der voreingestellte Wert lautet <tt class="docutils literal">AUTHENTICATED_USER</tt>, d.h., dass alle authentifizierten Benutzer ihre eigene Version des Caches sehen. Nicht registrierte (anonyme) Benutzer sehen alle den gleichen Inhalt.</p>
</div>
</div>
</div>
<div class="section" id="zope-enterprise-objects-verwenden">
<h3>Zope Enterprise Objects verwenden</h3>
<p>Das letzte Wort bei der Skalierung und Administration von Plone ist es, ZEO (Zope Enterprise Objects) zu benutzen. Das ist ein wesentliches Werkzeug in sehr vielen Bereichen von Plone, sowohl für die Entwicklung als auch für die Produktion. Viele Leute denken, es sollte zu den Standardeinrichtungen von Plone gehören, und vielleicht wird es das bald. Im Moment wird unter Linux ZEO mit Plone ausgeliefert, aber nicht installiert. Es funktioniert auch unter Windows, wird dort aber nicht vollständig mit Diensten oder einer einfachen Installation unterstützt.</p>
<p>In einer normalen Plone-Installation gibt es eine Instanz von Plone, die mit einer Instanz der ZODB kommuniziert. Während diese eine Plone-Instanz auf die ZODB zugreift, wird diese gesperrt und kein anderer Prozess kann darauf zugreifen. Das beschränkt die Skalierbarkeit der Site und führt eine isolierte Schwachstelle ein. In der Welt relationaler Datenbanken wäre das gleichbedeutend damit, dass nur ein Prozess auf Ihre Datenbank zugreifen könnte.</p>
<p>ZEO bricht die Abhängigkeit auf und isoliert den Zugriff auf die ZODB (auch <em>ZEO-Server</em> genannt), damit mehrere Prozesse (auch <em>ZEO-Clients</em> genannt) sich mit der ZODB verbinden können, wie in Abbildung 14.9 zu sehen ist. In der Terminologie relationaler Datenbanken ist das äquivalent mit einem Prozess, der die Verbindung zur Datenbank herstellt.</p>
<a class="reference external image-reference" href="img/14-09.png/image_view_fullscreen"><img alt="img/14-09.png" class="original" src="img/14-09.png" /></a>
<p>Abbildung 14.9. Eine Standardkonfiguration von ZEO</p>
<p>Da sich mehrere Prozesse mit einer ZODB verbinden können, haben Sie nun die Möglichkeit, mit mehreren Kopien von Plone zu arbeiten. Im Wesentlichen könnten Sie nun zwei oder drei Plone-Instanzen mit dem gleichen Inhalt haben. Das heißt, Sie können nicht nur die Last Ihrer Site auf viele Computer verteilen, sondern können nun mit einem Programm eine Verbindung zu Ihrer Site herstellen und aufwendige Aufgaben auf anderen Computern ausführen oder Ihre Site zur Laufzeit auf Python-Ebene untersuchen.</p>
<p>Und schließlich ist ein auch nicht ganz unwesentlicher Punkt der, dass die Zeiten für einen Neustart eines ZEO-Clients sehr kurz sind. Der Aufwand beim Laden der Datenbanken entfällt, d.h., Sie können Plone-Sites schnell neu starten.</p>
<div class="section" id="installation-von-zeo">
<h4>Installation von ZEO</h4>
<p>ZEO ist in Zope 2.7 enthalten, der Zope-Version, die von diesem Buch unterstützt wird. In früheren Zope-Versionen war es separat verfügbar. Im Moment gibt es keine einfache Möglichkeit, ZEO unter Windows zu installieren. Das Skript <tt class="docutils literal">mkzeoinstance</tt> funktioniert nicht. ZEO selbst funktioniert, wie es soll, aber Sie müssen die ZEO-Quellen lesen, um zu sehen, wie man das anstellt. Außerdem funktioniert <tt class="docutils literal">zopectl</tt> nicht unter Windows, d.h., die folgenden Beispiele funktionieren dort ebenso wenig.</p>
<div class="section" id="linux">
<h5>Linux</h5>
<p>Um einen ZEO-Server zu erstellen, benutzen Sie das Skript <tt class="docutils literal">mkzeoinstance</tt> im Verzeichnis <tt class="docutils literal"><span class="pre">/opt/Zope-2.7/bin</span></tt>. Das setzt voraus, dass Zope bereits wie in Kapitel 2 beschrieben installiert ist. Das Skript erwartet folgende Parameter:</p>
<ul class="simple">
<li>Directory: Das Verzeichnis, in dem die ZEO-Server-Instanz erzeugt werden soll.</li>
<li>Host: Der Host und Port, an dem der Server reagieren sollte, im Format <tt class="docutils literal">host:port</tt>. Der Port ist jener Port, mit dem sich ZEO-Clients verbinden, und sollte durch eine Firewall geschützt sein, da ZEO keine Sicherheit gegen unautorisierten Zugriff bietet. Parameter sind optional. Der voreingestellte Port ist 9999.</li>
<li>User und Password: Der vorgegebene Standardbenutzer und sein Passwort für den Server im Format <tt class="docutils literal">benutzer:passwort</tt>. Ist optional.</li>
</ul>
<p>Folgendes z.B. installiert ZEO unter <tt class="docutils literal">/var/zeo</tt> auf dem Standard-Port:</p>
<pre class="literal-block">
cd /opt/Zope-2.7/bin
./mkzeoinstance /var/zeo
</pre>
<p>Hierdurch wird eine neue Datenbank mit der entsprechenden Konfiguration erstellt. Diese Datenbank ist an einem neuen Ort, aber das ist in Ordnung. Wenn Sie eine existierende Zope-Installation mit ZEO aufrüsten wollen, dann müssen Sie das laufende Zope anhalten und dann die Datenbank von Ihrer alten Installation in das neue ZEO-Verzeichnis verschieben. In meinem Fall heißt das, die Datei <tt class="docutils literal">Data.fs</tt> von <tt class="docutils literal">/var/zope/var</tt> nach <tt class="docutils literal">/var/zeo/var</tt> zu verschieben.</p>
<p>Als Nächstes müssen Sie die Konfiguration Ihrer Zope-Instanz ändern. Öffnen Sie dazu <tt class="docutils literal">zope.conf</tt> in <tt class="docutils literal">etc</tt>, und geben Sie folgende Information ein:</p>
<pre class="literal-block">
# ZEO client storage:
#
&lt;zodb_db main&gt;
   mount-point /
   &lt;zeoclient&gt;
     server localhost:9999
     storage 1
     name zeostorage
     var $INSTANCE/var
   &lt;/zeoclient&gt;
&lt;/zodb_db&gt;
</pre>
<p>Im obigen Code geben Sie den Port und den Server an, auf dem sich der ZEO-Server befindet. Außerdem müssen Sie die vorhandene Abbildung auf die lokale Datenbank auskommentieren. Das sollte etwa wie folgt aussehen:</p>
<pre class="literal-block">
#&lt;zodb_db main&gt;
#    # Main FileStorage database
#    &lt;filestorage&gt;
#      path $INSTANCE/var/Data.fs
#    &lt;/filestorage&gt;
#    mount-point /
#&lt;/zodb_db&gt;
</pre>
<p>Um zu testen, ob das funktioniert, starten Sie zuerst den ZEO-Server. Dazu sind eventuell mehr Rechte notwendig, als der Benutzer hat, unter dem Sie ihn installiert haben:</p>
<pre class="literal-block">
$ cd /var/zeo/bin
$ ./zeoctl start
daemon process started, pid=29316
</pre>
<p>Der ZEO-Daemon wurde erfolgreich gestartet. Starten Sie nun einen Zope-Client, und versuchen Sie, sich z.B. wie folgt damit zu verbinden:</p>
<pre class="literal-block">
$ cd /var/zope/bin
$ ./zopectl start
daemon process started, pid=29338
</pre>
<p>Das macht alles einen guten Eindruck, und Sie können nun wie gewohnt auf Ihr Plone zugreifen.</p>
</div>
</div>
<div class="section" id="zeo-clients-benutzen">
<h4>ZEO-Clients benutzen</h4>
<p>In dieser Konfiguration wird über den ZEO-Server auf die ZODB zugegriffen, und jede Zope-Instanz ist ein ZEO-Client. Mehrere ZEO-Clients können mit dem Server eine Verbindung aufbauen. Client und Server müssen keineswegs auf dem gleichen Rechner laufen, solange der Client eine Verbindung zum Server herstellen kann. Falls die Clients auf dem gleichen Computer sind, muss sich jeder Client an einen anderen HTTP- und FTP-Port binden, um Konflikte untereinander zu vermeiden.</p>
<p>Wenn Ihr Client startet, verbindet er sich mit dem Speicher, der in Ihrer Konfiguration angegeben ist, statt mit dem lokalen Standardspeicher. Eine häufige Anforderung ist die, einen zusätzlichen Rechner zu erlauben, auf dem rechenintensive Aufgaben laufen, z.B. eine Aktualisierung des Katalogs, eine Komprimierung der Datenbank oder eine Durchführung komplexer Abfragen, ohne dass die Performance der anderen Clients sinkt. Mit der Funktion <tt class="docutils literal">zopectl</tt> lässt sich das recht einfach machen:</p>
<pre class="literal-block">
$ cd /var/zope/bin
$ ./zopectl debug
Starting debugger (the name &quot;app&quot; is bound to the top-level Zope object)
</pre>
<p>Um die Datenbank zu komprimieren, würden Sie dann Folgendes machen:</p>
<pre class="literal-block">
&gt;&gt;&gt; app.Control_Panel.Database.manage_pack(days=0)
</pre>
<p>Weil Sie einen ZEO-Client ausführen, müssen Sie dem Server sagen, dass eine Änderung vorgenommen wurde und die Caches aktualisiert wurden. Schließen Sie die Transaktion wie folgt ab:</p>
<pre class="literal-block">
&gt;&gt;&gt; get_transaction().commit()
&gt;&gt;&gt; app._p_jar.close()
</pre>
<p>Das empfiehlt sich besonders dann, wenn Sie eine hochperformante Site betreiben und die Datenbank komprimieren müssen. Die Site wird ein wenig langsamer laufen, wenn die Transaktion beendet wird, aber der größte Teil der schweren Arbeit erfolgt auf dem Client, der die Komprimierung durchführt. Das könnte ein von Ihrer Site völlig verschiedener Rechner sein. Daher ist das eine hervorragende Methode, die Last zu verteilen.</p>
<p>Bei der Fehlersuche ist es extrem hilfreich, zu diesem Prompt zu gelangen, da Sie dort die Objekte in diesem <tt class="docutils literal">app</tt>-Objekt untersuchen können. Sie werden feststellen, dass sie mit den Objekten übereinstimmen, die Sie im ZMI sehen. Beispiel:</p>
<pre class="literal-block">
&gt;&gt;&gt; app.objectIds()
['acl_users', 'Control_Panel', 'temp_folder',...
</pre>
<p>Wie lautet die API für dieses <tt class="docutils literal">app</tt>-Objekt? Sie können die eingebaute Python-Funktion <tt class="docutils literal">dir</tt> benutzen, um das Objekt zu untersuchen und sogar die Methode <tt class="docutils literal">__doc__</tt>, um die darin enthaltenen Kommentar-Strings wie folgt zu sehen:</p>
<pre class="literal-block">
$dir(app)
&gt;&gt;&gt; dir(app)
['COPY', 'COPY__roles__', 'Control_Panel', 'DELETE',...
&gt;&gt;&gt; app.valid_roles.__doc__
'Return list of valid roles'
</pre>
<p>Ein gutes Beispiel einer ZEO-basierten Anwendung ist <em>CMFNewsFeed</em> (<a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>). Sie stellt eine Verbindung zu Plone mit einem ZEO-Client her. Dieser separate Client legt dann los und sammelt alle Nachrichten, die er finden kann, und fügt die Daten in die Site ein. Dadurch, dass alles Sammeln und Katalogisieren in einem separaten Prozess läuft, wird garantiert, dass die Performance der Haupt-Site hoch bleibt.</p>
<p>Für Entwickler ist ZEO ein unverzichtbares Werkzeug. Damit können Sie durch ein Programm mit Ihrem Server interagieren, während er läuft. Wenn Sie als erfahrener Programmierer an dieser Stelle immer noch verwirrt sein sollten, was Plone und die Objektdatenbank angeht, dann öffnet Ihnen ZEO normalerweise die Augen.</p>
</div>
<div class="section" id="lastverteilung-und-ausfallsicherung">
<h4>Lastverteilung und Ausfallsicherung</h4>
<p>Obwohl ZEO die Möglichkeit bietet, Plone auf vielen Servern zu betreiben, hat es für den Benutzer nichts zu bieten, womit er eine Lastverteilung vornehmen könnte. Unter <em>Lastverteilung</em> (engl. <em>load balancing</em>) versteht man, dass hereinkommende Anfragen auf verschiedene Server gesendet werden, um die Last der Seitengenerierung zu verteilen. Ausgefeilte Werkzeuge testen, ob der Server überhaupt läuft, bevor sie ihm eine Anfrage schicken.</p>
<p>Bei der Verteilung der Last haben Sie Hardware- und Software-Möglichkeiten. Squid z.B. beherrscht die dynamische Ausfallsicherung (engl. <em>failover</em>). <em>Pound</em> ist ein Beispiel für ein Lastverteilungssystem, das Sie unter <a class="reference external" href="http://www.apsis.ch/pound/index.html">http://www.apsis.ch/pound/index.html</a> finden.</p>
<p>Squid kennt das <em>Internet Cache Protocol</em> (ICP), ein Protokoll, mit dem es prüfen kann, ob eine Plone-Site läuft, bevor Anfragen an sie weitergereicht werden. Auf hochgradig dynamischen Sites ist das eventuell ein Muss. Weitere Informationen zu ICP und Zope finden Sie unter <a class="reference external" href="http://www.zope.org/Members/htrd/icp/intro">http://www.zope.org/Members/htrd/icp/intro</a>.</p>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appA.rst">
    <title>Anhang A: Wichtige Konfigurationen und einige APIs</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appA.rst</link>
    <description>Dieser Anhang enthält einige der wichtigsten Konfigurationsmöglichkeiten bei der Entwicklung mit Zope, Plone und Python. Er enthält Informationen für Site-Entwickler und listet auch einige der nützlichsten APIs (Application Programming Interfaces) auf.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Wichtige Konfigurationen und einige APIs</h2>
<p>Dieser Anhang enthält einige der wichtigsten Konfigurationsmöglichkeiten bei der Entwicklung mit Zope, Plone und Python. Er enthält Informationen für Site-Entwickler und listet auch einige der nützlichsten APIs (Application Programming Interfaces) auf.</p>
<div class="section" id="einrichten-ihrer-umgebung">
<h3>Einrichten Ihrer Umgebung</h3>
<p>In den folgenden Abschnitten finden Sie Informationen über die Konfiguration Ihrer Entwicklungs- oder Produktionsumgebung, damit Sie diese optimal einrichten können. Wenn Sie viel mit Plone entwickeln, möchte ich Ihnen diese Einstellungen sehr empfehlen.</p>
<div class="section" id="pythonpath-einrichten">
<h4>PYTHONPATH einrichten</h4>
<p>Die Einrichtung von <tt class="docutils literal">PYTHONPATH</tt> ist sehr nützlich, weil Sie dadurch sehr leicht auf die gesamte Zope-Funktionalität vom Python-Prompt aus zugreifen können. Sie können ganz leicht herausfinden, ob Sie diese Einstellung bereits verwenden. Versuchen Sie einfach, das Modul <tt class="docutils literal">PageTemplate</tt> aus <tt class="docutils literal">Products</tt> zu importieren. Wenn das noch nicht eingerichtet ist, werden Sie folgenden Fehler sehen:</p>
<pre class="literal-block">
$ python -c &quot;import Products.PageTemplate&quot;
Traceback (most recent call last):
  File &quot;&lt;string&gt;&quot;, line 1, in ?
ImportError: No module named Products.PageTemplate
</pre>
<div class="section" id="unix-linux-und-mac-os-x">
<h5>Unix, Linux und Mac OS X</h5>
<p>Finden Sie zuerst das Produktverzeichnis Ihrer Zope-Installation (nicht die Instanzwurzel). Bei einer Standardinstallation liegt es unter <tt class="docutils literal"><span class="pre">/opt/Zope-2.7/lib/python</span></tt>. Unter Windows liegt es unter <tt class="docutils literal"><span class="pre">C:\Programme\Plone\Zope\lib\python</span></tt>. Wenn Python gestartet wird, liest es eine Umgebungsvariable namens <tt class="docutils literal">PYTHONPATH</tt>, aus der es alle dort angegebenen Verzeichnisse in seinen Suchpfad für neue Module übernimmt. Das heißt, Sie müssen Ihr Verzeichnis zu dieser Variablen hinzufügen.</p>
<p>Das machen Sie mit dem Befehl <tt class="docutils literal">export</tt>. Um also zu sehen, ob zu Beginn irgendetwas in <tt class="docutils literal">PYTHONPATH</tt> enthalten ist, führen Sie Folgendes aus:</p>
<pre class="literal-block">
$ export | grep PYTHONPATH
declare -x PYTHONPATH=&quot;/home/andy/modules&quot;
</pre>
<p>In meinem Fall habe ich bereits eine Umgebungsvariable namens <tt class="docutils literal">PYTHONPATH</tt>, aber auf Ihrem Rechner haben Sie diese Einstellung möglicherweise nicht. Bei mir enthält sie einen Pfad zu einigen internen Modulen. Nun müssen Sie also Zope 2 zu diesem Pfad hinzufügen, z.B. so:</p>
<pre class="literal-block">
$ export PYTHONPATH=&quot;/opt/Zope-2.7/lib/python:$PYTHONPATH&quot;
</pre>
<p>Ob das funktioniert hat, können Sie testen, indem Sie den folgenden Befehl wiederholen, wobei Sie darauf achten sollten, dass kein Fehler auftritt.</p>
<pre class="literal-block">
$ python -c &quot;import Products.PageTemplate&quot;
</pre>
</div>
<div class="section" id="windows">
<h5>Windows</h5>
<p>Unter Windows befindet sich das Zope-Produktverzeichnis in <tt class="docutils literal"><span class="pre">C:\Programme\Plone</span> 2\Zope\lib\python</tt>. Wenn Python gestartet wird, liest es eine Umgebungsvariable namens <tt class="docutils literal">PYTHONPATH</tt>, aus der es alle dort angegebenen Verzeichnisse in seinen Suchpfad für neue Module übernimmt. Das heißt, Sie müssen Ihr Verzeichnis zu dieser Variablen hinzufügen. Unter Windows fügen Sie eine Umgebungsvariable hinzu, indem Sie auf das Icon &quot;Arbeitsplatz&quot; rechtsklicken und <em>Eigenschaften</em> auswählen. Klicken Sie im Dialogfeld <em>Systemeigenschaften</em> auf den Reiter <em>Erweitert</em>, und klicken Sie dann unter <em>Systemvariablen</em> auf den Button <em>Neu</em>, wie in Abbildung A.1 zu sehen ist.</p>
<a class="reference external image-reference" href="img/A-01.png/image_view_fullscreen"><img alt="img/A-01.png" class="original" src="img/A-01.png" /></a>
<p>Abbildung A.1. Das Dialogfeld für die Umgebungsvariablen</p>
<p>Anschließend wird das Dialogfeld <em>Systemvariablen</em> geöffnet. Um eine Variable zu bearbeiten, wählen Sie eine aus der Liste und bearbeiten den Wert, wie in Abbildung A.2 gezeigt wird. Die Variable sollte <tt class="docutils literal">PYTHONPATH</tt> heißen, und ihr Wert sollte der Ort sein, wo sich Ihr Plone befindet, z.B. <tt class="docutils literal"><span class="pre">C:\Programme\Plone</span> 2\Zope\lib\python</tt>.</p>
<a class="reference external image-reference" href="img/A-02.png/image_view_fullscreen"><img alt="img/A-02.png" class="original" src="img/A-02.png" /></a>
<p>Abbildung A.2. Eine Variable hinzufügen</p>
<p>Nun sollten Sie die Import-Anweisung ganz normal ausführen können. Das können Sie von der Kommandozeile aus tun, oder Sie starten PythonWin und probieren dort den Import aus, wie in Abbildung A.3 demonstriert wird.</p>
<a class="reference external image-reference" href="img/A-03.png/image_view_fullscreen"><img alt="img/A-03.png" class="original" src="img/A-03.png" /></a>
<p>Abbildung A.3. Importieren in PythonWin</p>
<p>Ab jetzt können Sie nicht nur Produkte importieren, sondern Sie können auf der Kommandozeile auch <tt class="docutils literal">import Zope</tt> ausführen, was Voraussetzung für viele Skripten und Werkzeuge in den fortgeschritteneren Kapiteln dieses Buchs ist.</p>
</div>
</div>
<div class="section" id="ausfuhrung-von-unittests-einrichten">
<h4>Ausführung von Unittests einrichten</h4>
<p>Plone enthält einige hundert Unittests, die eine hervorragende Methode darstellen, um zu überprüfen, ob Ihr Plone korrekt funktioniert. Die Plone-Unittests finden Sie in <tt class="docutils literal">CMFPlone/tests</tt>. Wenn Sie mit Plone entwickeln, dann wäre es eine gute Idee, diese auf Ihrem Rechner einzurichten, damit Sie eigene Tests hinzufügen können. Um diese Tests auszuführen, benötigen Sie <tt class="docutils literal">ZopeTestCase</tt>. In Zukunft wird das vermutlich ein Teil von Plone werden, aber im Moment müssen Sie das noch unter <a class="reference external" href="http://zope.org/Members/shh/ZopeTestCase">http://zope.org/Members/shh/ZopeTestCase</a> herunterladen.</p>
<p>Die Installation ist etwas ungewöhnlich, Sie müssen das Paket erst auspacken und dann in das Verzeichnis <tt class="docutils literal">lib/Python/Testing</tt> des Zope-Wurzelverzeichnisses kopieren. Unter Unix liegt es normalerweise in <tt class="docutils literal"><span class="pre">opt/Zope-2.7</span></tt>, während es unter Windows in <tt class="docutils literal"><span class="pre">C:\Programme\Plone</span> 2\Zope</tt> liegt.</p>
<p>Als nächstes benötigen Sie ein Skript, das die Tests ausführt. Dazu schreibe ich normalerweise ein kleines Shell-Skript und kopiere es ins <tt class="docutils literal">bin</tt>-Verzeichnis meiner Plone-Instanz. Dann kann ich einfach dieses Shell-Skript starten, um die Tests auszuführen. Unter Unix, wo sich meine Plone-Instanz unter <tt class="docutils literal">/var/test</tt> befindet, sieht mein Skript wie folgt aus:</p>
<pre class="literal-block">
export SOFTWARE_HOME=/opt/Zope-2.7/lib/python
export INSTANCE_HOME=/var/test123

echo Testing CMFPlone...
cd $INSTANCE_HOME/Products/CMFPlone/tests
python2.3 runalltests.py
</pre>
<p>Unter Windows sieht das Äquivalent dazu so aus:</p>
<pre class="literal-block">
set SOFTWARE_HOME=C:\Programme\Plone 2\Zope
set INSTANCE_HOME=C:\Programme\Plone 2\Data

cd &quot;C:\Programme\Plone 2\Data\Products\CMFPlone\tests&quot;
&quot;C:\Programme\Plone 2\Python\python.exe&quot; runalltests.py
</pre>
<p>Die Ausgabe sollte ungefähr wie folgt aussehen:</p>
<pre class="literal-block">
[root&#64;basil bin]# ./testAll.sh
Testing CMFPlone...
SOFTWARE_HOME: /opt/Zope-2.7/lib/python
INSTANCE_HOME: /opt/Zope-2.7/lib/python/Testing
Loading Zope, please stand by ... done (7.899s)
Installing CMFCore ... done (1.363s)
Installing CMFDefault ... done (0.713s)
Und so weiter...
</pre>
<p>Das Unittest-Framework ist eine hervorragende Grundlage für die Durchführung von Unittests bei eigenen Produkten. Wenn Sie Produkte oder allgemein Software für Plone entwickeln, dann sollten Sie idealerweise Unittests dafür schreiben. Die in Plone enthaltenen Unittests sind ein exzellentes Beispiel dafür, wie Sie das machen können.</p>
</div>
<div class="section" id="die-zope-konfigurationsdatei">
<h4>Die Zope-Konfigurationsdatei</h4>
<p>Die Zope-Konfigurationsdatei ist in einem Format namens ZConfig geschrieben. Weitere Informationen über ZConfig finden Sie unter <a class="reference external" href="http://www.zope.org/Members/fdrake/zconfig">http://www.zope.org/Members/fdrake/zconfig</a>. Die Konfigurationsdatei hat den Namen <tt class="docutils literal">zope.conf</tt> und befindet sich im <tt class="docutils literal">etc</tt>-Verzeichnis Ihrer Zope-Installation. Manche Installationsprogramme erzeugen auch eine Datei namens <tt class="docutils literal">plone.conf</tt> mit Plone-spezifischen Angaben.</p>
<p>Als Plattform für alle Plone 2-Installationen wird Zope 2.7 empfohlen. In dieser Version von Zope kam diese Datei zum ersten Mal vor. Davor wurden Parameter über die Kommandozeile an Zope übergeben.</p>
<p>In dieser Datei fangen alle Kommentare mit einem <tt class="docutils literal">#</tt> an. In ZConfig definieren Sie Variablen, die später in dieser Konfigurationsdatei benutzt werden können. Dazu verwenden Sie eine Zeile im folgenden Format:</p>
<pre class="literal-block">
%define variablen-name variablen-wert
</pre>
<p>Folgende Zeilen sind Beispieldefinitionen aus meiner Konfigurationsdatei:</p>
<pre class="literal-block">
%define INSTANCE /var/test
%define ZOPE /opt/Zope-2.7
</pre>
<p>Diese Variablen benutzen Sie dann, indem Sie ihren Namen ein <tt class="docutils literal">$</tt> voranstellen, wie z.B. in <tt class="docutils literal">$INSTANCE</tt>.</p>
<p>Tabelle A.1 enthält die gesamte Zope-Konfiguration für Version 2.0.1. Die Spalte <em>Direktive</em> enthält den Variablennamen in der Konfigurationsdatei. Die Spalte <em>Beschreibung</em> erklärt, was die Direktive bewirkt, und die Spalte <em>Vorgabe</em> gibt an, was passiert, wenn kein Wert für diese Variable gesetzt ist. In der Spalte <em>Beispiel</em> habe ich ein paar Beispiele als zusätzliche Hilfe zur Erklärung angegeben.</p>
<p>Tabelle A.1. Direktiven in der Zope-Konfigurationsdatei</p>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
<col width="40%" />
<col width="14%" />
<col width="28%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Direktive</th>
<th class="head">Beschreibung</th>
<th class="head">Vorgabe</th>
<th class="head">Beispiel</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><em>instancehome</em></td>
<td>Der Pfad zu den Dateien mit Daten,
Produkten, dem Import-Verzeichnis und
dem <tt class="docutils literal">Extensions</tt>-Verzeichnis. Jede
Zope-Instanz sollte eine solche
Direktive haben.</td>
<td>None</td>
<td><em>/var/zope</em> oder
<em>$INSTANCE</em></td>
</tr>
<tr><td><em>client home</em></td>
<td>Das Verzeichnis, in dem die Dateien
zur Prozessidentifikation von Zope
liegen, z.B. die Prozess-ID-Datei
<tt class="docutils literal">Z2.pid</tt>. Von seinem Gebrauch wird
abgeraten. Siehe stattdessen
<tt class="docutils literal"><span class="pre">pid-filename</span></tt>.</td>
<td><em>INSTANCE/</em>
<em>var</em></td>
<td><em>$INSTANCE_HOME/var</em></td>
</tr>
<tr><td><em>path</em></td>
<td>Name eines Verzeichnisses, das am
Anfang von Pythons Modulsuchpfad
eingefügt werden soll. Diese Direktive
kann mehrfach verwendet werden. Da das
zu spät sein kann, sollte lieber, wie
vorher beschrieben, <tt class="docutils literal">PYTHONPATH</tt>
verändert werden.</td>
<td>None</td>
<td><em>path /home/pythonModules</em></td>
</tr>
<tr><td><em>products</em></td>
<td>Name eines Verzeichnisses, das weitere
Produkte enthält. Kann mehrfach
verwendet werden. Jedes identifizierte
Verzeichnis wird zum <tt class="docutils literal">__path__</tt> des
<tt class="docutils literal">Products</tt>-Pakets hinzugefügt. Ich
muss vom Gebrauch abraten, da
normalerweise ein <tt class="docutils literal">instance home</tt>
genügt. Es wurden auch schon Fehler in
Plone wegen dieser Direktive gemeldet.</td>
<td>None</td>
<td><em>products /home/chrism/</em>
<em>projects/myproducts</em></td>
</tr>
<tr><td><em>environment</em></td>
<td>Ein Abschnitt, in dem beliebige
Schlüssel/Wert-Paare als
Umgebungsvariablen definiert werden
können, während Zope läuft. Es
empfiehlt sich nicht, hier
Systemvariablen wie <tt class="docutils literal">PYTHONPATH</tt> zu
setzen. Die meisten Startup-Skripten
definieren für Sie solche Variablen.
Deswegen wird vom Gebrauch abgeraten.</td>
<td>None</td>
<td><em>&lt;environment&gt;</em>
<em>MY_PRODUCT_ENVVAR</em>
<em>foobar&lt;/environemnt&gt;</em></td>
</tr>
<tr><td><em>debug-mode</em></td>
<td>Ein Schalter, der verschiedene Bereiche
im Betrieb von Zope betrifft, die bei
der Entwicklung mit Zope nützlich sind.
Ich empfehle, alle Entwicklungsserver
in diesem Modus zu betreiben. Manche
Plone-Installationsprogramme schalten
das standardmäßig evtl. aus Gründen der
Performance aus.</td>
<td>On</td>
<td><em>debug-mode off</em></td>
</tr>
<tr><td><em>effective-user</em></td>
<td>Wenn Sie Zope unter dem Benutzer
<tt class="docutils literal">root</tt> betreiben möchten, muss diese
Direktive mit dem Namen oder der ID
eines effektiven Benutzers angegeben
werden, zu dem Zope mit <tt class="docutils literal">suid</tt>
umschaltet, nachdem die Server-Ports
belegt sind. Funktioniert nur unter
Unix und wenn Zope unter <tt class="docutils literal">root</tt>
gestartet wird. Sie können einen
Benutzer namens <tt class="docutils literal">zope</tt> erstellen und
den effektiven Benutzer auf <tt class="docutils literal">zope</tt>
setzen, damit Zope garantiert unter
diesem Benutzer läuft. Außerdem dürfen
Sie dann Ports zwischen 21 und 80
belegen.</td>
<td>None</td>
<td><em>effective-user zope</em></td>
</tr>
<tr><td><em>enable-product-</em>
<em>installation</em></td>
<td>Wenn diese Direktive eingeschaltet ist,
führt Zope eine Produkt-Installation
beim Hochfahren durch (eine
Registrierung von Python-Modulen in
verschiedenen Produktverzeichnissen).
Wenn man das ausschaltet, erfolgt das
Hochfahren von Zope/ZEO eventuell
schneller, kann aber auch dazu führen,
dass Ihre Produktliste im Control Panel
nicht mehr synchron mit dem Inhalt der
Produktverzeichnisse ist.</td>
<td>Ein</td>
<td><em>enable-product-</em>
<em>installation off</em></td>
</tr>
<tr><td><em>locale</em></td>
<td>Das unterstützt die
Internationalisierung, durch die Angabe
eines Namens für ein Locale. Schauen
Sie in die Dokumentation Ihres
Betriebssystems für spezifische
Informationen.</td>
<td>None</td>
<td><em>locale fr_FR</em></td>
</tr>
<tr><td><em>port-base</em></td>
<td>Der auf Port-Nummern angewendete
Offset bei der ZServer-Konfiguration.
Wenn der <tt class="docutils literal"><span class="pre">http-server</span></tt>-Port gleich
8080 und <tt class="docutils literal"><span class="pre">port-base</span></tt> gleich 1000 ist,
hört der HTTP-Server den Port 9080 ab.
Das ist dann gut, wenn Sie mehrere
Plone-Instanzen auf einem Rechner
betreiben und schnell alle Port-Nummern
ändern müssen.</td>
<td>0</td>
<td><em>1000</em></td>
</tr>
<tr><td><em>datetime-format</em></td>
<td>Setzt diese Variable entweder auf
<tt class="docutils literal">us</tt> oder <tt class="docutils literal">international</tt>, um das
<tt class="docutils literal">DateTime</tt>-Modul jeweils dazu zu
zwingen, Datumsstrings mit den Formaten
MM-DD-JJ bzw. DD-MM-JJ zu parsen.
Der Vorgabewert lautet <tt class="docutils literal">us</tt>.</td>
<td>us</td>
<td><em>datetime-format</em>
<em>international</em></td>
</tr>
<tr><td><em>zserver-threads</em></td>
<td>Gibt die Anzahl der Threads an, mit
denen Zopes Webserver ZServer Anfragen
bedient. Auf den meisten Sites führt
ein hoher Wert an dieser Stelle nicht
zu besserer Performance. Dazu verwende
man ZEO und Caching.</td>
<td>4</td>
<td><em>zserver-threads 10</em></td>
</tr>
<tr><td><em>python-check-</em>
<em>interval</em></td>
<td>Ein Integer für das &quot;Check-Intervall&quot;
des Python-Interpreters, das bestimmt,
wie oft der Interpreter periodische
Aufgaben durchführt, z.B. das
Umschalten von Threads und die
Signalbehandler. Auf den meisten Sites
führt ein hoher Wert an dieser Stelle
nicht zu besserer Performance.</td>
<td>500</td>
<td><em>python-check-</em>
<em>interval 1000</em></td>
</tr>
<tr><td><em>zserver-read-</em>
<em>only-mode</em></td>
<td>Wenn eingeschaltet, erzeugt Zope keine
Log- und Prozess-ID-(PID-)Dateien.
Zugriffs- und Fehler-Logdateien werden
auf die Standardausgabe ausgegeben.</td>
<td>off</td>
<td><em>zserver-read-only-mode on</em></td>
</tr>
<tr><td><em>pid-filename</em></td>
<td>Der Pfad der Datei, in der die PIDs
von Zope geschrieben werden. Der
Vorgabewert lautet
<tt class="docutils literal"><span class="pre">client-home/Z2.pid</span></tt>.</td>
<td>CLIENT_HOME/
Z2.pid</td>
<td><em>pid-filename /home/chrism/</em>
<em>projects/sessions/var/</em>
<em>Z2.pid</em></td>
</tr>
<tr><td><em>lock-filename</em></td>
<td>Der Pfad der &quot;Lock-Datei&quot;, die von
Zope im laufenden Betrieb gesperrt
wird.</td>
<td>CLIENT_HOME/
Z2.lock</td>
<td><em>lock-filename/home/chrism/</em>
<em>projects/sessions/var/</em>
<em>Z2.lock</em></td>
</tr>
<tr><td><em>mime-types</em></td>
<td>Hiermit erfährt Zope von weiteren
<tt class="docutils literal"><span class="pre">mime-types</span></tt>-Dateien, die es laden
soll. Die Dateien haben das gleiche
Format wie die von Apache. Diese
Einstellung darf mehrmals in einer
Konfigurationsdatei vorkommen.</td>
<td>None</td>
<td><em>mime-types $INSTANCE/etc</em>
<em>mime-types</em></td>
</tr>
<tr><td><em>structured-</em>
<em>text-header-</em>
<em>level</em></td>
<td>Setzt den Vorgabewert des HTML-Header-
Levels in Dokumenten in strukturiertem
Text. Standardwert ist 3, d.h., die
obersten Header werden mit dem Tag
<tt class="docutils literal">&lt;h3&gt;</tt> erzeugt. Leider ignoriert CMF
diesen Wert noch, was irgendwann noch
geändert werden muss.</td>
<td>3</td>
<td><em>structured-text-header-</em>
<em>lebvel 1</em></td>
</tr>
<tr><td><em>rest-input-</em>
<em>encoding</em></td>
<td>Gibt die Eingabekodierung von
Dokumenten in restrukturiertem Text an,
z.B. als <tt class="docutils literal"><span class="pre">utf-8</span></tt>, <tt class="docutils literal"><span class="pre">iso-8859-15</span></tt>
oder eine andere gültige und von Python
erkannte Kodierung. Der Vorgabewert
ist der Ihrer Python-Version.</td>
<td>System-
vorgabewert</td>
<td><em>rest-input-encoding</em>
<em>iso-8859-15</em></td>
</tr>
<tr><td><em>rest-output-</em>
<em>encoding</em></td>
<td>Gibt die Ausgabekodierung von
Dokumenten in restrukturiertem Text an,
z.B. <tt class="docutils literal"><span class="pre">utf-8</span></tt>, <tt class="docutils literal"><span class="pre">iso-8859-15</span></tt> oder
eine andere gültige und von Python
erkannte Kodierung. Der Vorgabewert
ist der Ihrer Python-Version.</td>
<td>System-
vorgabewert</td>
<td><em>rest-output-encoding</em>
<em>iso-8859-15</em></td>
</tr>
<tr><td><em>cgi-environment</em></td>
<td>Ein Abschnitt, in dem Benutzer
beliebige Schlüssel/Wert-Paare für
CGI-Umgebungsvariablen angeben
können.</td>
<td>None</td>
<td><em>&lt;cgi-environment&gt;</em>
<em>HTTPS_SERVER Foobar</em>
<em>Server 1.0</em>
<em>HTTPS_PORT 443</em>
<em>&lt;/cgi-environment&gt;</em></td>
</tr>
<tr><td><em>dns-server</em></td>
<td>Gibt die IP-Adresse Ihres Domain Name
System-(DNS-)Servers an, durch den
in Zopes Zugriffslogdateien aufgelöste
Hostnamen geschrieben werden. Wenn Sie
das einschalten, werden Verzögerungen
durch DNS-Anfragen Ihre Site
verlangsamen.</td>
<td>None</td>
<td><em>dns-server 127.0.0.1</em></td>
</tr>
<tr><td><em>ip-address</em></td>
<td>Gibt die IP-Adresse vor, auf denen die
verschiedenen Server-Protokolle ihre
Anfragen erwarten. Ohne einen Wert an
dieser Stelle hört Zope alle
verfügbaren IP-Adressen auf dem
Rechner ab.</td>
<td>None</td>
<td><em>ip-address 127.0.0.1</em></td>
</tr>
<tr><td><em>http-realm</em></td>
<td>Der HTTP-Header-Wert für <tt class="docutils literal">Realm</tt>, den
Zope in dieser Instanz ausgibt. Dieser
Wert taucht oftmals in Dialogfeldern
zur einfachen Authentifizierung auf.</td>
<td>Zope</td>
<td><em>Plone</em></td>
</tr>
<tr><td><em>automatically-</em>
<em>quote-dtml-</em>
<em>request-data</em></td>
<td>Setzen Sie diese Direktive auf
<tt class="docutils literal">off</tt>, um das &quot;Autoquoting&quot;
von implizit erhaltenen <tt class="docutils literal">REQUEST</tt>-
Daten in DTML-Code zu unterbinden,
der ein <tt class="docutils literal">&lt;</tt> in <tt class="docutils literal"><span class="pre">&lt;dtml-var&gt;</span></tt>-
Konstrukten enthält. Sonst werden alle
implizit von <tt class="docutils literal">REQUEST</tt> in DTML
erhaltenen Daten (im Gegensatz zum
direkten Zugriff mit
<tt class="docutils literal">REQUEST.einVarName</tt>), die ein <tt class="docutils literal">&lt;</tt>
enthalten, mit HTML-Quotes versehen,
wenn sie mit <tt class="docutils literal"><span class="pre">&lt;dtml-var&gt;</span></tt> oder
<tt class="docutils literal">&amp;dtml-</tt> interpoliert werden.
Das verringert die Wahrscheinlichkeit,
dass Programmierer ihre Sites für
clientseitige Trojaner-Attacken
offen lassen.</td>
<td>on</td>
<td><em>automatically-quote-dtml</em>
<em>request-data on</em></td>
</tr>
<tr><td><em>trusted-proxy</em></td>
<td>Gibt einen oder mehrere Hostnamen oder
IP-Adressen an.</td>
<td>None</td>
<td><em>trusted-proxy</em>
<em>www.example.com</em></td>
</tr>
<tr><td><em>publisher-</em>
<em>profile-file</em></td>
<td>Gibt eine Datei im Dateisystem an,
durch die Zopes Profiling-Fähigkeiten
aktiviert werden. Weitere Informationen
dazu finden Sie unter dem
<em>Profiling</em>-Reiter im ZMI. Sollte in
Produktion nicht gesetzt werden, da
dadurch der Code wesentlich langsamer
ausgeführt wird als normal.</td>
<td>None</td>
<td><em>publisher-profile-file</em>
<em>$INSTANCE/var/profile.dat</em></td>
</tr>
<tr><td><em>security-</em>
<em>policy-</em>
<em>implementation</em></td>
<td>Die normale Sicherheitsmaschinerie von
Zope ist in C implementiert. Die
Python-Version können Sie verwenden,
wenn Sie hier <tt class="docutils literal">python</tt> setzen.
Diese ist langsamer, bietet mit
VerboseSecurity aber wichtige
Informationen.</td>
<td>C</td>
<td><em>security-policy-</em>
<em>implementation python</em></td>
</tr>
<tr><td><em>skip-</em>
<em>authentication-</em>
<em>checking</em></td>
<td>Setzen Sie hier <tt class="docutils literal">on</tt>, wenn Zope
Prüfungen bei der Authentifizierung von
Servern auslassen soll, die nur anonyme
Inhalte anbieten.</td>
<td>off</td>
<td><em>skip-authentication-</em>
<em>checking on</em></td>
</tr>
<tr><td><em>skip-</em>
<em>ownership-</em>
<em>checking</em></td>
<td>Setzen Sie hier <tt class="docutils literal">on</tt>, wenn Zope
Besitz-Prüfungen auslassen soll, wenn
Code &quot;über das Web&quot; ausgeführt werden
soll. Das ist standardmäßig
eingeschaltet, um Sicherheitsprobleme
mit Trojanern zu verhindern, wobei
Benutzer mit niedrigen Privilegien
solche mit höheren Privilegien dazu
bringen können, gefährlichen Code
auszuführen.</td>
<td>on</td>
<td><em>skip-ownership-checking</em>
<em>off</em></td>
</tr>
<tr><td><em>maximum-number-</em>
<em>of-session-</em>
<em>objects</em></td>
<td>Ein Integer-Wert als &quot;maximale Anzahl
von Unterobjekten&quot; des transienten
Objekt-Containers in
<tt class="docutils literal">/temp/folder/session_data</tt></td>
<td>1000</td>
<td><em>maximum-number-of-session-</em>
<em>objects 1000</em></td>
</tr>
<tr><td><em>session-add-</em>
<em>notify-script-</em>
<em>path</em></td>
<td>Ein optionaler Zope-Pfadname eines
aufrufbaren Objekts, das als &quot;Skript
beim Addieren von Objekten&quot; des
transienten Objekt-Containers
aufgerufen werden soll, der beim
Hochfahren im Ordner <tt class="docutils literal">/temp_folder</tt>
erzeugt wird. Für den Einsatz mit
Sessions.</td>
<td>ungesetzt</td>
<td><em>session-add-notify-script-</em>
<em>path /scripts/add_notifier</em></td>
</tr>
<tr><td><em>session-delete-</em>
<em>notify-script-</em>
<em>path</em></td>
<td>Ein optionaler Zope-Pfadname eines
aufrufbaren Objekts, das als &quot;Skript
beim Löschen von Objekten&quot; des
transienten Objekt-Containers
aufgerufen werden soll, der beim
Hochfahren im Ordner <tt class="docutils literal">/temp_folder</tt>
erzeugt wird. Für den Einsatz mit
Sessions.</td>
<td>ungesetzt</td>
<td><em>session-delete-notify-</em>
<em>script-path *
*/scripts/add_notifier</em></td>
</tr>
<tr><td><em>session-</em>
<em>timeout-</em>
<em>minutes</em></td>
<td>Ein Integer-Wert für die Anzahl der
Minuten, die als &quot;Datenobjekt-Timeout&quot;
des transienten Objekt-Containers
<tt class="docutils literal">/temp/folder/session_data</tt> benutzt
werden soll.</td>
<td>20</td>
<td><em>session-timeout-minutes 30</em></td>
</tr>
<tr><td><em>suppress-all</em>
<em>access-rules-</em></td>
<td>Wenn auf <tt class="docutils literal">on</tt> gesetzt, werden in
Ihrer Zope-Site keine Zugriffsregeln
ausgeführt. Nützlich, wenn Sie sich
selbst aus einem bestimmten Teil Ihrer
Site aussperren, indem Sie eine
falsche Zugriffsregel einstellen.</td>
<td>off</td>
<td><em>suppress-all-access-rules</em>
<em>on</em></td>
</tr>
<tr><td><em>suppress-all</em>
<em>site-roots</em></td>
<td>Wenn auf <tt class="docutils literal">on</tt> gesetzt, sind in
Ihrer Zope-Site keine Site-Roots
aktiviert. Nützlich, wenn Sie sich
selbst aus einem bestimmten Teil Ihrer
Site aussperren, indem Sie eine
falsche Site-Root schreiben.</td>
<td>off</td>
<td><em>suppress-all-site-roots on</em></td>
</tr>
<tr><td><em>database-</em>
<em>quota-size</em></td>
<td>Die obere Grenze als Anzahl von Bytes
für die Größe der FileStorage-basierten
Zope-Datenbank. Nachdem diese Zahl
erreicht wird, können keine weiteren
Objekte zur Datenbank hinzugefügt
werden.</td>
<td>None</td>
<td><em>database-quota-size</em>
<em>1000000</em></td>
</tr>
<tr><td><em>read-only-</em>
<em>database</em></td>
<td>Bewirkt, dass die FileStorage-basierte
Zope-Datenbank ZODB nur lesend geöffnet
wird. Andere Dateien, z.B. Logdateien,
können geschrieben werden.</td>
<td>off</td>
<td><em>read-only-database on</em></td>
</tr>
<tr><td><em>zeo-client-name</em></td>
<td>Wenn Sie einen persistenten
ZEO-Client-Cache haben möchten, der
den Cache-Inhalt über Neustarts von
ClientStorage hinweg aufbewahrt,
müssen Sie einen <tt class="docutils literal"><span class="pre">zeo-client-name</span></tt>
definieren. Wenn Sie sonst ZEO
benutzen, wird der Client-Cache in
temporären Dateien gespeichert, die
gelöscht werden, wenn ClientStorage
beendet wird. Der Wert von
<tt class="docutils literal"><span class="pre">zeo-client-name</span></tt> ist eindeutig für
die erzeugten Cache-Dateien, falls
diese Zope-Instanz ein ZEO-Client ist.</td>
<td>off</td>
<td><em>zeo-client-name on</em></td>
</tr>
<tr><td><em>logger</em></td>
<td>Dieser Bereich sollte einen oder
mehrere <tt class="docutils literal">logger</tt>-Abschnitte mit den
Namen <tt class="docutils literal">access</tt>, <tt class="docutils literal">event</tt> und
<tt class="docutils literal">trace</tt> definieren. Der <tt class="docutils literal">access</tt>-
Logger loggt Zugriffe auf den Zope-
Server fest, der <tt class="docutils literal">event</tt>-Logger
loggt Informationen zu Zope-Events,
und der <tt class="docutils literal">trace</tt>-Logger loggt
detaillierte Informationen zu
Serveranfragen (nur für die
Fehlersuche). Jeder Logger-Abschnitt
darf ein Name/Wert-Paar eines Levels
enthalten, mit dem der Grad an
Logging-Details angegeben wird. Der
Vorgabe-Level lautet <tt class="docutils literal">INFO</tt>.
Erlaubte Werte hierfür sind
<tt class="docutils literal">CRITICAL</tt>, <tt class="docutils literal">ERROR</tt>, <tt class="docutils literal">WARN</tt>,
<tt class="docutils literal">INFO</tt>, <tt class="docutils literal">DEBUG</tt> und <tt class="docutils literal">ALL</tt>.
Jeder Logger-Abschnitt darf weiterhin
ein oder mehrere <tt class="docutils literal">handler</tt>-
Abschnitte enthalten, die den Typ des
Log-Handlers angeben. Es gibt fünf
Handler-Typen: <tt class="docutils literal">logfile</tt>, <tt class="docutils literal">syslog</tt>,
<tt class="docutils literal"><span class="pre">win32-eventlog</span></tt>, <tt class="docutils literal"><span class="pre">http-handler</span></tt>
und <tt class="docutils literal"><span class="pre">email-notifier</span></tt>. Jeder hat
seine eigenen erlaubten Unterschlüssel,
die gewisse Aspekte des Handlers
bestimmen. In allen Handler-
Abschnitten kann auch <tt class="docutils literal">format</tt>
(der Format-String der Log-Einträge),
<tt class="docutils literal">dateformat</tt> (der Format-String der
Datums-Strings) und <tt class="docutils literal">level</tt> angegeben
werden. Letzterer hat die gleiche
Semantik wie wie der übergeordnete
Logger-Level, überschreibt aber den
Level-Logger des Handlers, in dem er
definiert ist.</td>
<td>Der
<tt class="docutils literal">access</tt>-Log
schreibt auf
dem Level
<tt class="docutils literal">INFO</tt> in
die Datei
<tt class="docutils literal">&lt;instance</tt>
<tt class="docutils literal">home/log</tt>
<tt class="docutils literal">/Z2.log&gt;</tt>,
der <tt class="docutils literal">event</tt>-
Log auf dem
Level <tt class="docutils literal">INFO</tt>
in das Datei-
Log, und der
<tt class="docutils literal">trace</tt>-Log
wird nirgendwo
geschrieben.</td>
<td><tt class="docutils literal">&lt;eventlog&gt;</tt>
``  level ALL``
``  &lt;logfile&gt;``
``    path $INSTANCE/log/``
<tt class="docutils literal">event.log</tt>
``    level INFO``
``  &lt;/logfile&gt;``
<tt class="docutils literal">&lt;/eventlog&gt;</tt>
<tt class="docutils literal">&lt;logger access&gt;</tt>
``  level WARN``
``  &lt;logfile&gt;``
``    path $INSTANCE/log/``
<tt class="docutils literal">Z2.log</tt>
``    format %(message)s``
``  &lt;/logfile&gt;``
<tt class="docutils literal">&lt;/logger&gt;</tt></td>
</tr>
<tr><td><em>warnfilter</em></td>
<td>In diesem Abschnitt können Sie
Warnungsfilter angeben. Folgende
Schlüssel gelten in einem
<tt class="docutils literal">warnfilter</tt>-Abschnitt, <tt class="docutils literal">action</tt>:
einer der Strings <tt class="docutils literal">error</tt>,
<tt class="docutils literal">ignore</tt>, <tt class="docutils literal">always</tt>, <tt class="docutils literal">default</tt>,
<tt class="docutils literal">module</tt> und <tt class="docutils literal">once</tt>; <tt class="docutils literal">message</tt>:
ein String mit einem regulären
Ausdruck, mit dem die Warnung
übereinstimmen muss (unabhängig von
Groß-/Kleinschreibung); <tt class="docutils literal">category</tt>:
ein Python-Klassenname mit Punkten
(muss eine Unterklasse von <tt class="docutils literal">Warning</tt>
sein), von der die Warnungskategorie
eine Unterklasse sein muss, damit die
Übereinstimmung erfolgen kann;
<tt class="docutils literal">module</tt>: ein String mit einem
regulären Ausdruck, der mit dem
Modulnamen übereinstimmen muss
(unabhängig von der Schreibweise);
<tt class="docutils literal">lineno</tt>: ein Integer, der mit der
Nummer der Zeile übereinstimmen muss,
in der die Warnung aufgetreten ist,
oder 0, was mit allen Zeilennummern
übereinstimmt.</td>
<td>None</td>
<td><tt class="docutils literal">&lt;warnfilter&gt;</tt>
<tt class="docutils literal">action ignore</tt>
<tt class="docutils literal">category</tt>
<tt class="docutils literal">exceptions.</tt>
<tt class="docutils literal">DeprecationWarning</tt>
<tt class="docutils literal">&lt;/warnfilter&gt;</tt></td>
</tr>
<tr><td><em>max-listen-</em>
<em>sockets</em></td>
<td>Die maximale Anzahl von Sockets, die
ZServer versucht zu öffnen, um
einkommende Verbindungen zu bedienen.</td>
<td>1000</td>
<td><em>max-listen-sockets 500</em></td>
</tr>
<tr><td><em>servers</em></td>
<td>Eine Reihe von Abschnitten, mit denen
die verschiedenen ZServer von Zope
angegeben werden können. Es können
sieben Servertypen definiert werden:
<tt class="docutils literal"><span class="pre">http-server</span></tt>, <tt class="docutils literal"><span class="pre">ftp-server</span></tt>,
<tt class="docutils literal"><span class="pre">webdav-source-server</span></tt>,
<tt class="docutils literal"><span class="pre">persistent-cgi</span></tt>, <tt class="docutils literal"><span class="pre">fast-cgi</span></tt>,
<tt class="docutils literal"><span class="pre">monitor-server</span></tt> und <tt class="docutils literal"><span class="pre">icp-server</span></tt>.
Wenn keine Server definiert werden,
werden die vorgegebenen Server benutzt.
Ports können entweder in einer
einfachen Form angegeben werden (80)
oder in komplexer Form inklusive
Hostname (127.0.0.1:80). Wenn der
Hostname weggelassen wird, wird als
Hostname <tt class="docutils literal"><span class="pre">default-ip-address</span></tt>
benutzt. Port-Nummern werden durch
die Einstellung in <tt class="docutils literal"><span class="pre">port-base</span></tt> mit
dem Standardwert 8000 verschoben.
In Ihrer Plone-Installation kann das
verändert worden sein, damit eine
erste Installation einfacher wird.</td>
<td>HTTP-Server
auf Port 8080
und FTP auf
8021</td>
<td><tt class="docutils literal"><span class="pre">&lt;http-server&gt;</span></tt>
``  # valid key is``
<tt class="docutils literal">&quot;address&quot; and ``
<span class="pre">``&quot;force-connection-</span></tt>
<tt class="docutils literal">close&quot;</tt>
``  address 8080``
``  # force-connection-``
<tt class="docutils literal">close&quot; on</tt>
<tt class="docutils literal"><span class="pre">&lt;/http-server&gt;</span></tt>
<tt class="docutils literal"><span class="pre">&lt;ftp-server&gt;</span></tt>
``  # valid key is``
<tt class="docutils literal">&quot;address&quot;</tt>
``  address 8021``
<tt class="docutils literal"><span class="pre">&lt;/ftp-server&gt;</span></tt></td>
</tr>
<tr><td><em>database</em></td>
<td>Der <tt class="docutils literal">database</tt>-Abschnitt erlaubt die
Angabe eigener Datenbank- und Speicher-
typen. Es kann mehr als ein
<tt class="docutils literal">zodb_db</tt>-Abschnitt definiert
werden. Die Werte im Speicher werden
von den Werten in dem jeweiligen
Datenbank-Client gesetzt.</td>
<td>Siehe Bsp.</td>
<td>((argh..... siehe unten))</td>
</tr>
</tbody>
</table>
<pre class="literal-block">
&lt;zodb_db main&gt;
  # Main FileStorage database
  &lt;filestorage&gt;
    path $INSTANCE/var/Data.fs
  &lt;/filestorage&gt;
  mount-point /
&lt;zodb_db&gt;
&lt;zodb_db temporary&gt;
  # Temporary database database (for sessions)
  &lt;temporarystorage&gt;
    name temporary storage for sessioning
  &lt;/temporarystorage&gt;
  mount-point /temp_folder
  container-class Products.TemporaryFolder.TemporaryContainer
&lt;zodb_db&gt;
</pre>
<p>Von allen Konfigurationen sind die letzten paar (Datenbank, Server und Logger) Direktiven in einer einfachen XML-artigen Syntax. Wenn Sie mit der Apache-Protokolldatei arbeiten, wird Ihnen dieses Format bekannt vorkommen. Diese Direktiven können Sie auch mehrfach anwenden. So besteht eine häufige Konfiguration von Installationsprogrammen auf dem Mac oder unter Windows darin, HTTP-Verbindungen auf zwei Ports, 80 und 8080, durchzuführen. Das machen Sie wie folgt:</p>
<pre class="literal-block">
&lt;http-server&gt;
  address 8080
&lt;/http-server&gt;

&lt;http-server&gt;
  address 80
&lt;/http-server&gt;
</pre>
</div>
</div>
<div class="section" id="regeln-zur-textformatierung">
<h3>Regeln zur Textformatierung</h3>
<p>Plone verfügt mit External Editor und Epoz über zwei exzellente Werkzeuge zur Bearbeitung von HTML. In Zope und Python werden allerdings zwei Formate von einfachem Text häufig verwendet: strukturierter Text und restrukturierter Text. Da die beiden genannten Editoren qualitativ sehr gut sind, glaube ich, dass Sie diese Formate nicht benötigen werden. Aber wenn Sie mit Zope oder Plone entwickeln werden, werden Ihnen diese Formate sehr wahrscheinlich irgendwann begegnen.</p>
<p>Beide Formate versuchen, ähnliche Dinge zu machen: Sie bieten ein System zur Auszeichnung von einfachem Text an, aus dem sie HTML generieren. Das zielt auf Entwickler ab, die gern mit einfachem Text arbeiten und daran gewöhnt sind, die aber den Aufwand scheuen, selbst HTML zu produzieren.</p>
<div class="section" id="strukturierter-text">
<h4>Strukturierter Text</h4>
<p>Es folgt eine Darstellung, wie sie bereits in einem älteren Artikel erschienen ist. Sie finden ihn online unter <a class="reference external" href="http://plone.org/documentation-old/howto/UsingStructuredText">http://plone.org/documentation-old/howto/UsingStructuredText</a>. Im folgenden Abschnitt ist die Einrückung des Textes im Code von entscheidender Bedeutung dafür, wie der Text dargestellt wird.</p>
<div class="section" id="einfache-formatierung">
<h5>Einfache Formatierung</h5>
<p>Das Grundkonzept von strukturiertem Text baut auf einem Absatz auf. Das Beispiel</p>
<pre class="literal-block">
Das ist der erste Absatz.

Das ist der zweite Absatz.
</pre>
<p>wird zu folgendem HTML umgewandelt:</p>
<pre class="literal-block">
&lt;p&gt;Das ist der erste Absatz.&lt;/p&gt;
&lt;p&gt;Das ist der zweite Absatz.&lt;/p&gt;
</pre>
<p>Die Whitespaces zwischen Elementen in strukturiertem Text sind von Bedeutung. In diesem Fall bewirkt eine Leerzeile zwischen den beiden anderen, dass ein neuer Absatz anfängt. Das ist intuitiv leicht zu verstehen. In E-Mails z.B. werden Absätze auch mit Leerzeilen voneinander getrennt. Für Hervorhebungen im Text benutzt strukturierter Text eine andere Konvention, nämlich Sternchen. Beispiel:</p>
<pre class="literal-block">
Das ist der *erste* Absatz.

Das ist der **zweite** Absatz.
</pre>
<p>In HTML entstehen daraus die Tags <tt class="docutils literal">em</tt> und <tt class="docutils literal">strong</tt>:</p>
<pre class="literal-block">
&lt;p&gt;Das ist der &lt;em&gt;erste&lt;/em&gt; Absatz.&lt;/p&gt;
&lt;p&gt;Das ist der &lt;strong&gt;zweite&lt;/strong&gt; Absatz.&lt;/p&gt;
</pre>
<p>Auch dieses Muster kann man häufig in E-Mails beobachten. Einige andere häufig benutzte Muster werden ebenfalls unterstützt, z.B. Verweise auf einen Jargon-Begriff:</p>
<pre class="literal-block">
Wenn Sie 'STX' lesen, wissen Sie, dass es eine
Abkürzung für 'strukturierter Text' ist.
</pre>
<p>Die HTML-Ausgabe hierzu sieht wie folgt aus:</p>
<pre class="literal-block">
&lt;p&gt;Wenn Sie &lt;code&gt;STX&lt;/code&gt; lesen, wissen Sie, dass es eine
Abkürzung für &lt;code&gt;strukturierter Text&lt;/code&gt; ist.&lt;/p&gt;
</pre>
</div>
<div class="section" id="einruckung-verwenden">
<h5>Einrückung verwenden</h5>
<p>Der vorangegangene Abschnitt handelte von Textkonventionen, mit denen eine Semantik verbunden ist. Bei der Verarbeitung des strukturierten Textes werden aus dieser Semantik bestimmte HTML-Tags erzeugt. Die Einrückung von strukturiertem Text hat ebenfalls eine Semantik. Die grundlegendste hat mit dem Konzept von Überschriften in HTML zu tun. Im folgenden Beispiel steht die Einrückung für eine outline-artige Struktur.</p>
<pre class="literal-block">
Einrückung verwenden
  Der vorangegangene Abschnitt handelte von Textkonventionen, mit denen eine
Semantik verbunden ist. Bei der Verarbeitung des strukturierten Textes werden
aus dieser Semantik bestimmte HTML-Tags erzeugt.
</pre>
<p>Daraus wird folgender HTML-Code produziert:</p>
<pre class="literal-block">
&lt;h1&gt;Einrückung verwenden&lt;/h1&gt;

&lt;p&gt;Der vorangegangene Abschnitt handelte von Textkonventionen, mit denen eine
Semantik verbunden ist. Bei der Verarbeitung des strukturierten Textes werden
aus dieser Semantik bestimmte HTML-Tags erzeugt.&lt;/p&gt;
</pre>
<p>In der Einrückung war also eine Bedeutung enthalten, der Absatz war nämlich der Überschrift untergeordnet, und diese Beziehung wird in HTML ausgedrückt. Tatsächlich kann eine solche Outline-Beziehung wie folgt fortgesetzt werden:</p>
<pre class="literal-block">
Einrückung verwenden

  Der vorangegangene Abschnitt handelte von Textkonventionen, mit denen eine
Semantik verbunden ist. Bei der Verarbeitung des strukturierten Textes werden
aus dieser Semantik bestimmte HTML-Tags erzeugt.

  Grundlagen der Einrückung

    In diesem Abschnitt werden wir die Grundlagen der Einrückung untersuchen...
</pre>
<p>Daraus wird folgeder HTML-Code produziert:</p>
<pre class="literal-block">
&lt;h1&gt;Einrückung verwenden&lt;/h1&gt;

&lt;p&gt;Der vorangegangene Abschnitt handelte von Textkonventionen, mit denen eine
Semantik verbunden ist. Bei der Verarbeitung des strukturierten Textes werden
aus dieser Semantik bestimmte HTML-Tags erzeugt.&lt;/p&gt;

&lt;h2&gt;Grundlagen der Einrückung&lt;/h2&gt;

&lt;p&gt;In diesem Abschnitt werden wir die Grundlagen der Einrückung untersuchen...&lt;/p&gt;
</pre>
</div>
<div class="section" id="listen-und-listeneintrage">
<h5>Listen und Listeneinträge</h5>
<p>Listen werden in strukturiertem Text ebenfalls unterstützt, darunter ungeordnete, geordnete und beschreibende Listen. Die Konvention zu ungeordneten Listen ist ein häufig benutztes Muster in der Kommunikation mit Texten.</p>
<p>In HTML gibt es drei verschiedene Arten von Listen:</p>
<ul class="simple">
<li>ungeordnete Listen</li>
<li>geordnete Listen</li>
<li>beschreibende Listen</li>
</ul>
<p>In strukturiertem Text dürfen Sie Listenelemente mit den vorangestellten Symbolen *, o und - auszeichnen. Das obige Beispiel produziert folgenden HTML-Code:</p>
<pre class="literal-block">
&lt;p&gt;In HTML gibt es drei verschiedene Arten von Listen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ungeordnete Listen&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;geordnete Listen&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;beschreibende Listen&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Die Konvention hinter geordneten Listen sieht in strukturiertem Text wie folgt aus:</p>
<pre class="literal-block">
In HTML gibt es drei verschiedene Arten von Listen:

1. ungeordnete Listen
2. geordnete Listen
3. beschreibende Listen
</pre>
<p>Dabei wird Folgendes produziert:</p>
<pre class="literal-block">
&lt;p&gt;In HTML gibt es drei verschiedene Arten von Listen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;ungeordnete Listen&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;geordnete Listen&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;beschreibende Listen&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
</pre>
<p>Beschreibende Listen lassen sich genauso leicht mit doppelten Spiegelstrichen angeben. Beispiel:</p>
<pre class="literal-block">
geordnete Listen -- HTML-Viewer wandeln die Listenelemente in eine nummerierte Folge um

beschreibende Listen -- werden normalerweise für Definitionslisten wie in Glossaren verwendet
</pre>
<p>Daraus wird folgender HTML-Code:</p>
<pre class="literal-block">
&lt;dl&gt;
&lt;dt&gt;geordnete Listen&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;HTML-Viewer wandeln die Listenelemente in eine nummerierte Folge um&lt;/p&gt;&lt;/dd&gt;
&lt;dt&gt;beschreibende Listen&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;werden normalerweise für Definitionslisten wie in Glossaren verwendet&lt;/p&gt;&lt;/dd&gt;
&lt;/dl&gt;
</pre>
</div>
<div class="section" id="beispiel-code">
<h5>Beispiel-Code</h5>
<p>Autoren von strukturiertem Text können eine einfache Konvention benutzen, um die mit der Semantik eines <tt class="docutils literal">code</tt>-Tags in HTML verbundene unproportionale Darstellung zu erreichen. Der Code</p>
<pre class="literal-block">
Sobald Sie das Dialogfeld sehen, klicken Sie auf den Button 'OK'.
</pre>
<p>wird zum Beispiel mit folgendem HTML-Code dargestellt:</p>
<pre class="literal-block">
&lt;p&gt;Sobald Sie das Dialogfeld sehen, klicken Sie
auf den Button &lt;code&gt;OK&lt;/code&gt;.&lt;/p&gt;
</pre>
<p>Manchmal benötigen Sie aber auch längere Code-Passagen. Was, wenn Sie z.B. eine Python-Funktion mitten in einem Artikel über Python dokumentieren möchten? Einen solchen Code-Block geben Sie an, indem Sie einen Absatz mit zwei Doppelpunkten beenden (<tt class="docutils literal">::</tt>) und den oder die folgenden Absätze einrücken. Aus</p>
<pre class="literal-block">
In diesem Beispiel wandeln wir Menschenjahre in Hundejahre um:

def hundeJahre(alter):
    &quot;&quot;&quot;Wandle Menschenjahre in Hundejahre um.&quot;&quot;&quot;
    return alter * 7
</pre>
<p>wird dieser HTML-Code erzeugt:</p>
<pre class="literal-block">
&lt;p&gt;In diesem Beispiel wandeln wir Menschenjahre in Hundejahre um:&lt;/p&gt;

&lt;pre&gt;
def hundeJahre(alter):
    &quot;&quot;&quot;Wandle Menschenjahre in Hundejahre um.&quot;&quot;&quot;
    return alter * 7
&lt;/pre&gt;
</pre>
<p>Bei dieser Konvention, ein <tt class="docutils literal">::</tt> am Ende eines Absatzes mit einem eingerückten Block zu kombinieren, wird nicht nur eine Code-Semantik angewendet, sondern der eingerückte Block wird auch <em>geschützt</em>. Das heißt, die Schnipsel mit strukturiertem Text bzw. HTML in diesem Artikel werden nicht angetastet und bleiben in ihrer rohen Darstellung. Im folgenden Beispiel werden die Zeichen für kleiner-als, größer-als und das kaufmännische Und geschützt:</p>
<pre class="literal-block">
Hier ist ein HTML-Beispiel:

&lt;html&gt;
&lt;p&gt;Dies ist eine Seite zu Hunden &amp; Katzen.&lt;/p&gt;
&lt;html&gt;
</pre>
<p>was das folgenden HTML-Code produziert:</p>
<pre class="literal-block">
&lt;p&gt;Hier ist ein HTML-Beispiel:&lt;/p&gt;

&lt;pre&gt;
  &lt;html&gt;
    &lt;p&gt;Dies ist eine Seite zu Hunden &amp; Katzen.&lt;/p&gt;
  &lt;html&gt;
&lt;/pre&gt;
</pre>
</div>
<div class="section" id="hyperlinks">
<h5>Hyperlinks</h5>
<p>Die vorangegangenen Abschnitte konzentrierten sich auf verschiedene Arten, mit üblichen Textkonventionen eine Präsentationssemantik in HTML zu erzielen. Aber das Web besteht nicht nur aus Text. Verbindungen von Wörtern und Sätzen mit weiteren Informationen sowie die Einbindung von Bildern sind ebenso wichtig. Daher werden Hyperlinks und Bild-Tags auch im strukturierten Textformat unterstützt.</p>
<p>Beginnen wir z.B. mit einem einfachen Hyperlink. Wenn Sie z.B den folgenden Absatz über Python in strukturiertem Text haben:</p>
<pre class="literal-block">
Weitere Informationen zu Python finden Sie auf der
&quot;Python-Website&quot;:http://www.python.org.
</pre>
<p>wird daraus Folgendes in HTML:</p>
<pre class="literal-block">
&lt;p&gt;Weitere Informationen zu Python finden Sie auf der
&lt;a href=&quot;http://www.python.org&quot;&gt;Python-Website&lt;/a&gt;.
</pre>
<p>Die Konvention dahinter ist ziemlich einfach:</p>
<ul class="simple">
<li>Der Verweistext wird in Anführungszeichen gesetzt.</li>
<li>Dem zweiten Anführungszeichen folgen ein Doppelpunkt und eine URL.</li>
<li>Nach der URL dürfen Interpunktionszeichen stehen.</li>
</ul>
<p>Zu dieser Basiskonvention gibt es eine Reihe von Varianten, damit auch relative, <tt class="docutils literal">mailto</tt>- und Bild-URLs verwendet werden können.</p>
</div>
</div>
<div class="section" id="restrukturierter-text">
<h4>Restrukturierter Text</h4>
<p>Restrukturierter Text (im Englischen oft als reStructured Text geschrieben) ist eine neuere Version von strukturiertem Text, die einige Probleme beheben soll, die manche Leute mit älteren Versionen hatten. Strukturierter Text versagt nicht nur beim Thema Internationalisierung, sondern produziert ungültige Auszeichnungen. Außerdem ist seine Syntax manchmal etwas schwer verständlich.</p>
<p>Das neue restrukturierte Textformat ist zu einem der Standards für die Dokumentation von Python-Code geworden und wird auch im Docutils-Projekt verwendet. Die Online-Dokumentation ist so gut, dass ich Ihnen ohne Vorbehalte empfehlen möchte, sie unter <a class="reference external" href="http://docutils.sourceforge.net/rst.html">http://docutils.sourceforge.net/rst.html</a> zu lesen.</p>
<p>Es folgt einiges an Material, das von Richard Jones geschrieben wurde.</p>
<div class="section" id="einleitung">
<h5>Einleitung</h5>
<p>Als Grundeinheit wird ein Absatz erkannt, also ein Textbrocken, der mit Leerzeilen abgetrennt ist, wobei eine Leerzeile ausreicht. Absätze müssen alle die gleiche Einrückungstiefe haben, d.h., sie müssen links gleich weit weg vom Rand sein. Eingerückte Absätze erscheinen als eingerückte Zitate. Aus dem Code</p>
<pre class="literal-block">
Dies ist ein Absatz, ein recht kurzer noch dazu.

  Aus diesem Absatz wird ein eingerückter Textblock,
  in dem oftmals ein anderer Text zitiert wird.

Und noch ein Absatz.
</pre>
<p>wird folgende Ausgabe:</p>
<pre class="literal-block">
&lt;blockquote&gt;
  &lt;p&gt;Dies ist ein Absatz, ein recht kurzer noch dazu.&lt;/p&gt;
    &lt;blockquote&gt;
      Aus diesem Absatz wird ein eingerückter Textblock,
      in dem oftmals ein anderer Text zitiert wird.
    &lt;/blockquote&gt;
  &lt;p&gt;Und noch ein Absatz.&lt;/p&gt;
&lt;/blockquote&gt;
</pre>
</div>
<div class="section" id="textstile">
<h5>Textstile</h5>
<p>In Absätzen und anderen Textteilen können Sie Text zusätzlich mit *kursiv* als <em>kursiv</em> oder mit **fett** als <strong>fett</strong> auszeichnen.</p>
<p>Wenn Sie möchten, dass etwas nichtproportional erscheint, verwenden Sie doppelte Rückanführungszeichen: <tt class="docutils literal">``</tt>. Beachten Sie, dass innerhalb dieser Anführungszeichen nichts angetastet wird, d.h. Sternchen usw. bleiben unverändert.</p>
<p>Falls Sie ein &quot;besonderes&quot; Zeichen im Text verwenden möchten, können Sie das normalerweise tun, denn restrukturierter Text ist ziemlich clever. Das Sternchen z.B. funktioniert prima. Wenn Sie wirklich einmal Text mit Sternchen drumherum haben möchten, der <em>nicht</em> kursiv gesetzt werden soll, müssen Sie angeben, dass das Sternchen keine Bedeutung hat. Das machen Sie, indem Sie wie folgt einen Rückschrägstrich davor setzen:</p>
<pre class="literal-block">
\*
</pre>
<p>Oder Sie umschließen ihn mit doppelten Rückanführungszeichen wie folgt:</p>
<pre class="literal-block">
``\*``
</pre>
</div>
<div class="section" id="listen">
<h5>Listen</h5>
<p>Es gibt drei verschiedene Sorten von Listen: geordnete, ungeordnete und beschreibende Listen. Bei allen dreien dürfen Sie beliebig viele Absätze, Unterlisten usw. verwenden, solange der linke Absatzrand genauso weit eingerückt ist wie die erste Textzeile im Listenelement.</p>
<p>Listen stehen immer am Anfang eines neuen Absatzes, d.h., sie müssen nach einer Leerzeile stehen. Beginnen Sie eine Zeile mit einer Zahl oder einem Buchstaben, gefolgt von einem Punkt, mit einer runden Klammer danach oder insgesamt in runden Klammern, was immer Ihnen lieber ist. Alle Beispiele in Listing A.1 werden erkannt.</p>
<p>Listing A.1. Beispiele für Punkte</p>
<pre class="literal-block">
1. Zahlen

A. Großbuchstaben
   auch über mehrere Zeilen

   sogar mit zwei Absätzen darin!

a. Kleinbuchstaben

   3. mit einer Unterliste, die mit
      einer anderen Zahl beginnt
   4. aber die Zahlen sollten schon
      die richtige Reihenfolge haben!

I. Große römische Zahlen

i. Kleine römische Zahlen

(1) wieder Zahlen

1) und nochmal
</pre>
<p>Listing A.2 zeigt das Ergebnis, aber beachten Sie, dass die Stile für die verschiedenen Aufzählungslisten nicht von allen Browsern unterstützt werden, d.h., Sie können eventuell nicht den ganzen Effekt sehen.</p>
<p>Listing A.2. Beispielliste</p>
<pre class="literal-block">
&lt;ol class=&quot;arabic simple&quot;&gt;
&lt;li&gt;Zahlen&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;upperalpha&quot;&gt;
&lt;li&gt;&lt;p class=&quot;first&quot;&gt;Großbuchstaben
auch über mehrere Zeilen&lt;/p&gt;
&lt;p&gt;sogar mit zwei Absätzen darin!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;loweralpha simple&quot;&gt;
&lt;li&gt;Kleinbuchstaben&lt;ol class=&quot;arabic&quot; start=&quot;3&quot;&gt;
&lt;li&gt;mit einer Unterliste, die mit
einer anderen Zahl beginnt&lt;/li&gt;
&lt;li&gt;aber die Zahlen sollten schon
die richtige Reihenfolge haben!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;upperroman simple&quot;&gt;
&lt;li&gt;Große römische Zahlen&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;lowerroman simple&quot;&gt;
&lt;li&gt;Kleine römische Zahlen&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;arabic simple&quot;&gt;
&lt;li&gt;wieder Zahlen&lt;/li&gt;
&lt;/ol&gt;
&lt;ol class=&quot;arabic simple&quot;&gt;
&lt;li&gt;und nochmal&lt;/li&gt;
&lt;/ol&gt;
</pre>
<p>Statt wie bei geordneten Listen können Sie Listeneinträge auch mit einem Punkt
davor haben, indem Sie entweder <tt class="docutils literal">-</tt>, <tt class="docutils literal">+</tt> oder <tt class="docutils literal">*</tt> wie folgt verwenden:</p>
<pre class="literal-block">
* ein Punkt mit &quot;*&quot;

  - eine Unterliste mit &quot;-&quot;

    + eine andere Unterliste

  - ein weiterer Eintrag
</pre>
<p>was folgenden Code erzeugt:</p>
<pre class="literal-block">
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;ein Punkt mit &amp;quot;*&amp;quot;&lt;ul&gt;
&lt;li&gt;eine Unterliste mit &amp;quot;-&amp;quot;&lt;ul&gt;
&lt;li&gt;eine andere Unterliste&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ein weiterer Eintrag&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>Anders als die beiden vorausgegangenen Listenarten bestehen beschreibende Listen aus einem Begriff und seiner Definition. Das Format einer solchen Liste lautet wie folgt:</p>
<pre class="literal-block">
*Was*
  Beschreibende Listen verbinden einen Begriff mit einer Definition.

*Wie*
  Der Begriff besteht aus einem einzeiligen Satz, und die Definition
  besteht aus einem oder mehreren Absätzen, die relativ zum Begriff eingerückt
  sind. Zwischen Begriff und Definition sind keine Leerzeilen erlaubt.
</pre>
<p>Dadurch wird der folgende Code erzeugt:</p>
<pre class="literal-block">
&lt;blockquote&gt;
&lt;dl class=&quot;docutils&quot;&gt;
&lt;dt&gt;&lt;em&gt;Was&lt;/em&gt;&lt;/dt&gt;
&lt;dd&gt;Beschreibende Listen verbinden einen Begriff mit einer Definition.&lt;/dd&gt;
&lt;dt&gt;&lt;em&gt;Wie&lt;/em&gt;&lt;/dt&gt;
&lt;dd&gt;Der Begriff besteht aus einem einzeiligen Satz, und die Definition
besteht aus einem oder mehreren Absätzen, die relativ zum Begriff eingerückt
sind. Zwischen Begriff und Definition sind keine Leerzeilen erlaubt.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/blockquote&gt;
</pre>
</div>
<div class="section" id="vorformatierung-code-beispiele">
<h5>Vorformatierung (Code-Beispiele)</h5>
<p>Um einfach einen Brocken Text einzufügen, der unter keinen Umständen jemals angetastet werden soll, beenden Sie den vorhergehenden Absatz mit <em>::</em>. Der vorformatierte Block gilt als beendet, wenn der Text wieder die Einrückungsebene vor dem vorformatierten Block erreicht. Beispiel:</p>
<pre class="literal-block">
Beispiel::

      Leerräume, Zeilenenden, Leerzeilen und alle Arten
      von Auszeichnungen, z.B. *das* oder \das, werden
      in solchen literalen Blöcken erhalten.
    Schau mal, eine Ebene rauf
    (aber nicht weit genug).

Nicht mehr im Beispiel.
</pre>
<p>Dadurch wird der folgende Code erzeugt:</p>
<pre class="literal-block">
&lt;div class=&quot;document&quot;&gt;
&lt;p&gt;Beispiel:&lt;/p&gt;
&lt;pre class=&quot;literal-block&quot;&gt;
  Leerräume, Zeilenenden, Leerzeilen und alle Arten
  von Auszeichnungen, z.B. *das* oder \das, werden
  in solchen literalen Blöcken erhalten.
Schau mal, eine Ebene rauf
(aber nicht weit genug).
&lt;/pre&gt;
&lt;p&gt;Nicht mehr im Beispiel.&lt;/p&gt;
&lt;/div&gt;
</pre>
<p>Beachten Sie, dass ein Absatz, der nur aus <tt class="docutils literal">::</tt> besteht, in der Ausgabe nicht erscheint. Beispiel:</p>
<pre class="literal-block">
::

  Das ist vorformatierter Text, und der
  letzte Absatz mit &quot;*::*&quot; wird entfernt.
</pre>
<p>Das erzeugt den folgenden Code:</p>
<pre class="literal-block">
&lt;pre class=&quot;literal-block&quot;&gt;
  Das ist vorformatierter Text, und der
  letzte Absatz mit &quot;::&quot; wird entfernt.
&lt;/pre&gt;
</pre>
</div>
<div class="section" id="abschnitte">
<h5>Abschnitte</h5>
<p>Mit Überschriften können Sie längere Texte in Abschnitte untergliedern. Sie bestehen aus einer einzelnen Textzeile mit nur einer Schmuckzeile danach oder einer davor und danach. Diese Zeilen bestehen aus Minuszeichen (<em>-----</em>), Gleichheitszeichen (<em>=====</em>), Tilden (<em>~~~~~</em>) oder einem beliebigen nichtalphanumerischen Zeichen, das Ihnen gefällt:</p>
<pre class="literal-block">
- = ` : ' &quot; ~ ^ _ * + # &lt; &gt;
</pre>
<p>Eine Schmuckzeile nur unter einer Überschrift ist verschieden von einer, die vor und nach einer Überschrift verwendet wird, aber sonst aus dem gleichen Zeichen besteht. Diese Zeile davor bzw. danach muss mindestens so lang sein wie die Textzeile. Diese Zeilen sollten Sie konsistent verwenden, da alle Überschriften mit der gleichen Schmuckzeile sich auf der gleichen Ebene befinden. Beispiel:</p>
<pre class="literal-block">
Kapitel 1 Titel
===============

Abschnitt 1.1 Titel
-------------------

Unterabschnitt 1.1.1 Titel
~~~~~~~~~~~~~~~~~~~~~~~~~~

Abschnitt 1.2 Titel
-------------------

Kapitel 2 Titel
===============
</pre>
<p>Daraus wird der folgende Code, der hier in vereinfachtem Pseudo-XML angegeben ist:</p>
<pre class="literal-block">
&lt;section&gt;
  &lt;title&gt;
    Kapitel 1 Titel
  &lt;section&gt;
    &lt;title&gt;
      Abschnitt 1.1 Titel
    &lt;section&gt;
      &lt;title&gt;
        Unterabschnitt 1.1.1 Titel
  &lt;section&gt;
    &lt;title&gt;
      Abschnitt 1.2 Titel
&lt;section&gt;
  &lt;title&gt;
    Kapitel 2 Titel
</pre>
<p>Pseudo-XML verwendet eine Einrückung zur Darstellung der Schachtelung und enthält keine schließenden Tags. Hierbei kann man nicht wie bei den anderen Beispielen eine echte Ausgabe zeigen, weil Abschnitte in Blöcken nicht vorkommen dürfen. Vergleichen Sie als konkretes Beispiel den Quelltext zum englischen Original dieses Textes unter <a class="reference external" href="http://docutils.sourceforge.net/docs/rst/quickstart.html">http://docutils.sourceforge.net/docs/rst/quickstart.html</a> mit der erzeugten Ausgabe.</p>
<p>Beachten Sie, dass dabei die Abschnittsüberschriften als Ziele von Links fungieren, wenn man ihren Namen benutzt. Um einen Link zur Überschrift <em>Listen</em> zu erstellen, schreiben Sie <tt class="docutils literal">Listen_</tt>. Wenn in der Überschrift ein Leerzeichen vorkommt, wie in <em>Restrukturierter Text</em>, müssen Sie die Überschrift wie folgt in Anführungszeichen setzen: <tt class="docutils literal">`Restrukturierter Text`_</tt>.</p>
</div>
<div class="section" id="bilder">
<h5>Bilder</h5>
<p>Um ein Bild in Ihrem Dokument einzubinden, verwenden Sie die Direktive <tt class="docutils literal">image</tt>. Zum Beispiel wird der folgende Code:</p>
<pre class="literal-block">
.. image:: images/biohazard.png
</pre>
<p>wie folgt zu HTML umgewandelt:</p>
<pre class="literal-block">
&lt;div class=&quot;image&quot;&gt;
&lt;img alt=&quot;images/biohazard.png&quot; src=&quot;images/biohazard.png&quot; /&gt;
&lt;/div&gt;
</pre>
<p>Der Teil <tt class="docutils literal">images/biohazard.png</tt> bezeichnet den Dateinamen des Bildes, das an dieser Stelle im Dokument erscheinen soll. Beim Bildformat (Format, Größe usw.) gibt es keine Einschränkungen. Wenn das Bild in HTML-Code erscheinen soll und Sie noch weitere Angaben dazu machen möchten, können Sie das wie folgt tun:</p>
<pre class="literal-block">
.. image:: images/biohazard.png
     :height: 100
     :width: 200
     :scale: 50
     :alt: Alternativtext
</pre>
</div>
</div>
</div>
<div class="section" id="verschiedenes">
<h3>Verschiedenes</h3>
<p>Es folgen ein paar Tipps, die für Plone-Entwickler hilfreich sein könnten.</p>
<div class="section" id="alle-globalen-definitionen-im-haupt-template">
<h4>Alle globalen Definitionen im Haupt-Template</h4>
<p>Tabelle A.2 listet alle globalen Definitionen im Haupt-Template auf, zusammen mit dem Code, der sie definiert, und einer Beschreibung. Wie immer gilt, dass dies keine starre Liste ist, sondern eine, die sich im Laufe der Zeit vermutlich verändern wird. Daher empfehle ich, einen Blick in den Quellcode zu werfen. Diese Zusammenstellung stammt aus <tt class="docutils literal">CMFPlone/skins/main_template/globale_defines.pt</tt>.</p>
<p>Diese Definitionen werden vorwiegend in Page Templates benutzt und stellen nützliche Abkürzungen dar. Beispiel:</p>
<pre class="literal-block">
&lt;a href=&quot;&quot; tal:attributes=&quot;portal_url&quot;&gt;Url to the portal&lt;/a&gt;
</pre>
<p>Tabelle A.2. Globale Definitionen im Haupt-Template</p>
<table border="1" class="docutils">
<colgroup>
<col width="28%" />
<col width="33%" />
<col width="39%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Name</th>
<th class="head">Code</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><em>utool</em></td>
<td><em>nocall:here/portal_url;</em></td>
<td>Das Werkzeug portal_url.</td>
</tr>
<tr><td><em>portal</em></td>
<td><em>utool/getPortalObject;</em></td>
<td>Das eigentliche Portal-Objekt.</td>
</tr>
<tr><td><em>portal_object</em></td>
<td><em>nocall:portal;</em></td>
<td>Ein weiterer Name für das
Portal.</td>
</tr>
<tr><td><em>portal_url</em></td>
<td><em>utool;</em></td>
<td>Ein weiterer Name für das
Werkzeug portal_url.</td>
</tr>
<tr><td><em>mtool</em></td>
<td><em>nocall:portal/</em>
<em>portal_membership;</em></td>
<td>Das Werkzeug membership.</td>
</tr>
<tr><td><em>gtool</em></td>
<td><em>nocall:portal/</em>
<em>portal_groups |</em>
<em>nothing;</em></td>
<td>Das Werkzeug groups, falls
vorhanden.</td>
</tr>
<tr><td><em>dtool</em></td>
<td><em>nocall:portal/</em>
<em>portal_groupdata |</em>
<em>nothing;</em></td>
<td>Das Werkzeug groups data
tool, falls vorhanden.</td>
</tr>
<tr><td><em>atool</em></td>
<td><em>nocall:portal/</em>
<em>portal_actions;</em></td>
<td>Das Werkzeug portal_actions.</td>
</tr>
<tr><td><em>aitool</em></td>
<td><em>nocall:portal/</em>
<em>portal_actionicons |</em>
<em>nothing;</em></td>
<td>Das Werkzeug
portal_actionicons.</td>
</tr>
<tr><td><em>putils</em></td>
<td><em>nocall:portal/</em>
<em>plone_utils;</em></td>
<td>Das Werkzeug utils.</td>
</tr>
<tr><td><em>wtool</em></td>
<td><em>nocall:portal/</em>
<em>portal_workflow;</em></td>
<td>Das Werkzeug portal_workflow.</td>
</tr>
<tr><td><em>ifacetool</em></td>
<td><em>nocall:portal/</em>
<em>plone_interface |</em>
<em>nothing;</em></td>
<td>Das Werkzeug
portal_interface, falls
vorhanden.</td>
</tr>
<tr><td><em>portal_title</em></td>
<td><em>portal_object/Title;</em></td>
<td>Der Portaltitel.</td>
</tr>
<tr><td><em>object_title</em></td>
<td><em>here/Title;</em>
<em>here/Title;</em></td>
<td>Der Titel des aktuellen
Objekts.</td>
</tr>
<tr><td><em>member</em></td>
<td><em>mtool/</em>
<em>getAuthenticatedMember;</em></td>
<td>Das aktuelle Mitglied.</td>
</tr>
<tr><td><em>checkPermission</em></td>
<td><em>nocall:</em>
<em>mtool/checkPermission;</em></td>
<td>Die Funktion checkPermission
des membership-Werkzeugs.</td>
</tr>
<tr><td><em>membersfolder</em></td>
<td><em>mtool/getMembersFolder;</em></td>
<td>Der Members-Ordner des
aktuellen Mitglieds, falls
vorhanden.</td>
</tr>
<tr><td><em>isAnon</em></td>
<td><em>mtool/isAnonymousUser;</em></td>
<td>Boolescher Wert, falls der
Benutzer anonym ist.</td>
</tr>
<tr><td><em>actions</em></td>
<td><em>python: portal.</em>
<em>portal_actions.</em>
<em>listFilteredActionsFor</em>
<em>(here);</em></td>
<td>Die Aktionen am aktuellen
Ort.</td>
</tr>
<tr><td><em>keyed_actions</em></td>
<td><em>python: portal.</em>
<em>keyFilteredActions</em>
<em>(actions);</em></td>
<td>Die Liste aller Aktionen mit
einer ID.</td>
</tr>
<tr><td><em>user_actions</em></td>
<td><em>actions/user;</em></td>
<td>Aktionen für den Benutzer.</td>
</tr>
<tr><td><em>workflow_actions</em></td>
<td><em>actions/workflow;</em></td>
<td>Workflow-Aktionen.</td>
</tr>
<tr><td><em>folder_actions</em></td>
<td><em>actions/folder;</em></td>
<td>Ordner-Aktionen.</td>
</tr>
<tr><td><em>global_actions</em></td>
<td><em>actions/global;</em></td>
<td>Globale Aktionen.</td>
</tr>
<tr><td><em>portal_tabs</em></td>
<td><em>actions/portal_tabs|</em>
<em>nothing;</em></td>
<td>Portalreiter-Aktionen.</td>
</tr>
<tr><td><em>wf_state</em></td>
<td><em>python:wtool.getInfoFor</em>
<em>(here,'review_state',</em>
<em>None);</em></td>
<td>Workflow-Zustand des
aktuellen Objekts.</td>
</tr>
<tr><td><em>portal_properties</em></td>
<td><em>portal/</em>
<em>portal_properties;</em></td>
<td>Das Objekt portal_properties.</td>
</tr>
<tr><td><em>site_properties</em></td>
<td><em>portal_properties/</em>
<em>site_properties;</em></td>
<td>Das Objekt site_properties.</td>
</tr>
<tr><td><em>ztu</em></td>
<td><em>modules/ZTUtils;</em></td>
<td>Das Modul ZTUtils, ein
nützliches Hilfsmodul.</td>
</tr>
<tr><td><em>actions</em></td>
<td><em>options/actions|</em>
<em>actions;</em></td>
<td>Die über das Template
explizit übergebenen Aktionen.</td>
</tr>
<tr><td><em>wf_actions</em></td>
<td><em>workflow_actions;</em></td>
<td>Ein weiterer Name für
Workflow-Aktionen.</td>
</tr>
<tr><td><em>isFolder</em></td>
<td><em>python:here.</em>
<em>getTypeInfo().getId()</em>
<em>in site_properties.</em>
<em>use_folder_tabs;</em></td>
<td>Boolescher Wert, falls der
Kontext ein Ordner ist.</td>
</tr>
<tr><td><em>template_id</em></td>
<td><em>options/template_id |</em>
<em>template/getId |</em>
<em>| nothing;</em></td>
<td>Die ID des aktuellen
Templates.</td>
</tr>
<tr><td><em>slots_mapping</em></td>
<td><em>options/slots_mapping|</em>
<em>here/prepare_slots|</em>
<em>nothing;</em></td>
<td>Die Slots-Abbildung.</td>
</tr>
<tr><td><em>Iterator</em></td>
<td><em>python:modules</em>
<em>['Products.CMFPlone']</em>
<em>.IndexIterator;</em></td>
<td>Ein Iterator, verwendet in
Templates.</td>
</tr>
<tr><td><em>tabindex</em></td>
<td><em>python:Iterator</em>
<em>(pos=30000);</em></td>
<td>Der Tabindex-Iterator für
Formulare.</td>
</tr>
<tr><td><em>here_url</em></td>
<td><em>here/absolute_url;</em></td>
<td>Aktuelle absolute_url.</td>
</tr>
<tr><td><em>sl</em></td>
<td><em>slots_mapping/left;</em></td>
<td>Abbildung für linke Slots.</td>
</tr>
<tr><td><em>sr</em></td>
<td><em>slots_mapping/right;</em></td>
<td>Abbildung für rechte Slots.</td>
</tr>
<tr><td><em>default_language</em></td>
<td><em>site_properties/</em>
<em>default_language|</em>
<em>nothing;</em></td>
<td>Standardsprache in
site_properties.</td>
</tr>
<tr><td><em>allowed_types</em></td>
<td><em>here/getAllowedTypes;</em></td>
<td>Erlaubte Inhaltstypen in
diesem Ordner.</td>
</tr>
<tr><td><em>is_editable</em></td>
<td><em>python:here.</em>
<em>showEditableBorder(</em>
<em>template_id=</em>
<em>template_id,</em>
<em>allowed_types=</em>
<em>allowed_types,</em>
<em>actions=actions );</em></td>
<td>Gibt an, ob das aktuelle
Objekt bearbeitet werden kann
und eine grüne Umrandung
haben soll.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="api-zu-datetime">
<h4>API zu DateTime</h4>
<p>Ich habe im ganzen Buch schon <tt class="docutils literal">DateTime</tt>-Objekte verwendet, ohne ihre API zu erklären. In Plone werden solche Objekte sehr häufig verwendet, z.B. bei der Suche mit Datumsangaben oder bei deren Darstellung.</p>
<p>Um ein <tt class="docutils literal">DateTime</tt>-Objekt zu erstellen, übergeben Sie einen String, der auch als Datumsangabe interpretiert werden kann:</p>
<pre class="literal-block">
&gt;&gt;&gt; from DateTime import DateTime
&gt;&gt;&gt; d = DateTime('2004/12/01')
&gt;&gt;&gt; d.month()
12
</pre>
<p>Auf diesen Datumsobjekten können Sie dann gewisse Operationen ausführen. Um z.B. die Differenz zwischen zwei Datumsobjekten zu bilden, machen Sie Folgendes:</p>
<pre class="literal-block">
&gt;&gt;&gt; x = DateTime('2004/11/02')
&gt;&gt;&gt; z = DateTime('2004/11/30')
&gt;&gt;&gt; z - x
28.0
</pre>
<p>Dieser Abschnitt enthält nur die Kurzfassung des APIs. Die vollständige Dokumentation finden Sie im <tt class="docutils literal">DateTime</tt>-Verzeichnis Ihrer Zope-Installation.</p>
<p><tt class="docutils literal">DateTime</tt>-Objekte repräsentieren Zeitpunkte und bieten eine Schnittstelle zu ihrer Darstellung, ohne dass dabei der absolute Wert des Objekts verändert wird.</p>
<p>Sie können <tt class="docutils literal">DateTime</tt>-Objekte aus einer Vielzahl von Strings oder aus numerischen Daten erzeugen, oder Sie erzeugen sie aus anderen <tt class="docutils literal">DateTime</tt>-Objekten. Sie können ihre Darstellung an viele Zeitzonen anpassen, und Sie können <tt class="docutils literal">DateTime</tt>-Objekte für eine gegebene Zeitzone erstellen.</p>
<p>Außerdem verfügen <tt class="docutils literal">DateTime</tt>-Objekte zum Teil über ein zahlenähnliches Verhalten:</p>
<ul class="simple">
<li>Zwei <tt class="docutils literal">DateTime</tt>-Objekte können voneinander subtrahiert werden, um die Zeitdauer in Tagen dazwischen zu beschreiben.</li>
<li>Ein <tt class="docutils literal">DateTime</tt>-Objekt kann zu einer positiven oder negativen Zahl addiert werden, um ein neues <tt class="docutils literal">DateTime</tt>-Objekt plus der angegebenen Zahl von Tagen zu erzeugen.</li>
<li>Eine positive oder negative Zahl kann zu einem <tt class="docutils literal">DateTime</tt>-Objekt addiert werden, um ein neues <tt class="docutils literal">DateTime</tt>-Objekt plus der angegebenen Zahl von Tagen zu erzeugen.</li>
<li>Eine positive oder negative Zahl kann von einem <tt class="docutils literal">DateTime</tt>-Objekt subtrahiert werden, um ein neues <tt class="docutils literal">DateTime</tt>-Objekt minus der angegebenen Zahl von Tagen zu erzeugen.</li>
</ul>
<p>Sie können <tt class="docutils literal">DateTime</tt>-Objekte zu der Anzahl von Tagen seit dem 1. Januar 1901 im Integer-, Long- oder Float-Typ konvertieren, indem Sie die normalen Funktionen <tt class="docutils literal">int</tt>, <tt class="docutils literal">long</tt> und <tt class="docutils literal">float</tt> verwenden. Beachten Sie aber, dass diese Funktionen die Anzahl der Tage in der Greenwich Mean Time (GMT) angeben und nicht in der lokalen Zeitzone Ihres Rechners. Außerdem bieten <tt class="docutils literal">DateTime</tt>-Objekte auch einen Zugriff auf ihren Wert in einem Float-Format, der mit dem <tt class="docutils literal">time</tt>-Modul in Python verwendet werden kann, vorausgesetzt, dass dieser Wert im Zeitraum des auf der Epoche basierenden <tt class="docutils literal">time</tt>-Moduls liegt.</p>
<p>Ein <tt class="docutils literal">DateTime</tt>-Objekt sollte als unveränderlich betrachtet werden. Alle Umwandlungs- und andere Operationen geben ein neues <tt class="docutils literal">DateTime</tt>-Objekt zurück und verändern nicht das gegebene Objekt.</p>
<p>Um ein <tt class="docutils literal">DateTime</tt>-Objekt zu erzeugen, übergeben Sie einen String, der ein gültiges Datum beschreibt. Wenn Sie kein Argument übergeben, wird ein Objekt für den aktuellen Zeitpunkt erzeugt. Der Wert eines <tt class="docutils literal">DateTime</tt>-Objekts wird immer in absoluter UTC-Zeit verwaltet und wird im Kontext einer Zeitzone dargestellt, die auf den Argumenten basiert, die bei der Erzeugung des Objekts angegeben wurden.</p>
<p>Tabelle A.3 beschreibt alle Methoden, die bei einem <tt class="docutils literal">DateTime</tt>-Objekt aufgerufen werden können.</p>
<p>Tabelle A.3 Verfügbare Methoden auf einem <tt class="docutils literal">DateTime</tt>-Objekt</p>
<table border="1" class="docutils">
<colgroup>
<col width="30%" />
<col width="70%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Methode</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><em>aMonth()</em></td>
<td>Gibt den abgekürzten Monatsnamen zurück.</td>
</tr>
<tr><td><em>pCommon()</em></td>
<td>Gibt eine String-Darstellung des Objektwerts im
Format Jan. 13, 2005 1:41 pm zurück.</td>
</tr>
<tr><td><em>minute()</em></td>
<td>Gibt die Minute zurück.</td>
</tr>
<tr><td><em>isLeapYear()</em></td>
<td>Gibt zurück, ob das aktuelle Jahr (im Kontext der
Zeitzone des Objekts) ein Schaltjahr ist.</td>
</tr>
<tr><td><em>pMonth()</em></td>
<td>Gibt den (mit Punkt) abgekürzten Monatsnamen zurück.</td>
</tr>
<tr><td><em>DayOfWeek()</em></td>
<td>Kompatibilität: siehe <tt class="docutils literal">Day()</tt>.</td>
</tr>
<tr><td><em>Day_()</em></td>
<td>Kompatibilität: siehe <tt class="docutils literal">pDay()</tt>.</td>
</tr>
<tr><td><em>isCurrentDay()</em></td>
<td>Gibt im Kontext der Zeitzone dieses Objekts zurück,
ob dieses Objekt einen Zeitpunkt am heutigen Tag
darstellt.</td>
</tr>
<tr><td><em>Mon()</em></td>
<td>Kompatibilität: siehe <tt class="docutils literal">aMonth()</tt>.</td>
</tr>
<tr><td><em>hour()</em></td>
<td>Gibt die Stunde in 24-Stunden-Darstellung zurück.</td>
</tr>
<tr><td><em>Date()</em></td>
<td>Gibt den Datums-String zu dem Objekt zurück.</td>
</tr>
<tr><td><em>aCommonZ()</em></td>
<td>Gibt eine String-Darstellung des Objektwerts im
Format Jan 13, 2005 1:40 pm GMT+1 zurück.</td>
</tr>
<tr><td><em>fCommonZ()</em></td>
<td>Gibt eine String-Darstellung des Objektwerts im
Format January 13, 2005 1:42 pm GMT+1 zurück.</td>
</tr>
<tr><td><em>isCurrentYear()</em></td>
<td>Gibt im Kontext der Zeitzone dieses Objekts zurück,
ob dieses Objekt einen Zeitpunkt im laufenden Jahr
darstellt.</td>
</tr>
<tr><td><em>AMPMMinutes()</em></td>
<td>Gibt den Zeit-String für ein Objekt ohne Sekunden
mit Angabe von am/pm zurück.</td>
</tr>
<tr><td><em>dd()</em></td>
<td>Gibt den Tag als String mit zwei Ziffern zurück.</td>
</tr>
<tr><td><em>TimeMinutes()</em></td>
<td>Gibt den Zeit-String für ein Objekt ohne Sekunden
in 24-Stunden-Darstellung zurück.</td>
</tr>
<tr><td><em>h_24()</em></td>
<td>Gibt die Stunde in 24-Stunden-Darstellung zurück.</td>
</tr>
<tr><td><em>isPast()</em></td>
<td>Gibt zurück, ob dieses Objekt einen Zeitpunkt vor
dem Zeitpunkt dieses Aufrufs darstellt.</td>
</tr>
<tr><td><em>dow()</em></td>
<td>Gibt den Wochentag als Integer zurück, wobei Sonntag
gleich 0 ist.</td>
</tr>
<tr><td><em>isFuture()</em></td>
<td>Gibt zurück, ob dieses Objekt einen Zeitpunkt nach
dem Zeitpunkt dieses Aufrufs darstellt.</td>
</tr>
<tr><td><em>pCommonZ()</em></td>
<td>Gibt eine String-Darstellung des Objektwerts im
Format Jan. 13, 2005 2:01 pm GMT+1 zurück.</td>
</tr>
<tr><td><em>timezone()</em></td>
<td>Gibt die Zeitzone des Objekts zurück.</td>
</tr>
<tr><td><em>h_12()</em></td>
<td>Gibt die Stunde in 12-Stunden-Darstellung zurück.</td>
</tr>
<tr><td><em>PreciseTime()</em></td>
<td>Gibt eine genaue String-Darstellung des Objekts
zurück.</td>
</tr>
<tr><td><em>isCurrentMinute()</em></td>
<td>Gibt im Kontext der Zeitzone dieses Objekts zurück,
ob dieses Objekt einen Zeitpunkt in der laufenden
Minute darstellt.</td>
</tr>
<tr><td><em>rfc822()</em></td>
<td>Gibt den Zeitpunkt im Format RFC 822 zurück.</td>
</tr>
<tr><td><em>equalTo(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl, wie sie Pythons <tt class="docutils literal">time</tt>-Modul
zurückgibt. Gibt zurück, ob das Objekt einen
Zeitpunkt beschreibt, der identisch mit dem
angegebenen Objekt ist.</td>
</tr>
<tr><td><em>yy()</em></td>
<td>Gibt das Kalenderjahr als String mit zwei Ziffern
zurück.</td>
</tr>
<tr><td><em>mm()</em></td>
<td>Gibt den Monat als String mit zwei Ziffern zurück.</td>
</tr>
<tr><td><em>Mon_()</em></td>
<td>Kompatibilität: siehe <tt class="docutils literal">pMonth()</tt>.</td>
</tr>
<tr><td><em>toZone(z)</em></td>
<td>Gibt ein <tt class="docutils literal">DateTime</tt>-Objekt mit dem aktuellen Wert
in der angegebenen Zeitzone zurück.</td>
</tr>
<tr><td><em>earliestTime()</em></td>
<td>Gibt im Kontext der Zeitzone des Objekts ein neues
<tt class="docutils literal">DateTime</tt>-Objekt zurück, das den frühestmöglichen
Zeitpunkt darstellt (in ganzen Sekunden), der immer
noch auf den Tag des aktuellen Objekts fällt.</td>
</tr>
<tr><td><em>aDay()</em></td>
<td>Gibt den abgekürzten Wochentag zurück.</td>
</tr>
<tr><td><em>dayOfYear()</em></td>
<td>Gibt  Kontext der Zeitzone des Objekts den Tag des
Jahres zurück.</td>
</tr>
<tr><td><em>latestTime()</em></td>
<td>Gibt im Kontext der Zeitzone des Objekts ein neues
<tt class="docutils literal">DateTime</tt>-Objekt zurück, das den spätestmöglichen
Zeitpunkt darstellt (in ganzen Sekunden), der immer
noch auf den Tag des aktuellen Objekts fällt.</td>
</tr>
<tr><td><em>notEqualTo(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl, wie sie Pythons <tt class="docutils literal">time</tt>-Modul
zurückgibt. Gibt zurück, ob das Objekt einen
Zeitpunkt beschreibt, der verschieden vom angegebenen
Objekt ist.</td>
</tr>
<tr><td><em>PreciseAMPM()</em></td>
<td>Gibt eine genaue String-Darstellung des Objekts mit
Angabe von am/pm zurück.</td>
</tr>
<tr><td><em>day()</em></td>
<td>Gibt den Tag als ganze Zahl zurück.</td>
</tr>
<tr><td><em>timeTime()</em></td>
<td>Gibt den Zeitpunkt als Fließkommazahl in UTC zurück,
dem Format, das Python im <tt class="docutils literal">time</tt>-Modul
verwendet. Beachten Sie, dass man mit <tt class="docutils literal">DateTime</tt>
Zeitpunkte erstellen kann, die keine Entsprechung im
Modul <tt class="docutils literal">time</tt> haben, und in solchen Fällen wird der
Fehler <tt class="docutils literal">DateTimeError</tt> ausgelöst. Im Allgemeinen
muss der Wert eines <tt class="docutils literal">DateTime</tt>-Objekts zwischen dem
1. Januar 1970 (oder der Epoche auf Ihrem lokalen
Rechner) und dem 1. Januar 2038 liegen, um einen
gültigen Wert im Stil von <tt class="docutils literal">time.time()</tt> zu
erzeugen.</td>
</tr>
<tr><td><em>ampm()</em></td>
<td>Gibt den passenden Zeitzusatz (am bzw. pm) zurück.</td>
</tr>
<tr><td><em>greaterThan(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl,
wie sie z.B. von Pythons <tt class="docutils literal">time</tt>-Modul erzeugt wird.
Gibt zurück, ob das Objekt einen Zeitpunkt darstellt,
der größer als das angegebene Objekt ist.</td>
</tr>
<tr><td><em>month()</em></td>
<td>Gibt den Monat des Objekts als ganze Zahl zurück.</td>
</tr>
<tr><td><em>AMPM()</em></td>
<td>Gibt den Zeit-String zu einem Objekt auf die
nächste Sekunde genau zurück.</td>
</tr>
<tr><td><em>second()</em></td>
<td>Gibt die Sekunde zurück.</td>
</tr>
<tr><td><em>parts()</em></td>
<td>Gibt ein Tupel mit dem Kalenderjahr, Monat, Tag, der
Stunde, Minute, Sekunde und Zeitzone des Objekts
zurück.</td>
</tr>
<tr><td><em>greaterThanEqualTo(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl,
wie sie z.B. von Pythons <tt class="docutils literal">time</tt>-Modul erzeugt wird.
Gibt zurück, ob das Objekt einen Zeitpunkt darstellt,
der größer oder gleich dem angegebenen Objekt ist.</td>
</tr>
<tr><td><em>lessThanEqualTo(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl,
wie sie z.B. von Pythons <tt class="docutils literal">time</tt>-Modul erzeugt wird.
Gibt zurück, ob das Objekt einen Zeitpunkt darstellt,
der kleiner oder gleich dem angegebenen Objekt ist.</td>
</tr>
<tr><td><em>isCurrentHour()</em></td>
<td>Gibt im Kontext der Zeitzone des Objekts zurück, ob
dieses Objekt einen Zeitpunkt darstellt, der in die
laufende Stunde fällt.</td>
</tr>
<tr><td><em>aCommon()</em></td>
<td>Gibt eine String-Darstellung des Objektwertes im
Format Jan 13, 2005 4:22 pm zurück.</td>
</tr>
<tr><td><em>dow_1()</em></td>
<td>Gibt den Wochentag als ganze Zahl zurück, wobei,
anders als bei der Methode <tt class="docutils literal">dow</tt>, Sonntag gleich
1 ist.</td>
</tr>
<tr><td><em>Day()</em></td>
<td>Gibt den vollen Namen des Wochentages zurück.</td>
</tr>
<tr><td><em>fCommon()</em></td>
<td>Gibt eine String-Darstellung des Objektwertes im
Format January 13, 2005 4:29 pm zurück.</td>
</tr>
<tr><td><em>Month()</em></td>
<td>Gibt den vollen Monatsnamen zurück.</td>
</tr>
<tr><td><em>isCurrentMonth()</em></td>
<td>Gibt im Kontext der Zeitzone des Objekts zurück, ob
dieses Objekt einen Zeitpunkt darstellt, der in den
laufenden Monat fällt.</td>
</tr>
<tr><td><em>year()</em></td>
<td>Gibt das Kalenderjahr des Objekts zurück.</td>
</tr>
<tr><td><em>lessThan(t)</em></td>
<td>Vergleicht dieses <tt class="docutils literal">DateTime</tt>-Objekt mit einem
anderen <tt class="docutils literal">DateTime</tt>-Objekt oder einer
Fließkommazahl,
wie sie z.B. von Pythons <tt class="docutils literal">time</tt>-Modul erzeugt wird.
Gibt zurück, ob das Objekt einen Zeitpunkt darstellt,
der kleiner als das angegebene Objekt ist.</td>
</tr>
<tr><td><em>Time()</em></td>
<td>Gibt den Zeit-String für ein Objekt bis auf die
nächste Sekunde genau zurück.</td>
</tr>
<tr><td><em>pDay()</em></td>
<td>Gibt den (mit Punkt) abgekürzten Namen des
Wochentages zurück.</td>
</tr>
</tbody>
</table>
<p>Tabelle A.4 beschreibt alle eingebauten Methoden von <tt class="docutils literal">DateTime</tt>-Objekten.</p>
<p>Tabelle A.4. Eingebaute Methoden von <tt class="docutils literal">DateTime</tt>-Objekten</p>
<table border="1" class="docutils">
<colgroup>
<col width="30%" />
<col width="70%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Methode</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><em>`dateTime`</em></td>
<td>Wandelt ein <tt class="docutils literal">DateTime</tt>-Objekt in einen String um,
der wie ein Python-Ausdruck aussieht.</td>
</tr>
<tr><td><em>str(dateTime)</em></td>
<td>Wandelt ein <tt class="docutils literal">DateTime</tt>-Objekt in einen String um.</td>
</tr>
<tr><td><em>cmp(dateTime, other)</em></td>
<td>Vergleicht ein <tt class="docutils literal">DateTime</tt>-Objekt mit einem anderen
oder mit einer Fließkommazahl, wie
<tt class="docutils literal">time.time()</tt> sie zurückgibt.</td>
</tr>
<tr><td><em>hash(dateTime)</em></td>
<td>Berechnet den Hash-Wert eines <tt class="docutils literal">DateTime</tt>-Objekts.</td>
</tr>
</tbody>
</table>
<p>Tabelle A.5. beschreibt generische Dienste, die <tt class="docutils literal">DateTime</tt> unterstützt.</p>
<p>Tabelle A.5. Generische <tt class="docutils literal">DateTime</tt>-Funktionen</p>
<table border="1" class="docutils">
<colgroup>
<col width="30%" />
<col width="70%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Methode</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><em>dateTime + anderes</em></td>
<td>Ein <tt class="docutils literal">DateTime</tt>-Objekt darf zu einer Zahl addiert
werden und umgekehrt. Zwei <tt class="docutils literal">DateTime</tt>-Objekte
können aber nicht addiert werden.</td>
</tr>
<tr><td><em>dateTime - anderes</em></td>
<td>Von einem <tt class="docutils literal">DateTime</tt>-Objekt kann man entweder ein
anderes <tt class="docutils literal">DateTime</tt>-Objekt oder eine Zahl
subtrahieren, aber ein <tt class="docutils literal">DateTime</tt>-Objekt kann nicht
von einer Zahl subtrahiert werden.</td>
</tr>
<tr><td><em>anderes + dateTime</em></td>
<td>Addiert <tt class="docutils literal">dateTime</tt> zu <tt class="docutils literal">anderes</tt>. Ein
<tt class="docutils literal">DateTime</tt>-Objekt kann zu einer Zahl addiert werden
und umgekehrt, aber zwei <tt class="docutils literal">DateTime</tt>-Objekte können
nicht addiert werden.</td>
</tr>
<tr><td><em>int(dateTime)</em></td>
<td>Wandelt in eine Integer-Anzahl von Tagen seit dem
1. Januar 1901 (GMT) um.</td>
</tr>
<tr><td><em>long(dateTime)</em></td>
<td>Wandelt in eine Long-Anzahl von Tagen seit dem
1. Januar 1901 (GMT) um.</td>
</tr>
<tr><td><em>float(dateTime)</em></td>
<td>Wandelt in eine Float-Anzahl von Tagen seit dem
1. Januar 1901 (GMT) um.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="workflows-in-python-schreiben">
<h4>Workflows in Python schreiben</h4>
<p>Dieser Abschnitt behandelt die API zum Schreiben eines Workflows in Python, damit Sie nicht über das Web entwickeln müssen. Die folgenden Abschnitte enthalten eine verkürzte Liste der Methoden, die Sie benötigen, um einen Workflow zu erstellen. Um eine umfassende Liste zu erhalten, müssen Sie den Quellcode lesen, den Sie im Verzeichnis <tt class="docutils literal">Products/DCWorkflow</tt> Ihrer Plone-Instanz finden.</p>
<p>Das Schreiben eines Workflows beginnt mit dem Erstellen einer Definition. Diese kann man wie folgt importieren:</p>
<pre class="literal-block">
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
</pre>
<p>Erzeugen Sie eine neue Instanz dieser Definition, und geben Sie ihr eine ID:</p>
<pre class="literal-block">
wf = DCWorkflowDefinition(id)
</pre>
<p>Nun haben Sie einen Workflow, den Sie manipulieren können. Im Folgenden ist <tt class="docutils literal">wf</tt> die Workflow-Definition, die ich gerade erzeugt habe.</p>
<div class="section" id="zustande">
<h5>Zustände</h5>
<p>Zustände verfügen über die folgenden Methoden:</p>
<ul class="simple">
<li><strong>addState(Id)</strong>: Fügt einen Zustand mit der gegebenen ID hinzu. Zustände müssen Sie erst hinzufügen, bevor Sie sie manipulieren können.</li>
<li><strong>setInitialState(Id)</strong>: Setzt den Ausgangszustand für diesen Workflow.</li>
<li><strong>setProperties([key=value,...])</strong>: Eine Reihe von Schlüssel/Wert-Paaren, die auf Eigenschaften eines Zustands abgebildet werden. Um z.B. die Übergänge auf Veröffentlichen und Zurückweisen zu setzen, verwenden Sie <tt class="docutils literal"><span class="pre">setProperties(transitions=('publish',</span> <span class="pre">'reject'))</span></tt>..</li>
<li><strong>setPermission(recht, aquirieren, (rolle, [rolle...]))</strong>: Der Name des Rechts, ob <tt class="docutils literal">akquirieren</tt> aktiviert ist oder nicht (ein boolescher Wert) und dann ein Tupel aller Rollen, für die das gilt.</li>
</ul>
<p>Um auf diese Methoden zuzugreifen, verwenden Sie das <tt class="docutils literal">states</tt>-Objekt im Workflow-Objekt. Verwenden Sie z.B. <tt class="docutils literal"><span class="pre">wf.states.addState('new')</span></tt>.</p>
</div>
<div class="section" id="ubergange">
<h5>Übergänge</h5>
<p>Übergänge verfügen über die folgenden Methoden:</p>
<ul class="simple">
<li><strong>addTransition(id)</strong>: Fügt einen Übergang mit der gegebenen ID hinzu. Übergänge müssen Sie erst hinzufügen, bevor Sie sie manipulieren können.</li>
<li><strong>setProperties([key=value,...])</strong>: Eine Reihe von Schlüssel/Wert-Paaren, die auf Eigenschaften eines Übergangs abgebildet werden.</li>
</ul>
<p>Um auf diese Methoden zuzugreifen, verwenden Sie das <tt class="docutils literal">transitions</tt>-Objekt im Workflow-Objekt. Verwenden Sie z.B. <tt class="docutils literal"><span class="pre">wf.transitions.addTransition('reject')</span></tt>.</p>
</div>
<div class="section" id="variablen">
<h5>Variablen</h5>
<p>Variablen verfügen über die folgenden Methoden:</p>
<ul class="simple">
<li><strong>addVariable(id)</strong>: Fügt eine Variable mit der gegebenen ID hinzu. Variablen müssen Sie erst hinzufügen, bevor Sie sie manipulieren können.</li>
<li><strong>setStateVar(id)</strong>: Setzt den Variablennamen, der normalerweise <tt class="docutils literal">review_state</tt> ist.</li>
<li><strong>setProperties([key=value,...])</strong>: Eine Reihe von Schlüssel/Wert-Paaren, die auf Eigenschaften einer Variablen abgebildet werden.</li>
</ul>
<p>Um auf diese Methoden zuzugreifen verwenden Sie das <tt class="docutils literal">variables</tt>-Objekt im Workflow-Objekt. Verwenden Sie z.B. <tt class="docutils literal"><span class="pre">wf.transitions.addVariable('action')</span></tt>.</p>
</div>
<div class="section" id="sonstige">
<h5>Sonstige</h5>
<p>Worklists verhalten sich hier wie Übergänge, Variablen und Zustände. Um eine Worklist hinzuzufügen, rufen Sie <tt class="docutils literal">addWorklist</tt> auf. Eine weitere nützliche Methode besteht darin, Rechte zu der Liste der verwalteten Rechte für den Workflow hinzuzufügen. Das bewerkstelligt man mit der Methode <tt class="docutils literal">addManagedPermission(permission name)</tt>.</p>
</div>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appB.rst">
    <title>Anhang B: Code-Listings</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appB.rst</link>
    <description>Dieser Anhang enthält Listings von Code, wie er im restlichen Buch verwendet wird. Manche dieser Skripten, die extern ausgeführt werden, verlangen, dass bei Ihnen das Verzeichnis ``/Zope/lib/python`` auf Ihrem Python-Pfad liegt. In Anhang A wird das näher beschrieben. All diese Skripten werden im Buch detailliert beschrieben.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Anhang B</h2>
<h3 class="subtitle">Code-Listings</h3>
<p>Dieser Anhang enthält Listings von Code, wie er im restlichen Buch verwendet wird. Manche dieser Skripten, die extern ausgeführt werden, verlangen, dass bei Ihnen das Verzeichnis <tt class="docutils literal">/Zope/lib/python</tt> auf Ihrem Python-Pfad liegt. In Anhang A wird das näher beschrieben. All diese Skripten werden im Buch detailliert beschrieben.</p>
<div class="section" id="kapitel-5">
<h3>Kapitel 5</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 5.</p>
<div class="section" id="page-template-test-context">
<h4>Page Template: test_context</h4>
<p>Listing B.1 zeigt alle Variablen, die in Page Templates verfügbar sind. Es kann verwendet werden, um nützliche Informationen bei der Fehlersuche auszugeben.</p>
<p>Listing B.1. test_context</p>
<pre class="literal-block">
&lt;html&gt;
  &lt;head /&gt;
  &lt;body&gt;
    &lt;h1&gt;Debug information&lt;/h1&gt;
  &lt;h2&gt;CONTEXTS&lt;/h2&gt;
  &lt;ul&gt;
    &lt;tal:block
        tal:repeat=&quot;item CONTEXTS&quot;&gt;
    &lt;li
        tal:condition=&quot;python: item != 'request'&quot;
        tal:define=&quot;context CONTEXTS;&quot;&gt;
            &lt;b tal:content=&quot;item&quot; /&gt;
            &lt;span tal:replace=&quot;python: context[item]&quot; /&gt;
    &lt;/li&gt;
    &lt;/tal:block&gt;
  &lt;/ul&gt;
  &lt;h2&gt;REQUEST&lt;/h2&gt;
  &lt;p tal:replace=&quot;structure request&quot; /&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="page-template-user-info-1">
<h4>Page Template: user_info (1)</h4>
<p>user_info (1) ist ein rohes Page Template, das nicht dem Plone-Stil folgt und Benutzerinformationen ausgibt, wie in Listing B.2 zu sehen ist. Um dieses Template zu benutzen, müssen Sie die Benutzer-ID, die Sie untersuchen möchten, an den Abfrage-String übergeben. Beispiel: <tt class="docutils literal"><span class="pre">user_info?userName=andy</span></tt>.</p>
<p>Listing B.2. user_info (1)</p>
<pre class="literal-block">
&lt;html&gt;

&lt;body&gt;
&lt;div
  tal:omit-tag=&quot;&quot;
  tal:define=&quot;
    userName request/userName|nothing;
    userObj python: here.portal_membership.getMemberById(userName);
    getPortrait nocall: here/portal_membership/getPersonalPortrait;
    getFolder nocall: here/portal_membership/getHomeFolder
    &quot;&gt;
     &lt;p tal:condition=&quot;not: userName&quot;&gt;
        No username selected.
     &lt;/p&gt;
     &lt;p tal:condition=&quot;not: userObj&quot;&gt;
       That username does not exist.
     &lt;/p&gt;

     &lt;table tal:condition=&quot;userObj&quot;&gt;
       &lt;tr&gt;
         &lt;td&gt;
          &lt;img src=&quot;&quot;
            tal:replace=&quot;structure python: getPortrait(userName)&quot; /&gt;
          &lt;/td&gt;
          &lt;td&gt;
            &lt;ul&gt;
            &lt;li&gt;
              &lt;i&gt;Username:&lt;/i&gt;
              &lt;span tal:replace=&quot;userName&quot; /&gt;
            &lt;/li&gt;
            &lt;li&gt;
              &lt;i&gt;Full name:&lt;/i&gt;
              &lt;span tal:replace=&quot;userObj/fullname&quot; /&gt;
            &lt;/li&gt;
            &lt;li
              tal:define=&quot;home python: getFolder(userName)&quot;
              tal:condition=&quot;home&quot;&gt;
              &lt;i&gt;Home folder:&lt;/i&gt;
              &lt;a href=&quot;&quot;
                 tal:attributes=&quot;href home/absolute_url&quot;
                 tal:content=&quot;home/absolute_url&quot;&gt;Folder&lt;/a&gt;
            &lt;/li&gt;
            &lt;li&gt;
              &lt;i&gt;Email:&lt;/i&gt;
              &lt;a href=&quot;&quot;
                  tal:define=&quot;email userObj/email&quot;
                  tal:attributes=&quot;href string:mailto:$email&quot;
                  tal:content=&quot;email&quot;&gt;Email&lt;/a&gt;
            &lt;/li&gt;
            &lt;li&gt;
              &lt;i&gt;Last login time:&lt;/i&gt;
              &lt;span tal:replace=&quot;userObj/last_login_time&quot; /&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
       &lt;/td&gt;
     &lt;/tr&gt;
   &lt;/table&gt;

&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
</div>
<div class="section" id="kapitel-6">
<h3>Kapitel 6</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 6.</p>
<div class="section" id="page-template-user-info-2">
<h4>Page Template: user_info (2)</h4>
<p>Listing B.3 enthält eine verfeinerte Version des Page Templates <tt class="docutils literal">user_info</tt> aus Kapitel 5. Den Benutzernamen müssen Sie nicht übergeben, da es über alle Mitglieder Ihrer Site iteriert.</p>
<p>Listing B.3. user_info (2)</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
      lang=&quot;en-US&quot;
      i18n:domain=&quot;plone&quot;
      metal:use-macro=&quot;here/main_template/macros/master&quot;&gt;

&lt;body&gt;
&lt;div metal:fill-slot=&quot;main&quot;&gt;
  &lt;tal:block
    tal:define=&quot;
     getPortrait nocall: here/portal_membership/getPersonalPortrait;
     getFolder nocall: here/portal_membership/getHomeFolder
     &quot;&gt;
    &lt;table&gt;
     &lt;tal:block
      tal:repeat=&quot;userObj here/portal_membership/listMembers&quot;&gt;
       &lt;metal:block
         metal:use-macro=&quot;here/user_section/macros/userSection&quot; /&gt;
     &lt;/tal:block&gt;
    &lt;/table&gt;
 &lt;/tal:block&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="page-template-user-section">
<h4>Page Template: user_section</h4>
<p>Listing B.4 enthält ein Page Template-Makro zum vorherigen Page Template <tt class="docutils literal">user_info</tt> und zeigt bei jedem Aufruf die einzelnen Benutzerangaben.</p>
<p>Listing B.4. user_section</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;userSection&quot;
 tal:define=&quot;userName userObj/getUserName&quot;&gt;
 &lt;tr&gt;
   &lt;td&gt;
     &lt;img src=&quot;&quot;
       tal:replace=&quot;structure python: getPortrait(userName)&quot; /&gt;
   &lt;/td&gt;
   &lt;td tal:define=&quot;prop nocall: userObj/getProperty&quot;&gt;
     &lt;ul&gt;
       &lt;li&gt;
         &lt;i&gt;Username:&lt;/i&gt;
         &lt;span tal:replace=&quot;userName&quot; /&gt;
       &lt;/li&gt;
       &lt;li&gt;
         &lt;i&gt;Full name:&lt;/i&gt;
         &lt;span tal:replace=&quot;python: prop('fullname')&quot; /&gt;
       &lt;/li&gt;
       &lt;li
         tal:define=&quot;home python: getFolder(userName)&quot;
         tal:condition=&quot;home&quot;&gt;
         &lt;a href=&quot;&quot;
         tal:attributes=&quot;href home/absolute_url&quot;&gt;Home Folder&lt;/a&gt;
       &lt;/li&gt;
       &lt;li&gt;
         &lt;i&gt;Email:&lt;/i&gt;
         &lt;a href=&quot;&quot;
           tal:define=&quot;email python: prop('email')&quot;
           tal:attributes=&quot;href string:mailto:$email&quot;
           tal:content=&quot;email&quot;&gt;Email&lt;/a&gt;
       &lt;/li&gt;
       &lt;li&gt;
         &lt;i&gt;Last login time:&lt;/i&gt;
          &lt;span tal:replace=&quot;python: prop('last_login_time')&quot; /&gt;
       &lt;/li&gt;
     &lt;/ul&gt;
   &lt;/td&gt;
 &lt;/tr&gt;
&lt;/div&gt;
</pre>
</div>
<div class="section" id="script-python-google-ad-portlet">
<h4>Script (Python): google_ad_portlet</h4>
<p>Listing B.5 enthält ein Page Template, das Google-Anzeigen in einem Portlet darstellt.</p>
<p>Listing B.5. google_ad_portlet</p>
<pre class="literal-block">
&lt;div metal:define-macro=&quot;portlet&quot;&gt;
    &lt;div class=&quot;portlet&quot;&gt;
&lt;script type=&quot;text/javascript&quot;&gt;&lt;!--
google_ad_client = &quot;yourUniqueValue&quot;;
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = &quot;120x600_as&quot;;
//--&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;
  src=&quot;http://pagead2.googlesyndication.com/pagead/show_ads.js&quot;&gt;
&lt;/script&gt;

   &lt;/div&gt;
&lt;/div&gt;
</pre>
</div>
<div class="section" id="script-python-recently-changed">
<h4>Script (Python): recently_changed</h4>
<p>Listing B.6 enthält ein Skript, das alle ihm übergebenen Objekte untersucht und dann herausfindet, was es Neues gibt. Die Objekte müssen mit dem Parameter <tt class="docutils literal">objects</tt> übergeben werden.</p>
<p>Listing B.6. recently_changed</p>
<pre class="literal-block">
##title=recentlyChanged
##parameters=objects
from DateTime import DateTime

now = DateTime()
difference = 5 # in Tagen
result = []

for object in objects:
  diff = now - object.bobobase_modification_time()
  if diff &lt; difference:
    dct = {&quot;object&quot;:object,&quot;diff&quot;:int(diff)}
    result.append(dct)

return result
</pre>
</div>
<div class="section" id="externe-methode-readfile">
<h4>Externe Methode: readFile</h4>
<p>Listing B.7 zeigt ein Beispiel für eine externe Methode, die eine Datei liest.</p>
<p>Listing B.7. readFile</p>
<pre class="literal-block">
def readFile(self):
   fh = open(r'c:\Program Files\Plone\Data\Extensions\README.txt', 'rb')
   data = fh.read()
   return data
</pre>
</div>
<div class="section" id="python-skript-zpt-py">
<h4>Python-Skript: zpt.py</h4>
<p>Listing B.8 enthält ein Skript, das völlig unabhängig von Plone ist und  das eine Syntax-Prüfung eines Page Templates vornimmt. Das ist hilfreich bei der Fehlersuche von Page Templates, die außerhalb von Plone geschrieben werden.</p>
<p>Listing B.8. zpt.py</p>
<pre class="literal-block">
#!/usr/bin/python
from Products.PageTemplates.PageTemplate import PageTemplate
import sys

def test(file):
    raw_data = open(file, 'r').read()
    pt = PageTemplate()
    pt.write(raw_data)
    if pt._v_errors:
        print &quot;*** Error in:&quot;, file
        for error in pt._v_errors[1:]:
            print error

if __name__=='__main__':
    if len(sys.argv) &lt; 2:
        print &quot;python check.py file [files...]&quot;
        sys.exit(1)
    else:
        for arg in sys.argv[1:]:
            test(arg)
</pre>
</div>
<div class="section" id="page-template-feedbackform">
<h4>Page Template: feedbackForm</h4>
<p>Listing B.9 zeigt das Formular, in dem Benutzer Feedback eingeben können.</p>
<p>Listing B.9. feedbackForm</p>
<pre class="literal-block">
&lt;!--
$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
Copyright: ClearWind Consulting Ltd
License: http://www.clearwind.ca/license
--&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
lang=&quot;en-US&quot; i18n:domain=&quot;plone&quot;
metal:use-macro=&quot;here/main_template/macros/master&quot;&gt;
  &lt;body&gt;
    &lt;div metal:fill-slot=&quot;main&quot;
    tal:define=&quot;errors options/state/getErrors;&quot;&gt;
      &lt;p&gt;Please send us any feedback you might have about the
      site.&lt;/p&gt;
      &lt;form method=&quot;post&quot; tal:attributes=&quot;action template/id;&quot;&gt;
        &lt;fieldset&gt;
          &lt;legend class=&quot;legend&quot;
          i18n:translate=&quot;legend_feedback_form&quot;&gt;Website
          Feedback&lt;/legend&gt;
          &lt;div class=&quot;field&quot;
          tal:define=&quot;error_email_address errors/email_address|nothing;&quot;
               tal:attributes=&quot;class python:test(error_email_address, 'field error', 'field')&quot;&gt;

            &lt;label i18n:translate=&quot;label_email_address&quot;&gt;Your email
            address&lt;/label&gt;
            &lt;span class=&quot;fieldRequired&quot; title=&quot;Required&quot;
            i18n:attributes=&quot;title&quot;
            i18n:translate=&quot;label_required&quot;&gt;(Required)&lt;/span&gt;
            &lt;div class=&quot;formHelp&quot;
            i18n:translate=&quot;label_email_address_help&quot;&gt;Enter your
            email address.&lt;/div&gt;
            &lt;div tal:condition=&quot;error_email_address&quot;&gt;
              &lt;tal:block i18n:translate=&quot;&quot;
              content=&quot;error_email_address&quot;&gt;Error&lt;/tal:block&gt;
            &lt;/div&gt;
            &lt;input type=&quot;text&quot; name=&quot;email_address&quot;
            tal:define=&quot;user context/portal_membership/getAuthenticatedMember; email user/email|nothing&quot;
            tal:attributes=&quot;tabindex tabindex/next; value request/email_address|email|nothing&quot; /&gt;
          &lt;/div&gt;
          &lt;div class=&quot;field&quot;&gt;
            &lt;label i18n:translate=&quot;label_feedback_comments&quot;&gt;
            Comments&lt;/label&gt;
            &lt;div class=&quot;formHelp&quot; id=&quot;label_feedback_comments_help&quot;
            i18n:translate=&quot;label_feedback_comments_help&quot;&gt;Enter the
            comments you have about the site.&lt;/div&gt;
            &lt;textarea name=&quot;comments&quot; rows=&quot;10&quot;
            tal:content=&quot;request/comments|nothing&quot;
            tal:attributes=&quot;tabindex tabindex/next;&quot; /&gt;
          &lt;/div&gt;
          &lt;div class=&quot;formControls&quot;&gt;
            &lt;input class=&quot;context&quot; type=&quot;submit&quot; tabindex=&quot;&quot;
            name=&quot;form.button.Submit&quot; value=&quot;Submit&quot;
            i18n:attributes=&quot;value&quot;
            tal:attributes=&quot;tabindex tabindex/next;&quot; /&gt;
          &lt;/div&gt;
        &lt;/fieldset&gt;
        &lt;input type=&quot;hidden&quot; name=&quot;form.submitted&quot; value=&quot;1&quot; /&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="controller-python-skript-sendemail">
<h4>Controller Python-Skript: sendEmail</h4>
<p>Listing B.10 zeigt das Steuerskript, das dem Benutzer die E-Mail schickt.</p>
<p>Listing B.10. sendEmail</p>
<pre class="literal-block">
#!/usr/bin/python
#$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
#Copyright: ClearWind Consulting Ltd
#License: http://www.clearwind.ca/license

mhost = context.MailHost
emailAddress = context.REQUEST.get('email_address')
administratorEmailAddress = context.email_from_address
comments = context.REQUEST.get('comments')

# the message format, %s will be filled in from data
message = &quot;&quot;&quot;
From: %s
To: %s
Subject: Website Feedback

%s

URL: %s
&quot;&quot;&quot;

# format the message
message = message % (
    emailAddress,
    administratorEmailAddress,
    comments,
    context.absolute_url())

mhost.send(message)

screenMsg = &quot;Comments sent, thank you.&quot;
state.setKwargs( {'portal_status_message':screenMsg} )
return state
</pre>
</div>
<div class="section" id="controller-python-skript-validemail">
<h4>Controller Python-Skript: validEmail</h4>
<p>Listing B.11 zeigt das Skript, das die E-Mail validiert.</p>
<p>Listing B.11. validEmail</p>
<pre class="literal-block">
#$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
#Copyright: ClearWind Consulting Ltd
#License: http://www.clearwind.ca/license

email = context.REQUEST.get('email_address', None)

if not email:
    state.setError('email_address', 'An email address is required', new_status='failure')

if state.getErrors():
    state.set(portal_status_message='Please correct the errors shown.')

return state
</pre>
</div>
</div>
<div class="section" id="kapitel-7">
<h3>Kapitel 7</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 7.</p>
<div class="section" id="script-python-setskin">
<h4>Script (Python): setSkin</h4>
<p>Falls es in Form einer Zugriffsregel in Ihrer Site zugewiesen wurde, ändert das Skript in Listing B.12 alle Anfragen auf <tt class="docutils literal">intern.einesite.org</tt> so ab, dass die Skin <tt class="docutils literal">Plone Default</tt> verwendet wird. Sonst wird die Skin <tt class="docutils literal">Custom Chrome</tt> verwendet.</p>
<p>Listing B.12. setSkin</p>
<pre class="literal-block">
##title=Skin changing script
##parameters=
req = context.REQUEST
if req['SERVER_URL'].find('internal.somesite.org') &gt; -1:
    context.changeSkin(&quot;Plone Default&quot;)
context.changeSkin(&quot;Custom Chrome&quot;)
</pre>
</div>
<div class="section" id="css-plonecustom-css">
<h4>CSS: ploneCustom.css</h4>
<p>Listing B.13 zeigt das Cascading Stylesheet, das auf der NASA-Mars-Site verwendet wird.</p>
<p>Listing B.13. ploneCustom.css</p>
<pre class="literal-block">
body {
    background: #343434;
}

#visual-portal-wrapper {
    width: 680px;
    margin: 1em auto 0 auto;
}

#portal-top {
    background: url(&quot;http://mars.telascience.org/header.jpg&quot;) transparent no-repeat;
    padding: 162px 0 0 0;
    position: relative;
}

#portal-logo {
    background: transparent;
    background-image: none;
    margin: 0;
    position: absolute;
    top: 130px;
    left: 5px;
    z-index: 20;
}

#portal-logo a {
    padding-top: 25px;
    height /**/: 25px;
    width: 375px;
}

#portal-globalnav {
   background: url(&quot;http://mars.telascience.org/listspacer.gif&quot;) transparent;
    padding: 0;
    height: 21px;
    border: 0;
    margin: 0 0 1px 6px;
    clear: both;
}

#portal-globalnav li {
    display: block;
    float: left;
    height: 21px;
    background: url(&quot;http://mars.telascience.org/liststart.gif&quot;) transparent no-repeat;
    padding: 0 0 0 33px;
    margin: 0 0.5em 0 0;
}

#portal-globalnav li a {
    display: block;
    float: left;
    height: 21px;
    background: url(&quot;http://mars.telascience.org/listitem.gif&quot;) transparent right top;
    padding: 0 33px 0 0;
    border: 0;
    line-height: 2em;
    color: black;
    font-size: 90%;
    margin: 0;
}

#portal-globalnav li a:hover,
#portal-globalnav li.selected a {
    background-color: transparent;
    border: 0;
    color: #444;
}

#portal-personaltools {
    clear: both;
    margin-left: 6px;
    border-top-color: #776a44;
    border-top-style: solid;
    border-top-width: 1px;
}

#portal-breadcrumbs {
    clear: both;
}

#portal-breadcrumbs,
#portal-columns,
.documentContent {
    background: white;
    margin-left: 6px;
}

.documentContent {
   margin: 0;
   font-size: 100%;
}

.screenshotThumb {
  float:right;
}

#portal-footer {
    margin: -1px 0 0 6px;
    padding: 0.8em 0;
    border: 1px solid #ddd;
    border-style: solid none none none;
    background: white;
    color: #666;
    font-size: 90%;
}

#portal-footer a {
    color: #333;
    text-decoration: underline;
}

dt {
    color: #ECA200;
}

.documentDescription {
    font-size: 110%;
}

#portal-breadcrumbs img {
    display: none;
}

li.reqlist {
    margin-top: 0;
    margin-bottom: 0;
}
</pre>
</div>
</div>
<div class="section" id="kapitel-8">
<h3>Kapitel 8</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 8.</p>
<div class="section" id="script-python-mail-py">
<h4>Script (Python): mail.py</h4>
<p>Listing B.14 enthält ein Skript, das immer dann eine E-Mail an Benutzer schickt, wenn sich ein Objekt geändert hat.</p>
<p>Listing B.14. mail.py</p>
<pre class="literal-block">
##parameters=state_change
# the objects we need
object = state_change.object
mship = context.portal_membership
mhost = context.MailHost
administratorEmailAddress = context.email_from_address

# the message format, %s will be filled in from data
message = &quot;&quot;&quot;
From: %s
To: %s
Subject: New item submitted for approval - %s

%s

URL: %s
&quot;&quot;&quot;

for user in mship.listMembers():
    if &quot;Reviewer&quot; in mship.getMemberById(user.id).getRoles():
        if user.email:
            msg = message % (
                 administratorEmailAddress,
                 user.email,
                 object.TitleOrId(),
                 object.Description(),
                 object.absolute_url()
            )
        mhost.send(msg)
</pre>
</div>
</div>
<div class="section" id="kapitel-9">
<h3>Kapitel 9</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 9.</p>
<div class="section" id="externe-methode-importusers">
<h4>Externe Methode: importUsers</h4>
<p>Listing B.15 zeigt eine externe Methode zum Importieren von Benutzern aus einer .csv-Datei im Dateisystem.</p>
<p>Listing B.15. importUsers</p>
<pre class="literal-block">
#!/usr/bin/python
#$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
#Copyright: ClearWind Consulting Ltd
#License: http://www.clearwind.ca/license
import csv

fileName = &quot;/var/zope.zeo/Extensions/test.csv&quot;

def importUsers(self):
    reader = csv.reader(open(fileName, &quot;r&quot;))
    pm = self.portal_membership
    pr = self.portal_registration
    pg = self.portal_groups
    out = []
    ignoreLine = 1

    for row in reader:
        # ignore blank lines
        if not row: continue

        if ignoreLine:
            continue
            ignoreLine = 0

        # check we have exactly 4 items
        assert len(row) == 4
        id, name, email, groups = row

        password = pr.generatePassword()

        try:
            pr.addMember(id = id,
                password = password,
                roles = [&quot;Member&quot;,],
                properties = {
                    'fullname': name,
                    'username': id,
                    'email': email,
                    }
                )
            for groupId in groups.split(','):
                group = pg.getGroupById(groupId)
                group.addMember(id)

            out.append(&quot;Added user %s&quot; % id)

        except ValueError:
            out.append(&quot;Skipped %s&quot; % id)

    return &quot;\n&quot;.join(out)
</pre>
</div>
<div class="section" id="externe-methode-fixusers">
<h4>Externe Methode: fixUsers</h4>
<p>Listing B.16 zeigt ein Skript, das für alle Benutzer Eigenschaften von Epoz setzt.</p>
<p>Listing B.16. fixUsers</p>
<pre class="literal-block">
def fixUsers(self):
    pm = self.portal_membership
    members = pm.listMemberIds()

    out = []
    for member in members:
        # now get the actual member
        m = pm.getMemberById(member)
        # get the editor property for that member
        p = m.getProperty('wysiwyg_editor', None)

        out.append(&quot;%s %s&quot; % (p, member))
        if p is not None and p != 'Epoz':
            m.setMemberProperties({'wysiwyg_editor': 'Epoz',})
            out.append(&quot;Changed property for %s&quot; % member)
    return &quot;\n&quot;.join(out)
</pre>
</div>
<div class="section" id="externe-methode-getgroups">
<h4>Externe Methode: getGroups</h4>
<p>Listing B.17 zeigt ein Skript, das zuerst den Erzeuger eines Objekts holt. Dann holt es alle Benutzer, die in der gleichen Gruppe sind wie dieser Erzeuger.</p>
<p>Listing B.17. getGroups</p>
<pre class="literal-block">
##parameters=object=None
# object is the object to find all the members of the same group for
users = []
# get the creator
userName = object.Creator()
user = context.portal_membership.getMemberById(userName)
pg = context.portal_groups

# loop through the groups the user is in
for group in user.getGroups():
  group = pg.getGroupById(group)

   # loop through the users in each of those groups
  for user in group.getGroupUsers():
    if user not in users and user != userName:
      users.append(user)

return users
</pre>
</div>
</div>
<div class="section" id="kapitel-11">
<h3>Kapitel 11</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 11.</p>
<div class="section" id="script-python-scriptobjectcreation">
<h4>Script (Python): scriptObjectCreation</h4>
<p>Listing B.18 zeigt ein Skript, das einen Ordner mit einem Dokument darin erzeugt.</p>
<p>Listing B.18. scriptObjectCreation</p>
<pre class="literal-block">
##title=Create
##parameters=
# create with a random id
newId = context.generateUniqueId('Folder')

# create a object of type Folder
context.invokeFactory(id=newId, type_name='Folder')
newFolder = getattr(context, newId)

# create a new Document type
newFolder.invokeFactory(id='index.html', type_name='Document')

# get the new page
newPage = getattr(newFolder, 'index.html')
newPage.edit('html', '&lt;p&gt;This is the default page.&lt;/p&gt;')

# return something back to the calling script
return &quot;Done&quot;
</pre>
</div>
<div class="section" id="page-template-getcatalogresults">
<h4>Page Template: getCatalogResults</h4>
<p>Listing B.19 zeigt eine Beispielkatalogabfrage, die auf den REQUEST-Parametern eine Abfrage durchführt.</p>
<p>Listing B.19. getCatalogResults</p>
<pre class="literal-block">
##title=Get Catalog Results
##parameters=
return context.portal_catalog.searchResults(REQUEST=context.REQUEST)
</pre>
</div>
<div class="section" id="page-template-testresults">
<h4>Page Template: testResults</h4>
<p>Listing B.20 zeigt ein Beispielergebnis einer Katalogsuche.</p>
<p>Listing B.20. testResults</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
      lang=&quot;en-US&quot;
      metal:use-macro=&quot;here/main_template/macros/master&quot;
      i18n:domain=&quot;plone&quot;&gt;
&lt;body&gt;
&lt;div metal:fill-slot=&quot;main&quot;&gt;
&lt;ul tal:define=&quot;results here/getCatalogResults&quot;&gt;
    &lt;li tal:repeat=&quot;result results&quot;&gt;
        &lt;a href=&quot;&quot;
           tal:attributes=&quot;href result/getURL&quot;
           tal:content=&quot;result/Title&quot; /&gt;
        &lt;span tal:replace=&quot;result/Description&quot; /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="page-template-testform">
<h4>Page Template: testForm</h4>
<p>Listing B.21 zeigt ein Beispielformular, das die Seite <tt class="docutils literal">testResults</tt> mit einer Dropdown-Liste aufruft, die auf einer Katalogabfrage basiert.</p>
<p>Listing B.21. testForm</p>
<pre class="literal-block">
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en-US&quot;
      lang=&quot;en-US&quot;
      metal:use-macro=&quot;here/main_template/macros/master&quot;
      i18n:domain=&quot;plone&quot;&gt;
&lt;body&gt;
&lt;div metal:fill-slot=&quot;main&quot;&gt;
    &lt;p&gt;Select a content type to search for&lt;/p&gt;
    &lt;form method=&quot;post&quot; action=&quot;testResults&quot;&gt;
        &lt;select name=&quot;Type&quot;&gt;
            &lt;option
   tal:repeat=&quot;value python:here.portal_catalog.uniqueValuesFor('Type&quot;)&quot;
   tal: content=&quot;value&quot; /&gt;
        &lt;/select&gt;
        &lt;br /&gt;
        &lt;input type=&quot;submit&quot; class=&quot;context&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
</div>
<div class="section" id="kapitel-12">
<h3>Kapitel 12</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 12.</p>
<div class="section" id="beispielprodukt-plonesilvercity">
<h4>Beispielprodukt: PloneSilverCity</h4>
<p>Dieses Produkt finden Sie im Kollektiv unter <a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>. Sie können dieses Produkt auch von der Website zu diesem Buch unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a> herunterladen.</p>
</div>
<div class="section" id="beispielprodukt-plonestats">
<h4>Beispielprodukt: PloneStats</h4>
<p>Dieses Produkt finden Sie im Kollektiv unter <a class="reference external" href="http://sf.net/projects/collective">http://sf.net/projects/collective</a>. Sie können dieses Produkt auch von der Website zu diesem Buch unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a> herunterladen.</p>
</div>
</div>
<div class="section" id="kapitel-13">
<h3>Kapitel 13</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 13.</p>
<div class="section" id="beispielprodukt-archexample">
<h4>Beispielprodukt: ArchExample</h4>
<p>Dieses Produkt befindet sich im Archetypes-Release, aber Sie können eine Kopie auch von der Website zu diesem Buch unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a> herunterladen.</p>
</div>
<div class="section" id="page-template-email-widget-py">
<h4>Page Template: email_widget.py</h4>
<p>Listing B.22 zeigt ein eigenes Beispiel-Widget für Archetypes.</p>
<p>Listing B.22. email_widget.py</p>
<pre class="literal-block">
&lt;!--
$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
Copyright: ClearWind Consulting Ltd
License: http://www.clearwind.ca/license
--&gt;
&lt;html xmlns:tal=&quot;http://xml.zope.org/namespaces/tal&quot;
      xmlns:metal=&quot;http://xml.zope.org/namespaces/metal&quot;
      i18n:domain=&quot;plone&quot;&gt;

  &lt;body&gt;
    &lt;div metal:define-macro=&quot;edit&quot;&gt;
      &lt;div metal:use-macro=&quot;here/widgets/string/macros/edit&quot; /&gt;
    &lt;/div&gt;

    &lt;div metal:define-macro=&quot;search&quot;&gt;
      &lt;div metal:use-macro=&quot;here/widgets/string/macros/search&quot; /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;field&quot; metal:define-macro=&quot;view&quot;&gt;
        &lt;metal:block define-slot=&quot;widget_label&quot; /&gt;
        &lt;metal:block use-macro=&quot;here/widgets/field/macros/view&quot;&gt;
              &lt;metal:block fill-slot=&quot;widget_view&quot;&gt;
                  &lt;a href=&quot;#&quot; tal:attributes=&quot;href string:mailto:${accessor}&quot;
                     tal:content=&quot;accessor&quot;&gt;email&lt;/a&gt;
              &lt;/metal:block&gt;
        &lt;/metal:block&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div>
<div class="section" id="beispielprodukt-worldexample">
<h4>Beispielprodukt: WorldExample</h4>
<p>Das Listing für das Produkt WorldExample können Sie von der Website zu diesem Buch unter <a class="reference external" href="http://plone-book.agmweb.ca">http://plone-book.agmweb.ca</a> herunterladen.</p>
</div>
<div class="section" id="python-modul-personsql-py">
<h4>Python-Modul: PersonSQL.py</h4>
<p>Listing B.23 zeigt einen Archetypes-Inhaltstyp für eine Person, der die Daten in einer relationalen Datenbank speichert.</p>
<p>Listing B.23. PersonSQL.py</p>
<pre class="literal-block">
#!/usr/bin/python
#$Id: appB.rst,v 1.1 2005/09/08 12:41:33 dinu_gherman Exp $
#Copyright: ClearWind Consulting Ltd
#License: http://www.clearwind.ca/license

from Products.Archetypes.public import Schema
from Products.Archetypes.public import IntegerField, StringField
from Products.Archetypes.public import IntegerWidget, StringField
from Products.Archetypes.SQLStorage import PostgreSQLStorage
from config import PROJECTNAME

schema = BaseSchema + Schema((

  IntegerField('age',
      validators=((&quot;isInt&quot;,)),
      storage = SQLStorage(),
      widget=IntegerWidget(label=&quot;Your age&quot;),

      ),

  StringField('email',
      validators = ('isEmail',),
      index = &quot;TextIndex&quot;,
      storage = SQLStorage(),
      widget = StringWidget(label='Email',)
      ),

    ))

class PersonSQL(BaseContent):
    &quot;&quot;&quot;Our person object&quot;&quot;&quot;
    schema = schema

registerType(PersonSQL, PROJECTNAME)
</pre>
</div>
</div>
<div class="section" id="kapitel-14">
<h3>Kapitel 14</h3>
<p>Die folgenden Code-Listings stammen aus Kapitel 14.</p>
<div class="section" id="python-modul-header-py">
<h4>Python-Modul: header.py</h4>
<p>Listing B.24 zeigt ein Skript, das die Header für eine URL ausgibt.</p>
<p>Listing B.24. header.py</p>
<pre class="literal-block">
#!/usr/bin/python
import sys

from httplib import HTTP
from urlparse import urlparse

def getHeaders(url, method):
    p = list(urlparse(url))
    if not p[0]:
        url = 'http://' + url
        p = list(urlparse(url))

    h = HTTP(p[1])

    h.putrequest(method, p[2])
    h.putheader('Accept-Encoding', 'gzip, deflate')
    h.endheaders()

    reply = h.getreply()
    print &quot;Status:&quot;, reply[0]
    print &quot;Status message:&quot;, reply[1]
    hdrs = reply[2].headers
    hdrs.sort()
    for header in hdrs:
        print header[:-1]

def usage():
    print &quot;&quot;&quot;Usage: headers.py URL [method]

URL - the URL to get headers for, http:// default
method - GET default
&quot;&quot;&quot;
    sys.exit()

if __name__=='__main__':
    if len(sys.argv) &lt; 2: usage()
    method = 'GET'
    if len(sys.argv) &gt; 2:
        method = sys.argv[2]
    getHeaders(sys.argv[1], method)
</pre>
</div>
<div class="section" id="script-python-mycachingrules">
<h4>Script (Python): myCachingRules</h4>
<p>Listing B.25 enthält eine angepasste Caching-Regel für einen Policy-Manager, nur um Ihnen mehr Optionen zu geben.</p>
<p>Listing B.25. myCachingRules</p>
<pre class="literal-block">
##parameters=content
# cache all files, images and anything
# thats published
if content.portal_type in ['File', 'Image']:
    return 1
if content.review_state in ['published',]:
    return 1
</pre>
</div>
<div class="section" id="externe-methode-purge-cache">
<h4>Externe Methode: Purge Cache</h4>
<p>Listing B.26 zeigt ein Beispiel für ein Skript, das den Cache-Speicher löscht.</p>
<p>Listing B.26. Purge Cache</p>
<pre class="literal-block">
import urllib
import urlparse
import httplib

URLs = [
    # enter the URLs you would like
    # to purge here
    'http://localhost:8080',
]

def purge(objectURL):
    for url in URLs:
        if not url:
            continue
        assert url[:4] == 'http', &quot;No protocol specified&quot;

        url = urlparse.urljoin(url, objectURL)
        parsed = urlparse.urlparse(url)
        host = parsed[1]
        path = parsed[2]

        h = httplib.HTTP(host)
        h.putrequest('PURGE', path)
        h.endheaders()
        errcode, errmsg, headers = h.getreply()
        h.getfile.read()

if __name__ == '__main__':
    print purge('/')
</pre>
</div>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>


  <item rdf:about="http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appC.rst">
    <title>Anhang C: Glossar und Werkzeuge</title>
    <link>http://www.derstappen-it.de/dokumentationen/zope-plone/plone-buch/appC.rst</link>
    <description>Dieser Anhang beschreibt alle Standardwerkzeuge und -objekte, die in einer Plone-Site erzeugt werden, und verweist auf Stellen, wo sie im Buch vorkommen. Das Glossar enthält eine Liste aller wichtigen Begriffe, die in diesem Buch und in Plone verwendet werden.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 class="title">Glossar und Werkzeuge</h2>
<p>Dieser Anhang beschreibt alle Standardwerkzeuge und -objekte, die in einer Plone-Site erzeugt werden, und verweist auf Stellen, wo sie im Buch vorkommen. Das Glossar enthält eine Liste aller wichtigen Begriffe, die in diesem Buch und in Plone verwendet werden.</p>
<div class="section" id="werkzeuge">
<h3>Werkzeuge</h3>
<p>Tabelle C.1 beschreibt die Standardwerkzeuge, die Plone erzeugt.</p>
<p>Tabelle C.1. Plone-Werkzeuge</p>
<table border="1" class="docutils">
<colgroup>
<col width="43%" />
<col width="57%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Werkzeugname</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>caching_policy_manager</td>
<td>Dient dem Cachen von Inhalten (siehe
Kapitel 14).</td>
</tr>
<tr><td>content_type_registry</td>
<td>Bietet verschiedene Möglichkeiten, neue
Inhalte zu verarbeiten (siehe Kapitel 11).</td>
</tr>
<tr><td>plone_utils</td>
<td>Allgemeine Hilfsfunktionen, auf die
normalerweise nicht zugegriffen wird.</td>
</tr>
<tr><td>portal_actionicons</td>
<td>Verbindet ein Bild mit einer Aktion.</td>
</tr>
<tr><td>portal_actions</td>
<td>Die Kerndefinition der Aktionen.</td>
</tr>
<tr><td>portal_calendar</td>
<td>Enthält den calendar-Slot, wird sonst nicht
benutzt. In Kapitel 4 finden Sie
über Informationen den calendar-Slot.</td>
</tr>
<tr><td>portal_catalog</td>
<td>Katalogwerkzeug für Indexinhalte (siehe
Kapitel 11).</td>
</tr>
<tr><td>portal_controlpanel</td>
<td>Bietet eine Schnittstelle zu den in Plone
sichtbaren Aktionen im Control Panel.</td>
</tr>
<tr><td>portal_discussion</td>
<td>Dient zur Diskussion von Inhalten (siehe
Kapitel 4).</td>
</tr>
<tr><td>portal_factory</td>
<td>Sorgt dafür, dass bei der normalen
Erzeugung von Inhalten keine unfertigen
Teilobjekte in der Datenbank liegen bleiben
(siehe Kapitel 12).</td>
</tr>
<tr><td>portal_form</td>
<td>Veraltet, wegen Rückwärtskompatibilität
noch vorhanden.</td>
</tr>
<tr><td>portal_form_controller</td>
<td>Stellt Dienste für Formulare zur
Verfügung (siehe Kapitel 7).</td>
</tr>
<tr><td>portal_groupdata</td>
<td>Speichert Information über Gruppen (siehe
Kapitel 9).</td>
</tr>
<tr><td>portal_groups</td>
<td>Dient zur Erzeugung von Gruppen (siehe
Kapitel 9).</td>
</tr>
<tr><td>portal_interface</td>
<td>Bietet Entwicklern ein API zur Untersuchung
von Objektschnittstellen.</td>
</tr>
<tr><td>portal_memberdata</td>
<td>Speichert Information über Benutzer (siehe
Kapitel 9).</td>
</tr>
<tr><td>portal_membership</td>
<td>Kümmert sich um Optionen von
Mitgliedschaften (siehe Kapitel 10).</td>
</tr>
<tr><td>portal_metadata</td>
<td>Metadaten über Portal-Inhaltstypen. Wird
wenig benutzt.</td>
</tr>
<tr><td>portal_migration</td>
<td>Dient der Migration zu neuen Versionen von
Plone (siehe Kapitel 14).</td>
</tr>
<tr><td>portal_navigation</td>
<td>Veraltet, wegen Rückwärtskompatibilität
noch vorhanden.</td>
</tr>
<tr><td>portal_properties</td>
<td>Eigenschaften und Werte der Site (siehe
Kapitel 4.)</td>
</tr>
<tr><td>portal_quickinstaller</td>
<td>Ein Hilfswerkzeug für die schnelle
Installation von Produkten (siehe Kapitel
10).</td>
</tr>
<tr><td>portal_registration</td>
<td>Kümmert sich um Optionen bei der
Registrierung von Benutzern (siehe Kapitel
9).</td>
</tr>
<tr><td>portal_skins</td>
<td>Enthält Dienste zu Skins und alle Skins
selbst (siehe Kapitel 4-7).</td>
</tr>
<tr><td>portal_syndication</td>
<td>Bietet Zugriff auf RSS-Feeds zu
Plone-Inhalten. (RSS steht für Rich Site
Summary bzw. Really Simple Syndication.)</td>
</tr>
<tr><td>portal_types</td>
<td>Hauptwerkzeug bei der Behandlung von
Inhaltstypen in einem Portal (siehe Kapitel
11-13).</td>
</tr>
<tr><td>portal_undo</td>
<td>Bietet Zugang zu den Undo-Mechanismen von
Plone.</td>
</tr>
<tr><td>portal_url</td>
<td>Bietet Zugang zu nützlichen APIs bei der
Bestimmung von URLs.</td>
</tr>
<tr><td>portal_workflow</td>
<td>Bietet Workflow und dazugehörige
Möglichkeiten (siehe Kapitel 7).</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="objekte">
<h3>Objekte</h3>
<p>Tabelle C.2 beschreibt die Standardobjekte, die Plone in einer Plone-Site erzeugt.</p>
<p>Tabelle C.2. Plone-Objekte</p>
<table border="1" class="docutils">
<colgroup>
<col width="43%" />
<col width="57%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Objekt</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>HTTPCache</td>
<td>Enthält HTTP-Header für Skins (siehe
Kapitel 14).</td>
</tr>
<tr><td>MailHost</td>
<td>Bietet Zugang zum SMTP-Server (Simple Mail
Transfer Protocol) zum Versenden von
E-Mail.</td>
</tr>
<tr><td>Members</td>
<td>Ein umfangreicher Ordner, in dem die
Ordner der Site-Mitglieder erstellt werden
(siehe Kapitel 9).</td>
</tr>
<tr><td>RAMCache</td>
<td>Bietet Caching für Skins im RAM (Random
Access Memory; siehe Kapitel 14).</td>
</tr>
<tr><td>acl_users</td>
<td>Der Hauptordner für Benutzer (siehe Kapitel
14).</td>
</tr>
<tr><td>cookie_authentication</td>
<td>Bietet Authentifikation von Benutzern mit
Hilfe von Cookies (siehe Kapitel 9).</td>
</tr>
<tr><td>error_log</td>
<td>Die Protokolldatei von im System
aufgetretenen Fehlern (siehe Kapitel 4).</td>
</tr>
<tr><td>index_html</td>
<td>Das Standard-<em>index_html</em>, das auf der
Site erscheint (siehe Kapitel 6).</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="glossar">
<h3>Glossar</h3>
<p>Tabelle C.3 enthält Definitionen aller wichtigen Begriffe in der Plone-Welt.</p>
<p>Tabelle C.3. Plone-Definitionen</p>
<table border="1" class="docutils">
<colgroup>
<col width="43%" />
<col width="57%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Objekt</th>
<th class="head">Beschreibung</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Akquisition</td>
<td><em>Akquisition</em> ist ein Mechanismus in Zope
zur Vererbung von Objekteigenschaften.
Akquisition ist die Grundlage der
Zope-Objekthierarchie, die davon viel
Gebrauch macht.</td>
</tr>
<tr><td>Aktion</td>
<td>In der Plone-Terminologie sind <em>Aktionen</em>
eine konfigurierbare Art von
Navigationselementen  auf einer Site.
Einige Beispiele sind Anzeigen, Bearbeiten
und Mitglieder. In Kapitel 5 finden Sie
weitere Details.</td>
</tr>
<tr><td>Anfrage (request)</td>
<td>Jede Anfrage eines Clients nach einer Seite
erzeugt eine Anfrage an Plone. Diese wird
in einem Anfrage-Objekt in Zope gekapselt,
das normalerweise <tt class="docutils literal">REQUEST</tt> oder
<tt class="docutils literal">request</tt> heißt.</td>
</tr>
<tr><td>Anmeldung (login)</td>
<td>Durch diesen Prozess gehen Sie, wenn Sie
auf dem Anmeldeschirm Ihren Benutzernamen
und Passwort eingeben, was gleichbedeutend
mit der Authentifikation ist.</td>
</tr>
<tr><td>Anonyme Rolle</td>
<td>Eine Standardrolle im Sicherheitsmodell von
Zope. Die anonyme Rolle wird Besuchern der
Site so lange zugeordnet, bis sie sich mit
ihrer Zope-Kennung und ihrem Zope-Passwort
anmelden.</td>
</tr>
<tr><td>Antwort (response)</td>
<td>Zu jeder Anfrage wird eine Antwort
generiert, die in Zope in einem
Antwort-Objekt gekapselt wird. Dieses
heißt normalerweise <tt class="docutils literal">RESPONSE</tt> oder
<tt class="docutils literal">response</tt>.</td>
</tr>
<tr><td>Anzeige (view)</td>
<td>Eine <em>Anzeige</em> stellt Informationen mit
einer vordefinierten Struktur dar. Die
Aktionen z.B. in <tt class="docutils literal">portal_types</tt> sind
Anzeigen.</td>
</tr>
<tr><td>Archetypes</td>
<td>Ein Framework für die Entwicklung neuer
Inhaltstypen in Zope/CMF/Plone (siehe
Kapitel 13).</td>
</tr>
<tr><td>Authentifizierter Benutzer</td>
<td>Ein Benutzer, der im Zope-System angemeldet
ist. Wenn gerade keine Benutzer angemeldet
sind, werden anonyme Benutzer als
authentifizierte Benutzer betrachtet.</td>
</tr>
<tr><td>Authentifikation</td>
<td>Der von Zope verwendete
Identifizierungsprozess.</td>
</tr>
<tr><td>Benutzerschnittstelle
(UI, user interface)</td>
<td>Die <em>Benutzerschnittstelle</em> (auch User
Interface bzw. UI) besteht aus der Art und
Weise und den Bildschirmen, wie Sie
und mit denen Sie
mit einem Software-Programm interagieren.</td>
</tr>
<tr><td>Besitz (von Objekten)</td>
<td>Benutzer, die Objekte in Zope erzeugen,
werden zu <em>Besitzern</em> dieser Objekte. Jedes
Objekt in Zope hat einen Besitzer, außer
vielleicht jene, die bei der Installation
von Zope angelegt werden.</td>
</tr>
<tr><td>Besitzer-Rolle (owner)</td>
<td>Das ist die Standardrolle eines Besitzers
in Zope.</td>
</tr>
<tr><td>CMF</td>
<td>Das <em>Content Management Framework</em> ist ein
Zusatz zu Zope, der Dienste enthält, die
ein Content-Management-System benötigt.</td>
</tr>
<tr><td>CMFTypes</td>
<td>Der alte Name von Archetypes.</td>
</tr>
<tr><td>CMS</td>
<td>Ein <em>Content-Management-System</em> ist ein
System zur, nun ja, Verwaltung von
Inhalten.</td>
</tr>
<tr><td>Cookie-Authentifikation</td>
<td><tt class="docutils literal">cookie_authentication</tt> (auch bekannt
als <em>CookieCrumbler</em>) ermöglicht eine
formularbasierte Anmeldung (siehe Kapitel
9).</td>
</tr>
<tr><td>CSS</td>
<td><em>Cascading Style Sheets</em> sind ein System in
HTML, mit dem Elementstile definiert werden
können. Plone verwendet CSS sehr ausgiebig.
Sie finden einige Beispiele in Kapitel 7.</td>
</tr>
<tr><td>Dienste (services)</td>
<td>Das Ziel von CMF ist die Vereinheitlichung
der Verwaltung von Inhalten und die
Bereitstellung einer Reihe von Diensten,
darunter Katalogisierung, Workflow und
Syndizierung. CMF und Plone bieten viele
Dienste für Ihre Site. Es gibt öfffentlich
verfügbare Dienste wie Suche und
Diskussionen, ebenso wie Verwaltungsdienste
wie Workflow.</td>
</tr>
<tr><td>DTML</td>
<td>Die <em>Document Template Markup Language</em>
(DTML) ist eine serverseitige
Templating-Sprache, mit der dynamisch Teile
von Inhalten erzeugt werden können.
Sie wird
vor allem zusammen mit HTML verwendet. In
Plone wird sie kaum benutzt und wird
allgemein als veraltet betrachtet (siehe
Page Templates).</td>
</tr>
<tr><td>Diskussionen</td>
<td>Das Werkzeug <tt class="docutils literal">portal_discussion</tt> enthält
die Policy, die angibt, wie Diskussionen in
einem Plone-System funktionieren.</td>
</tr>
<tr><td>ECMAscript</td>
<td>Im Wesentlichen ist das JavaScript.</td>
</tr>
<tr><td>Eigenschaften (properties)</td>
<td>Im Wesentlichen sind das Attribute eines
Objekts. Die Eigenschaften eines
Zope-Objekts können Sie sehen, wenn Sie auf
den <em>Properties</em>-Reiter im ZMI klicken,
wenn Sie dieses Objekt gerade anzeigen.
Eigenschaften werden auch in Objekten
in der Plone-Schnittstelle benutzt, um
mögliche Eingenschaften von Objekten zu
beschreiben, z.B. Stichwörter.</td>
</tr>
<tr><td>Externe Methoden</td>
<td><em>Externe Methoden</em> sind im Wesentlichen
Python-Module im Dateisystem, die mit Zope
über das Objekt <em>External Method</em> verbunden
sind, das Sie aus dem Dropdown-Menü heraus
erzeugen können. Externe Methoden sind
mächtiger als <em>Script (Python)</em>-Objekte,
weil sie nicht genauso streng unter das
Sicherheitsmodell von Zope fallen, wie das
bei <em>Script (Python)</em>-Objekten der Fall
sein kann.</td>
</tr>
<tr><td>Factory</td>
<td>Eine <em>Factory</em> ist ein Werkzeug zur
Erzeugung anderer Objekte.</td>
</tr>
<tr><td>Factory-Typinformation (FTI)</td>
<td>Die <em>Factory-Typinformation</em> enthält die
Information, die im Werkzeug
<tt class="docutils literal">portal_types</tt> geladen wird.</td>
</tr>
<tr><td>Ebene(Layer)</td>
<td>In Plone ist eine <em>Skin</em> eine
durchnummerierte Sammlung von
<em>Ebenen</em> (Layers).
Bei Ebenen ist nicht genau festgelegt,
was sie machen können. Sie können visuelle
Aspekte einer Plone-Site ändern. Sie können
neue Inhaltstypen in einer mehr oder
weniger
präsentationsneutralen Weise einführen,
oder sie können das Verhalten in anderen
Skins ändern/überschreiben.</td>
</tr>
<tr><td>GPL</td>
<td>Die GPL (GNU Public License) beschreibt die
Lizenzbedingungen von Plone.</td>
</tr>
<tr><td>Globbing (ZCatalog)</td>
<td>Mit dem <em>Globbing</em>-Mechanismus können Sie
im ZCatalog mit Hilfe von Jokern (*)
suchen. Damit kann man auch nach
Teilwörtern im ZCatalog suchen.</td>
</tr>
<tr><td>HTML</td>
<td>Die <em>Hypertext Markup Language</em> ist die
grundlegende Auszeichnungssprache im Web.
Dieses Buch setzt voraus, dass Sie wissen,
was HTML ist.</td>
</tr>
<tr><td>i18n (<em>Internationalization</em>)</td>
<td>Unter <em>Internationalisierung</em> versteht
man die Aufbereitung eines Programms
derart, dass es ohne weitere Änderungen
am Quelltext in mehreren Sprachen
benutzt werden kann. Der Begriff <em>i18n</em>
entstand als Abkürzung, indem man alle
Buchstaben zwischen dem ersten und dem
letzten wegließ und dazwischen die Menge
der entfernten Buchstaben angab.</td>
</tr>
<tr><td>Inhalt (content)</td>
<td>Aus der Sicht des CMF ist alles <em>Inhalt</em>.
Das gilt für traditionelle Dinge wie
HTML-Seiten, aber auch für dynamische
Informationen wie Beiträge in einer
Diskussion oder Kalendereinträge. Das
heißt auch, dass Bilder, herunterladbare
Programme, Programmlogik in Skripten usw.
ebenfalls Inhalte sind.</td>
</tr>
<tr><td>Inhaltstyp (content type)</td>
<td>Ein <em>Inhaltstyp</em> ist der in einer
CMF/Plone-Instanz erlaubte Inhalt. Plone
enthält vordefinierte Inhaltstypen,
aber Sie
können eigene Inhaltstypen für Ihre eigenen
Bedürfnisse erstellen und diese in Ihrer
Plone-Instanz verwenden.</td>
</tr>
<tr><td>Instanz</td>
<td>Objekte werden auch als <em>Instanzen</em>
bezeichnet. Eine Instanz bzw. Objekt ist
eine Instanz einer Klasse.</td>
</tr>
<tr><td>JavaScript</td>
<td>Eine in Webbrowsern eingebaute Sprache, mit
der Sie Webseiten dynamischer machen
können. Ein gutes Beispiel für JavaScript
ist das grüne Dropdown-Menü zum
Hinzufügen von Elementen.</td>
</tr>
<tr><td>Kalender</td>
<td><tt class="docutils literal">portal_calendar</tt> bietet einen
Mechanismus, der verwaltet, welche Inhalte
im Kalender angezeigt werden.</td>
</tr>
<tr><td>Katalog</td>
<td>Ein interner Index der Inhalte in Plone,
in dem gesucht werden kann. Auf das
Katalogobjekt kann man über das Objekt
<tt class="docutils literal">portal_catalog</tt> im ZMI zugreifen (siehe
auch Kapitel 11).</td>
</tr>
<tr><td>Klasse</td>
<td>Eine <em>Klasse</em> ist die Form, aus der Objekte
gestanzt werden. Objekte sind Instanzen
einer Klasse. Sie können sich eine Klasse
als Entwurf eines Objekts vorstellen.</td>
</tr>
<tr><td>Klassen-Konstruktormethode</td>
<td>Eine <em>Konstruktormethode</em> für eine Klasse
ist eine Methode, die die Ausführung von
gewissen Aktionen erlaubt, gleich nachdem
eine Instanz der Klasse erzeugt wird und
noch bevor diese benutzt wird.
Standardattribute z.B. würde man in einer
Konstruktormethode setzen.</td>
</tr>
<tr><td>l10n (<em>Localization</em>)</td>
<td>Unter <em>Lokalisierung</em> versteht man die
Vorbereitung der Daten für eine bestimmte
Sprache. Plone z.B. ist i18n-fähig und hat
Lokalisierungen für mehrere Sprachen. Der
Begriff <em>l10n</em> entstand genau wie <em>i18n</em>
als Abkürzung für <em>Localization</em>.</td>
</tr>
<tr><td>Lokale Rolle</td>
<td><em>Lokale Rollen</em> werden an einen bestimmten
Zope-Benutzer für ein bestimmtes Objekt
vergeben. Eine lokale Rolle bestimmt die
Rechte dieses Benutzers an diesem Objekt.
Eine lokale Rolle kann dazu verwendet
werden, die Rechte eines Benutzers am
gegebenen Objekt einzuschränken. Sie können
mit lokalen Rollen auch Benutzern, die
evtl. nur beschränkte globale Rechte haben,
erweiterte Rechte an einer kleinen
Untermenge von Objekten geben.</td>
</tr>
<tr><td>Manager</td>
<td>Eine Standardrolle in Plone.</td>
</tr>
<tr><td>Metatyp</td>
<td>Ein eindeutiger String zu jedem
Zope-Produkt im ZMI-Menü <em>Available</em>
<em>Objects</em>.
Instanzen von Produkten werden mit diesem
Metatyp erzeugt. Jedes Produkt hat einen
eindeutigen Metatyp.</td>
</tr>
<tr><td>Metadaten</td>
<td>Informationen über Inhalte (siehe
<a class="reference external" href="http://www.dublincore.org">http://www.dublincore.org</a>).</td>
</tr>
<tr><td>METAL</td>
<td>Macro Expansion Template Attribute
Language.</td>
</tr>
<tr><td>Migration</td>
<td>Eine <em>Migration</em> ist ein größtenteils
automatisierter Vorgang, bei dem Sie Ihre
Plone-Instanz auf eine höhere Version
aktualisieren.</td>
</tr>
<tr><td>Mitgliederdaten (Memberdata)</td>
<td>In Plone speichert das Werkzeug
<tt class="docutils literal">portal_memberdata</tt> die Attribute von
Benutzern.</td>
</tr>
<tr><td>Namespace</td>
<td>Ein <em>Namespace</em> enthält die Namen aller
gültigen Variablen einer gegebenen
Klasseninstanz (eines Objekts) in einem
bestimmten Sichtbarkeitsbereich.</td>
</tr>
<tr><td>Nicht ordnerartige Objekte
(nonfolderish objects)</td>
<td>Das sind Objekte, die die keine anderen
Zope- oder Plone-Objekte enthalten können.
Das können z.B. Dokumente oder Dateien
sein.</td>
</tr>
<tr><td>Oberklasse (base class)</td>
<td>Eine <em>Oberklasse</em> ist eine Klasse, die ihre
Methoden, Eigenschaften usw. an ihre
Unterklassen weitergibt. Diese Unterklassen
erben die Methoden und Eigenschaften ihrer
Oberklasse.</td>
</tr>
<tr><td>Objekt</td>
<td>Ein <em>Objekt</em> ist eine Instanz einer Klasse.</td>
</tr>
<tr><td>Objektdatenbank (ODB)</td>
<td>Ein System zur Speicherung einer Hierarchie
von Objekten. Die ZODB ist ein Beispiel
einer Objektdatenbank. Solche
Objektdatenbanken können Sie nicht genauso
abfragen wie relationale Datenbanken.</td>
</tr>
<tr><td>OOTB (out of the box)</td>
<td>Plone ist ein Beispiel einer
OOTB-Anwendung, d.h. einer Anwendung,
die man sofort nach der Installation
benutzen kann.</td>
</tr>
<tr><td>Ordnerartiges Objekt
(folderish object)</td>
<td>Ein <em>ordnerartiges</em> Objekt in Zope ist
eines, das andere Objekte enthalten kann.
Die Objekte <em>Folder</em> und <em>PloneFolder</em>
sind Beispiele dafür.</td>
</tr>
<tr><td>Plone</td>
<td>&quot;Wenn Sie es immer noch nicht wissen,...&quot;</td>
</tr>
<tr><td>Portal-Aktionen</td>
<td>Portal-Aktionen betreffen die ganze Site,
anders als Inhaltstypen-Aktionen, die nur
einen lokalen Geltungsbereich haben.</td>
</tr>
<tr><td>Portal-Typ</td>
<td>Der <em>Portal-Typ</em> ist ein eindeutiger String
für alle Inhaltstypen in Plone. Jeder
Inhaltstyp in Plone hat einen Portal-Typ
zu seiner eindeutigen Identifikation (auch
wenn mehrere auf dem gleichen Metatyp
basieren können).</td>
</tr>
<tr><td>Portlets</td>
<td><em>Portlets</em> sind die kleinen Abschnitte auf
einer Plone-Site, die links und rechts auf
einer Seite als kleine Kästen erscheinen.</td>
</tr>
<tr><td>Python</td>
<td><em>Python</em> ist eine objektorientierte
Scripting-Hochsprache, in der Zope
geschrieben ist.</td>
</tr>
<tr><td>QuantumLeap</td>
<td>Sie werden bemerken, dass in Plone sehr
lange Ergebnislisten auf mehreren Seiten
angezeigt werden. Sie können eine dieser
Seiten beliebig anspringen, wobei Sie in
der Navigation benachbarte Seiten sehen
können. Dieser Mechanismus wird liebevoll
als <em>QuantumLeap(ing)</em>, d.h. QuantenSprung,
bezeichnet.</td>
</tr>
<tr><td>Rechte (permissions)</td>
<td>Die Rechte bestimmen, was ein Benutzer in
Zope tun darf. Rechte können nur auf Rollen
angewendet werden. Sie können keine Rechte
an einzelne Benutzer vergeben.</td>
</tr>
<tr><td>Registrierung</td>
<td><tt class="docutils literal">portal_registration</tt> verwaltet die
site-weite Policy, die bestimmt, wie sich
Benutzer im System registrieren können.</td>
</tr>
<tr><td>Repurposing</td>
<td>Inhaltstypen können auf der FTI anderer
Inhaltstypen basieren, was man mit
<em>Repurposing</em> (Umwidmen) bezeichnet. Dann
können Sie für neue Inhaltstypen eindeutige
Metadatenattribute wie <em>id</em>, <em>title</em> oder
<em>description</em> angeben.</td>
</tr>
<tr><td>Skin</td>
<td>Stellen Sie sich eine <em>Skin</em> als
Look-and-Feel einer Interaktion mit Plone
vor. Eine Skin enthält HTML, CSS,
JavaScript, Bilder und alle Interaktionen
zwischen dem Benutzer und Plone. Sie
können verschiedene Skins auf den gleichen
Inhalt anwenden, d.h. ein Inhalt kann auf
viele verschiedene Weisen angezeigt werden.
Manche Skins bieten zusätzliche Features
und Seiten.</td>
</tr>
<tr><td>Stichwörter</td>
<td>Unter dem <em>Eigenschaften</em>-Reiter von
Inhalten
können Sie <em>Stichwörter</em> zuweisen (in der
Metadaten-Terminologie auch <em>Thema</em> (Topic)
genannt). Mit diesem Mechanismus können Sie
Inhalte zueinander in Verbindung setzen.
Stichwörter können im Werkzeug
<em>portal_metadata</em> vordefiniert werden.</td>
</tr>
<tr><td>Syndizierung (syndication)</td>
<td>Unter <em>Syndizierung</em> versteht man den
Vorgang, mit dem eine Site Informationen
mit anderen Sites austauschen kann.
Die Syndizierung von Inhalten im CMF
ermöglicht es, Inhalte für andere Sites
bereitzustellen. Mit dem Werkzeug für diese
Syndizierung können Site-Manager die
site-weite Syndizierung von Inhalten
verwalten. Diese Inhalte werden im
RSS-Format in Ordnern verfügbar gemacht,
in denen eine Syndizierung aktiviert wurde.</td>
</tr>
<tr><td>TAL</td>
<td>Die <em>Tag Attribute Language</em> ist eine
Sprache zur dynamischen Auszeichnung von
HTML.</td>
</tr>
<tr><td>TALES</td>
<td>Die <em>TAL Expression Syntax</em> ist eine Syntax
für die Erweiterung von TAL.</td>
</tr>
<tr><td>Werkzeug (tool)</td>
<td>Ein <em>Werkzeug</em> ist eine Instanz einer
Klasse in der Plone-Site. Anders als bei
anderen Objekten kann es jedoch immer nur
eine Instanz eines bestimmten Werkzeugs
in einer Plone-Site geben. Manche dieser
Werkzeuge, z.B. <tt class="docutils literal">portal_catalog</tt>, bieten
dem Site-Manager auch Verwaltungsoptionen.</td>
</tr>
<tr><td>Workflow</td>
<td>Eine Methode, um Geschäftslogik in einem
separaten Modul zu kapseln (siehe Kapitel
8).</td>
</tr>
<tr><td>XML</td>
<td>Die <em>Extensible Markup Language</em> ist ein
Standard für den Austausch von Daten.</td>
</tr>
<tr><td>ZMI</td>
<td>Das <em>Zope Management Interface</em> besteht im
Allgemeinen aus der Web-Schnittstelle für
die Verwaltung und Administration von Zope.
(Beachten Sie am Ende der URL
<a class="reference external" href="http://ihre.zope.site:8080/manage">http://ihre.zope.site:8080/manage</a>
das <em>manage</em>, wenn Sie sich anmelden.)</td>
</tr>
<tr><td>Zope</td>
<td><em>Zope</em> ist ein Open Source-
Web-Application-Server, der in Python
geschrieben ist. Plone setzt auf Zope auf.</td>
</tr>
<tr><td>ZPL</td>
<td>Die <em>Zope Public License</em> beschreibt die
Lizenzbedingungen für den Einsatz von Zope.</td>
</tr>
<tr><td>ZPT</td>
<td><em>Zope Page Templates</em> ist das System, das
dynamische Seiten mit Hilfe von TAL
erzeugt.</td>
</tr>
</tbody>
</table>
</div>
]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    
    <dc:date>2006-02-15T12:18:17Z</dc:date>
    <dc:type>Chapter</dc:type>
  </item>





</rdf:RDF>

