Tech-Blog
Technische Artikel zu Themen: Zope, Plone, Python, Exim, Dovecot, Nagios, Shinken, Bacula und weiteren Open Source Themen...
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" />
Plone: Password encrypt
Beispiel
Password verschlüsseln
from AccessControl import AuthEncoding def _encryptPassword(self, pw): return AuthEncoding.pw_encrypt(pw, 'SSHA')
Password überprüfen
from AccessControl import AuthEncoding AuthEncoding.pw_validate(saved_pw, entered_pw)
Plone: PloneFormGen readonly fields
Ein verstecktes Feld anlegen
Wir legen ein FormStringField (Zeichenkettenfeld) an, füllen den Default wert mit dem Inhalt der angezeigt werden soll und markieren das Feld als versteckt.
Damit ist das Feld im Formular enthalten und wird beim versenden mit übertragen.
Ein Feld zur Anzeige des versteckten Feldes anlegen
Wir legen ein FormRichLabelField (Bezeichnungsfeld) an und geben in den overrides den folgenden TALES-Ausdruck ein:
string: <strong>My hidden field text:</strong> ${folder/myhiddenfield/getFgDefault}
Das Feld zeigt nun immer den Inhalt des Default-wertes des Feldes myhiddenfield an, kann aber selbst nicht editiert werden.
Text per CSS rotieren mit Compass
HTML Markup
<div id="box"> <h1 id="parent-fieldname-title" class="documentFirstHeading"> Spielplatzinspektion </h1> </div>
Compass SCSS
@import "compass/css3/transform"; #box{ width: 400px; height: 400px; border: 1px solid black; margin: 0 auto; } .verticalHeadline{ font-size: 12pt; margin: 0; text-align: center; border: 1px solid green; @include rotate(270deg); @include apply-origin(top right, false); position: relative; right: 2em; }
Resultat
Plone: Diazo Theme & password_reset: Passwort setzen fehlgeschlagen
Fehlendes base-Tag
Das Problem, im Theme fehlt das base-Tag welches Plone selbst rendert.
<base href="http://inqbus-hosting.de/" />
Dieses sollte mit folgender Regel ins Theme übertragen werden.
<drop theme="/html/head/title" /> <drop theme="/html/head/base" /> <drop theme="/html/head/comment()" /> <after content="/html/head/base | /html/head/comment() | /html/head/title" theme-children="/html/head" />
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" />
Plone: persistente Utilities manuell entfernen
Wenn man eine solche fehlerhafte Erweiterung deinstalliert und diese auch aus dem Buildout entfernt, kommt es zu Fehlermeldungen der folgenden Art.
AttributeError: type object 'IWarehouseContainer' has no attribute '__iro__'
Um den Fehler zu beheben, müssen wir die Erweiterung noch einmal im Buildout installieren.
Registrierung finden
Am besten schauen wir uns als erstes den Code der Erweiterung an, der das local utility registriert. Hier zu suchen wir in dem package nach dem Interface 'IWarehouseContainer'.
~/.buildout/eggs/getpaid.warehouse-0.3-py2.7.egg/getpaid/warehouse $ grep -r "IWarehouseContainer" .
Wir finden hier die Registrierung.
sm.registerUtility(component=warehouses, provided=interfaces.IWarehouseContainer )
Dies wird uns helfen das Utility zu entfernen.
Utility entfernen
Um das local utility zu entfernen starten wir die Instanz mit debug.
./bin/instance debug
Nun holen wir uns den site manager.
sm = app.Plone.getSiteManager()
Anschließend importieren wir das Interface IWarehouseContainer und holen uns das Utility.
from getpaid.warehouse.interfaces import IWarehouseContainer warehouse = sm.queryUtility( IWarehouseContainer )
Das Utility können wir jetzt mit sm.unregisterUtility(IWarehouseContainer) aus der Registrierung entfernen. Da das Utility selbst IWarehouseContainer nicht implementiert, muss das Interface in diesem Fall mit angegeben werden.
sm.unregisterUtility(warehouse, provided=IWarehouseContainer)
Reguläre Ausdrücke in Python
Dokumentation zu regulären Ausdrücken in Python
Ich möchte hier nicht die vorhandene Dokumentation wiederholen, daher sei hiermit auf darauf verwiesen:
Beispiele
Zum testen von Regulären Ausdrücken eigent sich folgender Aufruf:
p = re.search(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)', "Herr Conrad Meier")
Hier wird das Modul re direkt verwendet, was zum testen ok ist, später ist es besser den regulären Ausdruck zu Kompelieren, damit die Operationen schneller ausgeführt werden können.
p = re.compile(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)') p.search("Herr Conrad Meier")
Adressblock parsen
Wir wollen folgenden Adressblock parsen und Anrede, Name sowie PLZ und Ort auslesen.
ADDRESSBLOCK = u""" Inqbus GmbH & Co. KG Herr Maik Derstappen Geschäftsführer Karl-Heine-Str. 99 04109 Leipzig Plagwitz Sachsen Deutschland"""
Wir definieren uns nun 2 reguläre Ausdrücke, die die Zeile mit der Anrede und dem Namen, sowie die Zeile mit der PLZ und dem Ort beschreiben. In den Ausdrücken werden benannte Guppen erzeugt, um auf diese später einfach zugreifen zu können.
z.B. erzeugt folgender Ausdruck eine Gruppe mit dem Namen salutation, die die Anrede (Frau/Herr) enthält.
(?P<salutation>^Frau|^Herr)
ReSalutationName = re.compile(r'(?P<salutation>^Frau|^Herr)\s+(?P<name>.*)') ReZipecodeCity = re.compile(r'(?P<zipcode>\d{4,5})\s+(?P<city>.*)')
Im folgenden iterieren wir über die Zeilen des Adressblockes und wenden die regulären ausdrücke auf jede Zeile an. Wenn z.B. der erste Ausdruck ReSalutationName.search(line.strip()) ein Ergebnis zurück liefert, können wir auf die definierten Gruppen (salutation, name) per match_object.group('Gruppenname') zugreifen und bekommen den Teilstring der Gruppe zurück.
for line in ADDRESSBLOCK: salutation_name = ReSalutationName.search(line.strip()) if salutation_name: salutation = salutation_name.group('salutation') name = salutation_name.group('name') continue zipcode_city = ReZipecodeCity.search(line.strip()) if zipcode_city: zipcode = zipcode_city.group('zipcode') city = zipcode_city.group('city') continue
How to install MySQLdb in virtualenv on Debian
If you try to install MySQL-Python with pip as follow:
pip install MySQL-Python
You will get this error, because of missing header files of libmysqlclient:
$ pip install MySQL-Python
Downloading/unpacking MySQL-Python
Downloading MySQL-python-1.2.3.tar.gz (70Kb): 70Kb downloaded
Running setup.py egg_info for package MySQL-Python
sh: 1: mysql_config: not found
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "/home/maik/.virtualenvs/env1/build/MySQL-Python/setup.py", line 15, in <module>
metadata, options = get_config()
File "setup_posix.py", line 43, in get_config
libs = mysql_config("libs_r")
File "setup_posix.py", line 24, in mysql_config
raise EnvironmentError("%s not found" % (mysql_config.path,))
EnvironmentError: mysql_config not found
Complete output from command python setup.py egg_info:
sh: 1: mysql_config: not found
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "/home/maik/.virtualenvs/env1/build/MySQL-Python/setup.py", line 15, in <module>
metadata, options = get_config()
File "setup_posix.py", line 43, in get_config
libs = mysql_config("libs_r")
File "setup_posix.py", line 24, in mysql_config
raise EnvironmentError("%s not found" % (mysql_config.path,))
EnvironmentError: mysql_config not found
This error can be solved by installing the libmysqlclient header files.
aptitude install libmysqlclient-dev