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

Manage nodejs, grunt and bower with buildout

How you can manage nodejs, grunt and bower including npm and bower dependencies with zc.buildout.

Buildout config

We can use gp.recipe.node recipe, for details see: https://pypi.python.org/pypi/gp.recipe.node

[buildout]
parts +=
    nodejs
    bower
    node_modules

[nodejs]
recipe = gp.recipe.node
version = 0.10.22
npms = grunt-cli bower less jshint csslint blueimp-tmpl uglify-js
scripts = grunt bower lessc jshint csslint tmpl.js uglifyjs

[bower]
recipe = collective.recipe.cmd
shell = /bin/bash
on_install = true
on_update = true
cmds = ./bin/bower install --config.interactive=false; echo $'\nBower modules installed.\n'

[node_modules]
recipe = collective.recipe.cmd
shell = /bin/bash
on_install = true
on_update = true
cmds = NODE_PATH="" bin/npm install .; echo $'\nNodeJS modules installed.\n'

Plone: Mit Diazo eine HTML-Liste in eine Selection verwandeln

Wenn mann statt einer HTML-Liste (<ul>) eine Auswahlliste (<select>) benötigt, kann man dies leicht mit Diazo umwandeln, ohne tief ins System eingreifen zu müssen. Im folgenden wird die Aktion anhand eines Beispiels erläutert. Wir wandeln hier ein einfaches flaches Navigations-Portlet von Plone in eine Auswahl-Liste (<select>) um.

Ausgangsstruktur

Das HTML des Portlets, welches nur die Unterordner anzeigt, also aus einer Ebene besteht, sieht wie folgt aus:

<dl class="portlet portletNavigationTree">
  <dt class="portletHeader">
    <span class="portletTopLeft"></span>
    <a href="http://127.0.0.1:8081/Plone/sitemap" class="tile">SubNavi</a>
    <span class="portletTopRight"></span>
  </dt>

  <dd class="portletItem lastItem">
    <ul class="navTree navTreeLevel0">
      <li class="navTreeItem visualNoMarker navTreeFolderish section-etwas-ueber-plone">
        <a title="" class="state-published navTreeFolderish contenttype-folder"
          href="etwas-ueber-plone">
          <span>Etwas über Plone</span>
        </a>
      </li>
      <li class="navTreeItem visualNoMarker navTreeFolderish section-etwas-ueber-python">
        <a title="" class="state-published navTreeFolderish contenttype-folder"
          href="etwas-ueber-python">
          <span>Etwas über Python</span>
        </a>
      </li>
      <li class="navTreeItem visualNoMarker navTreeFolderish section-etwas-ueber-zope">
        <a title="" class="state-published navTreeFolderish contenttype-folder"
          href="etwas-ueber-zope">
          <span>Etwas über Zope</span>
        </a>
      </li>
    </ul>
     <span class="portletBottomLeft"></span>
     <span class="portletBottomRight"></span>
  </dd>
</dl>

Zielstruktur

Die HTML-Zielstruktur sieht wie folgt aus:

<div id="subNavWrapper">
  <form action="" method="get">
    <select name="subNavSel" size="1">
      <option value="etwas-ueber-plone"Etwas über Plone</option>
      <option value="etwas-ueber-python">Etwas über Python</option>
      <option value="etwas-ueber-zope">Etwas über Zope</option>
    </select>
  </form>
</div>

Diazo-Rules

Diazo-Rules (Regeln) zum umwandeln in die Zielstruktur:

<replace
  content-children="//dl[contains(@class, 'portletNavigationTree') and ./dt/a/text() = 'SubNavi']">

  <select name="subNavSel" size="1">
  <xsl:for-each select=".//ul[contains(@class, 'navTree')]/li/a">
    <option><xsl:attribute
        name="value"><xsl:value-of
          select="./@href" /></xsl:attribute><xsl:copy-of
          select="./span/text()" /></option>
  </xsl:for-each>
  </select>

</replace>

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