Direkt zum Inhalt | Direkt zur Navigation

Benutzerspezifische Werkzeuge

Sie sind hier: Startseite / Tech-Blog

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

Im Nachfolgenden wird Beschrieben wie man einen SecureMailHost programmatisch mit Python anlegen kann.

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

Plone hat eine mimetype_registry in der für viele MIME-Types auch icons hinterlegt sind. Diese können wie folgt zur Anzeige verwendet werden.

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

In älteren Versionen konnte man mit dem Add-on collective.contentleadimage einigen Inhalttypen Vorschaubilder ähnlich dem der Nachrichten verpassen. Für die neuen plone.app.contenttypes, welche auf Dexterity basieren funktioniert die Erweiterung nicht. Aber es gibt einen anderen eleganteren Weg.

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

Plone bietet vielfältige Möglichkeiten, vorhandene Elemente zu überschreiben. Unter anderem können wir mit Hilfe einfacher ZCML-Anweisungen leicht Views, Viewlets, Resource-Einträge usw von Plone oder Plone-Erweiterungen überschreiben.

Ü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.

System Message: ERROR/3 (<string>, line 27)

Unexpected indentation.
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

Mit Zope Global Utilities kann man Methoden, die man an verschiedenen Stellen in einem Portal benötigt zentral zur Verfügung stellen und an beliebigen Stellen 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

Im Zope Debug Modus, ist man Standardmäßig kein Manager, was hin und wieder aber benötigt wird.

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

Mit Sphinx kann man nicht nur ansehnliche HTML-Dokumente erzeugen, sondern auch entsprechende PDF-Dokumente. Unter Debian werden ein paar Pakete benötigt, damit dies funktioniert.
$ 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

Seit Plone3 finden Zope3-Viewlets immer häufiger Verwendung in Plone. Im folgenden wird gezeigt, wie ein eigenes Viewlet erstellt und dieses an die gewünschte Stelle in Plone eingebaut werden kann.

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

Manchmal reichen die Möglichkeitenvon Diazo alleine nicht aus um zum Ziel zu gelangen. Im folgenden Beispiel möchte ich zeigen, wie man mit Hilfe von XSLT CSS Klassen, in Abhängigkeit des Vorhandenseins der Portletspalten von Plone, in das body-Tag einfügen kann.

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" />