Direkt zum Inhalt | Direkt zur Navigation

Benutzerspezifische Werkzeuge

Sie sind hier: Startseite / Tech-Blog

Tech-Blog

Technische Artikel zu Themen: Zope, Plone, Python, Exim, Dovecot, Nagios, Shinken, Bacula und weiteren Open Source Themen...

Plone: ContentType mit NamedBlobFile in tests erzeugen

Im folgenden wird beschrieben, wie man einen ContentType mit einen NamedBlobFile field, in einem Unitest erzeugen kann.

Wir benötigen dazu eine Datei z.B. eine PDF-Datei, die wir programmatisch in unseren ContentType einsetzen können. Diese legen wir einfach in unserem tests-Verzeichniss, parallel zu unserem test ab.

Der folgende Code liest die Datei file.pdf ein und fügt sie in das Feld file des Objektes MyContentTypeObj

def getTestFile(filename):
    """ return contents of the file with the given name """
    filename = os.path.join(os.path.dirname(__file__), filename)
    return open(filename, 'r')

self.portal.invokeFactory(
    "MyContentType",
    id="mycontenttypeobj",
    title="My Content Type Obj",
)
MyContentTypeObj = self.portal.get('mycontenttypeobj')

data = getTestFile('file.pdf').read()
MyContentTypeObj.file = NamedBlobFile(
    data=data,
    contentType='',
    filename=u'file.pdf')

Plone: Javascript Links in Texten erlauben

Plone filter unsichere Inhalte aus volltexten. Wenn man aber alle Nutzer seines Portals kennt, kann man diese Regeln aus praktischen Gründen etwas lockern. z.B. ist es manchmal nützlich Links mit Javascript Funktionalität im Text platzieren zu können. Wie dies möglich gemacht wird, wird nachfolgend erklärt.

Das filtern von Javascript deaktivieren

Dazu müssen wir unter die Motorhaube, ins ZMI in /portal_transforms/safe_html gehen. Dort können wir den Wert für remove_javascript von 1 auf 0 ändern.

Danach am besten Plone neu starten, damit die Änderung wirklich sichtbar wird.

Plone: Kontakt-Link (site-action) für 2 Sprachen konfigurieren

Es ist etwas umständlich eine site-action wie die für das Kontaktformular für mehrere Sprachen auf das entsprechende Kontaktformular verweisen zu lassen. Daher sei es hier nochmal anhand eines mit PloneFormGen erstellten Formulares erleutert.

Wir legen uns mit PloneFormGen ein Kontaktformular an und übersetzen dies, so dass wir nun 2 Formulare mit unterschiedlichem Namen (kontakt und contact) haben.

site-action URL Expression

Die folgende URL-Expression tragen wir in die entsprechende site-action in portal_actions ein.

python: plone_portal_state.language() == 'de' and globals_view.navigationRootUrl() + '/kontakt' or globals_view.navigationRootUrl() + '/contact'

Wenn es mehr als 2 Sprachen werden sollten, empfiehlt es sich hier ein ScriptPython zu verwenden welches den entsprechenden String dann zurück gibt.

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.

Plone: AutoRename für Dexterity content types

Im folgenden wird beschrieben wie man Dexterity Objekte in Plone, nach dem Erzeugen automatisch umbenennen kann.

Ein Weg dies zu erreichen, ist es einen EventHandler zu verwenden

Wir erzeugen eine Datei mit dem Namen "subscribers.py" in unserem Package.

from plone import api
from zope.container.interfaces import INameChooser
from .myobject import IMyObject

def autorename_my_object(obj, event):
    if not IMyObject.providedBy(obj):
        log.error("provides not IMyObject")
        return
    parent = event.newParent
    new_id = INameChooser(parent).chooseName(obj.my_object_name, obj)
    api.content.rename(obj=obj, new_id=new_id)

Der Parameter obj.my_object_name stellt dabei die Vorlage für den Namen dar. Dieser wird normalisiert und es wird sicher gestellt, dass er nicht mit vorhandenen Objekten kollidiert.

Dann registrieren wir den EvendHandler via zcml in der configure.zcml

<subscriber
  for="my.example.myobject.IMyObject
       zope.lifecycleevent.interfaces.IObjectAddedEvent"
  handler=".subscribers.autorename_my_object"
  />

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.

Bibliotheken zur Konvertierung und Volltextindizierung von Dokumenten

Plone kann out of the box PDF-Dateien, Openoffice/Libreoffice und MSOffice Dokumente wie Dokumente und Tabellen sowie auch Impress und Powerpoint Presentationen konvertieren und für die Volltextsuche indizieren. Hierfür müssen auf dem Server ein paar Bibliotheken installiert werden.

Unter Debian Linux sind folgende Bibliotheken zu installieren

  • poppler-utils
  • libxslt1-dev
  • libxml2-dev
  • wv
  • xsltproc
  • unzip

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.