Tech-Blog
Technische Artikel zu Themen: Zope, Plone, Python, Pyramid, Django, Exim, Dovecot, Nagios, Shinken, Bacula und weiteren Open Source Themen...
Plone: einen SecureMailHost mit Python anlegen
Ziel (Kontext) für den SecureMailHost bestimmen
factory_dispatcher = container.manage_addProduct['SecureMailHost']
SecureMailHost erzeugen
factory_dispatcher.manage_addMailHost( 'MySecureMailHost', title='My SecureMailHost', smtp_host='mail.example.com', smtp_port=25, smtp_userid='jon@example.com', smtp_pass=None, smtp_notls=None, REQUEST=None)
Plone: zum MIME-Type passende icons anzeigen
Icon für eine Datei ermitteln
Plone stellt für solche Aufgaben eine BrowserView mit dem Namen "contenttype_utils" zur Verfügung. Wir holen uns die View und verwenden die Methode "getMimeTypeIcon" um uns den passenden icon URL zu ermitteln.
contenttype_utils = context.restrictedTraverse('contenttype_utils') icon_url = contenttype_utils.getMimeTypeIcon(context.file)
Context ist hier unser Dateiobjekt und file das entsprechende NamedBlobFile field.
Vorschaubilder (contentleadimage) für Dexterity basierte Ordner aktivieren
LeadImage Behavior
Die neuen Dexterity basierten Inhaltstypen bringen mehr Flexibilität bei den Grundfunktionen mit. Schon die Grundfunktionen werden aus verschiedenen Behavior zusammen gesetzt. Diese Behavior bringen einzelne Felder oder Funktionalitäten mit und können für alle Inhaltstypen aktiviert werden. Im Normalfall wird dies über eine GenericSetup-Anweisung wie z.B. in ./profiles/default/types/Folder erreicht.
<property name="behaviors" purge="False"> <element value="plone.app.contenttypes.behaviors.leadimage.ILeadImage"/> </property>
Mit dieser Anweisung in unserem eigenen Add-on ermöglichen wir Vorschaubilder für Ordner. Wenn Sie kein eigenes Add-on verwenden möchten, können Sie das Behavior auch in der Plone-Konfiguration und "Dexterity Inhaltstypen" aktivieren.
Plone: Überschreiben von Views, Viewlets und resource Einträgen
Überschreiben von einem Resource-Eintrag einer Plone-erweiterung
Wir haben z.B. einen Originaleintrag einer Plone-Erweiterung, die wir überschreiben möchten.
<browser:resource name="jquery.prettyPhoto.js" file="javascript/jquery.prettyPhoto.js" layer="..interfaces.IPrettyPhotoSpecific" />
Überschreiben mit einem Layer
Wir übernehmen einfach die Originalanweisung in eine configure.zcml in unserem Theme Package und übernehmen ebenfalls die zugehörige Ressource jquery.prettyPhoto.js. Wäre hier jetzt kein Layer verwendet worden, könnten wir einfach die gleiche Anweisung plus einer Layer-Anweisung in unserem Theme verwenden. Aber da hier schon im Original ein Layer definiert ist, müssen wir dafür sorgen das unsere Anweisung spezifischer ist. Dies erreichen wir in dem wir einen eigenen Layer angeben und diesen von dem Original-Layer ableiten.
<browser:resource name="jquery.prettyPhoto.js" file="javascript/jquery.prettyPhoto.js" layer=".interfaces.IThemeSpecific" />
Der Layer IThemeSpecific ist dann wie folgt in unserem Theme definiert.
from collective.prettyphoto.interfaces import IPrettyPhotoSpecific class IThemeSpecific(IPrettyPhotoSpecific): """Marker interface that defines a Zope 3 browser layer. """
Nach einem Neustart von Plone sollte jetzt unsere Version der Resource verwendet werden.
Überschreiben mit overwrites.zcml
Eine etwas gröbere Methode ZCML-Registrierungen zu überschreiben ist es, sie per overwrites.zcml zu überschreiben. Dies kann z.B. sinnvoll sein, wenn man die Registrierung für die ganze Zope-Instanz überschreiben möchte.
hierzu legen wir eine overwrites.zcml in unserem Package an und kopieren einfach die Originalanweisung hinein.
<browser:resource name="jquery.prettyPhoto.js" file="javascript/jquery.prettyPhoto.js" layer="collective.prettyphoto.interfaces.IPrettyPhotoSpecific" />
In der buildout.cfg muss die overwrites für dieses Package dann expliziet geladen werden. Wenn unser Theme Package my.theme heißt, dann lautet der Eintrag wie folgt.
zcml = my.theme-overrides
Zope / Plone: global utilities erzeugen und verwenden
Eigenschaften von global utilities
- global utilities werden beim Start der Zope-Instanz instanziert
- sind nicht persistent und können daher auch keine persistenten Daten enthalten
- können problemlos jeder Zeit entfernt werden, da keien persistente Registrierung in der Datenbank erfolgt
Ein Global Utility erzeugen
Utility Klasse
Um ein global utility zu erzeugen, legen wir uns eine Datei utilities.py mit folgenden Inhalt in unserem Package an:
from zope.site.hooks import getSite class PloneClipboardTool(object): """ provide some useful methods """ def get_plone_portal(self): """ return the portal object """ portal = getSite()
Utility Interface
In der Datei interfaces.py definieren wir ein entsprechendes Interface:
from zope.interface import Interface class IPloneClipboardTool(Interface): def get_plone_portal(self): """ return the portal object """
Utility Registrierung
In der configure.zcml registrieren wir unser global utility wie folgt:
<utility factory=".tool.PloneClipboardTool" provides=".interfaces.IPloneClipboardTool"/>
Ein Global Utility verwenden
Um auf das global utility zuzugreifen, verwenden wir getUtility() oder queryUtility().
getUtility
from zope.component import getUtility my_tool = getUtility(IPloneClipboardTool)
Sollte kein Utility gefunden werden, würde man hier einen ComponentLookupError bekommen.
*** ComponentLookupError: (<InterfaceClass inqbus.plone.clipboard.interfaces.IPloneClipboardTool>, 'test')
queryUtility
from zope.component import queryUtility my_tool = queryUtility(IPloneClipboardTool)
Sollte kein Utility gefunden werden, würde man hier 'None' bekommen.
Der folgende Aufruf bringt uns dann das Portalobjekt zurück.
plone_portal = my_tool.get_plone_portal()
Analog kann man beliebig viele Utilities und Methoden darin erzeugen und verwenden.
Plone: Dexterity Events
Die Dexterity Events sind in plone.dexterity.interfaces definiert:
class IBegunEvent(IObjectEvent): """Base begun event """ class IEditBegunEvent(IBegunEvent): """An edit operation was begun """ class IAddBegunEvent(IBegunEvent): """An add operation was begun. The event context is the folder, since the object does not exist yet. """ class ICancelledEvent(IObjectEvent): """Base cancel event """ class IEditCancelledEvent(ICancelledEvent): """An edit operation was cancelled """ class IAddCancelledEvent(ICancelledEvent): """An add operation was cancelled. The event context is the folder, since the object does not exist yet. """ class IEditFinishedEvent(IObjectEvent): """Edit was finished and contents are saved. This event is fired even when no changes happen (and no modified event is fired.) """
Manager werden im zope debug Modus
Mit Hilfe folgender Anweisungen, macht man sich zum Manager (admin):
from AccessControl.SecurityManagement import newSecurityManager, \ getSecurityManager, setSecurityManager old_sm = getSecurityManager() portal = app["Plone"] newSecurityManager(portal, portal.getOwner()) # do some thing as manager setSecurityManager(old_sm)
PDF-Dateien mit Sphinx unter Debian erzeugen
$ aptitude install texlive-base texlive-latex-extra
Wenn alle benötigten Pakete installiert sind sollte das folgende Komando eine PDF-Datei im Build-Ordner erzeugen.
$ make latexpdf [...] pdflatex finished; the PDF files are in build/latex.
Plone: Zope3 Viewlets
Was sind Viewlets?
Viewlets sind kleine Snipsel von Funktionen und Templates. Sie können statische Templates oder auch Funktionalitäten enthalten. Viewlets können in Plone für sortierbare ViewletManager registriert und dort angezeigt werden.
Plone Viewlets und ViewletManager
Die vorhandenen Viewlets und Viewletmanager können gut mit der in Plone eingebauten @@manage-viewlets View untersucht werden. In einem gewissen Rahmen können dort auch Veränderungen vorgenommen werden. So können z.B. Viewlets aus oder eingeblendet und innerhalb eines ViewletsManagers umgeordnet werden. Diese Aktionen können aber auch wie unten zu sehen, per GenericSetup in der viewlets.xml konfiguriert werden.
Viewlet code
from plone.app.layout.viewlets.common import ViewletBase class ExampleViewlet(ViewletBase): def update(self): """ """ self.id = self.context.id
Viewlet page template
Als Page Template braucht es nicht viel, jeder ZPT Schnipsel ohne komplettes XHTML-Gerüst reicht. Das Viewlet wird immer in einem anderen Template gerendert und benötigt daher kein eigenes Grundgerüst.
Hier mal ein Beispiel:
<ul id="enl_actions" i18n:domain="EasyNewsletter"> <li><a tal:attributes="href string:${view/enl_url}/easynewsletter_view" i18n:translate="">Archive</a></li> <li><a tal:attributes="href string:${view/enl_url}/enl_drafts_view" i18n:translate="">Drafts</a></li> <li><a tal:attributes="href string:${view/enl_url}/enl_subscribers_view" i18n:translate="">Subscribers</a></li> </ul>
Damit die Lokalisierung funktioniert muss hier die i18n:domain gesetzt werden.
Viewlet per zcml registrieren
<browser:viewlet name="derico.viewlet_example" manager="plone.app.layout.viewlets.interfaces.IBelowContent" template="viewlet_example.pt" layer="derico.viewlet_example.interfaces.IAddOnInstalled" permission="zope2.View" />
Viewlet per GenericSetup konfigurieren
In der Datei viewlets.xml kann man wie folgt die Viewlets konfiguerieren.
<?xml version="1.0"?> <object> <order manager="plone.belowcontent"> <viewlet name="derico.viewlet_example" insert-before="*" /> </order> </object>
Reihenfolge der Viewlets festlegen
<?xml version="1.0"?> <object> <order manager="plone.belowcontent"> <viewlet name="derico.viewlet_example" insert-before="*" /> <viewlet name="derico.viewlet_example2" insert-after="plone.belowcontenttitle.keywords" /> </order> </object>
Viewlets ein- und ausblenden
<?xml version="1.0"?> <hidden manager="plone.portalheader" skinname="*"> <viewlet name="plone.global_sections" /> </hidden>
<?xml version="1.0"?> <object> <hidden manager="plone.portalheader" skinname="*" purge="True" /> </object>
Eigenen Viewletmanager definieren
Wenn man das Viewlet nicht in die bestehenden Viewletmanager von Plone einhängen möchte, kann man sich selbst, in einem Template einen content provider definieren und einen Viewletmanager hierfür definieren.
from zope.viewlet.interfaces import IViewletManager class IExampleViewletmanger(IViewletManager): """ A viewlet manager that sits in the template and render the viewlets """
In einem Template kann man für den Viewletmanager wie folgt einen Platzhalter definieren. Hier werden die Viewlets dann eingefügt.
<div tal:replace="structure provider:derico.example_viewletmanager" />
Eigenen Viewletmanager registrieren
<browser:viewletManager name="derico.exampleviewlet" provides=".manager.IExampleViewletmanager" permission="zope2.View" class="plone.app.viewletmanager.manager.OrderedViewletManager" />
Diazo: CSS Klasse per XSLT in body-Tag einfügen
Plone hat ein 3 Spaltenlayout, sofern rechts und links Portlets vorhanden sind. In einigen Bereichen möchte man aber z.B hin und wieder die rechte Spalte ausblenden um mehr Platz zu haben. Hat man jetzt ein fixes Grid-Layout so bleibt die rechte Spalte leer, die mittle Spalte dehnt sich aber nicht aus. Die Lösung kann eine CSS-Klasse im body-Tag sein, welche dann in den Stylesheets als Marker verwendet werden kann.
CSS-Klasse in body-Tag einfügen
<xsl:attribute name="class"><xsl:value-of select="/html/body/@class" /> three_col</xsl:attribute>
Bedingtes Einfügen
<before theme-children="/html/body" method="raw"> <xsl:attribute name="class"><xsl:value-of select="/html/body/@class" /><xsl:if css:test="#portal-column-one"> col-one</xsl:if><xsl:if css:test="#portal-column-content"> col-content</xsl:if><xsl:if css:test="#portal-column-two"> col-two</xsl:if></xsl:attribute> </before> <drop css:theme="#portal-column-one" css:if-not-content="#portal-column-one" /> <drop css:theme="#portal-column-content" css:if-not-content="#portal-column-content" /> <drop css:theme="#portal-column-two" css:if-not-content="#portal-column-two" />