Python 2.3 unter Mac OS X 10.5 (Leopard) bauen
Python 2.3 lässt sich unter Leopard nicht ohne Eingriffe bauen. Ich erläutere hier, was zu tun ist, um ein Python ohne Framework-Unterstützung zu bauen, so wie man es zum Beispiel für ältere Zope-Versionen benötigt.
Nach dem Herunterladen von Python 2.3.6 und dem Entpacken muss man erst mal das configure-Script bearbeiten, da ein Workaround benutzt wird, der in Leopard nicht mehr nötig ist und nicht mehr funktioniert.
sed "s/-u __dummy //" configure > configure.leo
chmod 755 configure.leo
./configure.leo --prefix=<Installationsverzeichnis>
Das Configure-Script setzt einige Präprozessordirektiven falsch, die sind folgendermaßen zu korrigieren:
echo '#undef _POSIX_C_SOURCE' >> pyconfig.h
echo '#undef _XOPEN_SOURCE' >> pyconfig.h
echo '#define HAVE_BROKEN_POSIX_SEMAPHORES' >> pyconfig.h
Jetzt ist es möglich, Python zu bauen und zu installieren:
make
make install
Nun fehlt noch readline-Unterstützung. Dazu gibt es verschiedene Varianten. Die folgende von bbum@mac.com war die einzige, die bei mir funktioniert hat:
svn co http://svn.red-bean.com/bbum/trunk/pyreadline/
cd pyreadline
tar -xzf readline-5.1.tar.gz
cd readline-5.1
./configure --disable-shared --enable-static
sudo make install
cd ../readline-0.0.0
<Pfad zu installiertem python> setup.py install
Diese Anleitung basiert auf der Anleitung für Python2.3 unter Tiger und Readline für Python.
- Category(s)
- Python
Zope3: Testen von felderübergreifenden Bedingungen in Interfaces mit zope.formlib -- aktualisiert
Um felderübergreifende Bedinungen (interface invariants) mit zope.formlib testen zu können sind in Zope 3.4 keine Klimmzüge mehr nötig.
Dieser Eintrag ist eine Aktualisierung des Eintrags Testen von felderübergreifenden Bedingungen in Interfaces mit zope.formlib, der sich auf Zope 3.2 bezieht. Mit Zope 3.4 ist alles viel einfacher. (Zope 3.3 habe ich nicht getestet, da die Ideen aber aus Philipp von Weitershausens Buch stammen, sollte es auch da funktionieren.)
Definition
Feldübergreiffende Bedingungen werden in Interfaces als invariants
definiert. Als Beispiel soll ein Passwortfeld und eins für die
Wiederholung des Passworts dienen. Beide Werte müssen also gleich sein.
Wenn die Bedingung nicht erfüllt ist, soll eine Exception geworfen
werden. Da es sich um einen Validierungsfehler handelt, nutze ich zope.interface.Invalid.
Das Interface hat dann im Minimum folgende Gestalt:
import zope.interfaceEs werden die beiden Felder definiert und zum Schluss wird die Funktion arePasswordsEqual als über den Dekorator (@zope.interface.invariant) als Invariante des Interfaces definiert.
class IUser(zope.interface.Interface):
password = zope.schema.Password(title=u"Passwort")
password2 = zope.schema.Password(title=u"Wiederholung des Passworts")
@zope.interface.invariant
def arePasswordsEqual(user):
if user.password != user.password2:
raise zope.interface.Invalid(
u"""Das Passwort und die Wiederholung sind nicht gleich.""")
Benutzung mit zope.formlib
zope.formlib testet die Invarianten automatisch und zeigt Fehler oberhalb des Fomulars an. Es wird der Text der Exception angezeigt, der sich problemlos internationalisieren lassen sollte.
- Category(s)
- Zope 3
Programmatisch einen Zope3-Ordner in eine Site umwandeln
Eine Site enthält einen "SiteManager" und kann damit lokale Utilities beinhalten. Aus einem Ordner im ZMI eine Site zu machen, sind nur ein paar Klicks, programmatisch ist es aber auch nicht viel schwerer.
Angenommen, immer wenn eine Instanz der Klasse MySite angelegt wird, soll daraus eine Site gemacht werden. Es wird weiterhin angenommen, dass MySite das Interface IMySite implementiert.
Man abonniert zuerst per ZCML den ObjectAddedEvent:
<subscriber
for="zope.app.container.interfaces.IObjectAddedEvent"
handler=".eventhandler.onObjectAdded"
/>
Wir legen nun eine Datei eventhandler.py an und definieren dort die Funktion onObjectAdded, mit folgendem Inhalt:
from mypackage import IMySite
import zope.app.appsetup.bootstrap
import zope.app.component.interfaces
import zope.app.component.site
import zope.app.component.hooks
import zope.app.intids
import zope.app.intids.interfaces
def onObjectAdded(event):
if IMySite.providedBy(event.object):
# Macht aus object eine Site, wenn es noch keine ist
if not zope.app.component.interfaces.ISite.providedBy(event.object):
site_manager = zope.app.component.site.LocalSiteManager(event.object)
event.object.setSiteManager(site_manager)
# Wenn in der Site lokale Utilities installiert werden sollen, die
# sich auf den thread-globalen Context verlassen, dann ist dieser
# temporär zu setzen, damit in der richtigen, nämlich der neuen
# Site gesucht wird.
try:
old_site = zope.app.component.hooks.getSite()
zope.app.component.hooks.setSite(event.object)
# Hier kann man jetzt die Utilities installieren.
# z.B. ein IntId-Util
zope.app.appsetup.bootstrap.ensureUtility(event.object,
zope.app.intid.interfaces.IIntIds, '', zope.app.intid.IntIds)
finally:
# Zum Schluss wird der alte Zustand wieder hergestellt.
zope.app.component.hooks.setSite(old_site)
Wenn der ObjectAddedEvent ausgelöst wird, ist das Objekt schon in seinem Container eingebettet und hat selbst Kontext. Nur in der thread-globalen Variablen steht noch der alte Wert für die Site, so dass neu installierte Utilities nicht gefunden werden.
- Category(s)
- Zope 3
Grundlagen von Webdesign
Überblick
Das Medium Web unterscheidet sich vom Medium Druck. Dies erfordert entsprechend angepasste Arbeitsweisen bei der Gestaltung von Webseiten. Die folgenden Artikel geben einen Überblick über die Besonderheiten und weisen insbesondere auf Unterschiede zwischen Web und Druck hin:- Artikel über Webdesign (Wikipedia)
- Artikel über Webtypographie (Wikipedia)
- Planung und Ablauf von Webprojekten (selfhtml.org)
- Designtheorie (webdesign-referenz.org)
Medium Internet
Das Medium Internet fordert den Designer, sich mit weitergehenden Themen zu beschäftigen, die im Druck nicht oder in anderer Form relevant sind:- Barrierefreiheit
- Benutzerfreundlichkeit
Technische Detailinformationen
Umfassende Publikationen und Archive
Da es bereits umfassende Webseiten gibt, die Informationen für gute Gestaltung im Web zusammengetragen haben und pflegen, haben wir hier einige verlinkt:- webdesign-referenz.de
- A List Apart (englisch)
- Eric Meyer on CSS
- Current Style in Webdesign
- Web Design from Scratch
Gallerien
Diese Seiten sammeln Beispiele guter Gestaltung, die sich mit guter technischer Implementierung verträgt:- Category(s)
- Webdesign
Zope3-Formulare mit zope.formlib erzeugen
Benutzung von zope.formlib
Ab Zope 3.2 ist der empfohlene Weg, um Formulare zu erzeugen nicht mehr zope.app.form, sondern zope.formlib. Da es zu zope.formlib noch recht wenig Literatur gibt (als die zwei Zope3-Bücher geschrieben wurden, gab es sie noch nicht!) hier ein wenig Dokumentation.
Ausgangssituation (Beispiel)
Als Beispiel soll eine rudimentäre Adressverwaltung dienen. Die Interfaces sind dafür folgendermaßen definiert.
adressen/interfaces.py
from zope.interface import Interface
from zope.app.container.interfaces import IContainer
from zope.app.container.constraints import containers, contains
from zope.schema import TextLine, Text
class IAdresse(Interface):
u"Adresse"
containers('.IAdressenContainer')
name = TextLine(title=u"Name")
anschrift = Text(title=u"Anschrift")
class IAdressenContainer(IContainer):
u"Container für Adressen"
contains(IAddresse)
Es werden also zwei Interfaces erstellt, eins für Adressen und eins für einen Container, der die Adressen beinhaltet.
containers legt fest, dass IAdresse nur in IAdressenContainer erlaubt ist. Da dieses Interface erst später in der Datei definiert wird, muss der Pfad zu dem Interface als String angegeben werden.
containers fügt im Schema des Interfaces ein Feld __parent__ hinzu, welches ein Constraint hat, das sicherstellt, dass die Bedingung erfüllt wird.
adressen/adressen.py
from zope.interface import implements
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
from persistent import Persistent
from adressen.interfaces import IAdressenContainer, IAdresse
class AdressenContainer(BTreeContainer):
u"Container für Adressen."
implements(IAdressenContainer)
class Adresse(Persistent, Contained):
u"Eine Adresse"
name = u""
anschrift = u""
AdressenContainer ist ein BTreeContainer, Adressen ist eine Klassen, deren Objekte persitent gespeichert werden.
adressen/configure.zcml
<configure xmlns="http://namespaces.zope.org/zope">
<content class=".adressen.AdressenContainer">
<factory />
<allow interface=".interfaces.IAdressenContainer" />
</content>
<content class=".person.WaitingListItem">
<factory />
<allow interface=".interfaces.IAdresse"
set_schema=".interfaces.IAdresse" />
</content>
<include package=".browser" />
</configure>
Die Konfiguration berücksichtigt keine Sicherheitsaspekte, es der Einfacheit des Beispiels halber wird alles erlaubt.
Das include-Statement verweist schon auf die im nächsten Abschnitt beschriebenen Browser-Komponenten.
Die Browser-Komponenten für den AdressenContainer spare ich hier ein, weil daran nichts zope.formlib-spezifisches ist.
Hinzufügen-Formular (AddForm) mit zope.formlib
Für das Hinzufügen-Formular ist nur eine Klasse folgenden Inhalts zu definieren und dann per ZCML einzubinden.
browser/add.py
from zope.formlib import form
from adressen.interfaces import IAdresse
class AddForm(form.AddForm):
form_fields = form.FormFields(IAdresse).omit('__parent__')
Das ist die gesamte View-Klasse. Auf dem Attribut form_fields wird eine Instanz von form.FormFields gespeichert, die alle anzuzeigenden Schemaelemente beinhaltet. Es werden also die Schema-Felder aus IAdresse ermittelt. Zu diesen gehört auch das durch das containers-Statement erzeugte __parent__-Feld. Für dieses Feld existiert aber kein Widget und außerdem soll es sowieso nicht angezeigt werden. Dazu dient der Aufruf von omit, welches von einer bestehenden FormFields.-Instanz eine neue ohne die angegebenen Felder zurückliefert.
browser/configure.zcml
<configure xmlns="http://namespaces.zope.org/browser">
<page
class=".add.AddForm"
name="AddAdresse.html"
permission="zope.Public"
for="zope.app.container.interfaces.IAdding"
/>
<addMenuItem
title="Adresse"
description="Ein Adresse."
class="adressen.adressen.Adresse"
permission="zope.Public"
view="AddAdresse.html"
/>
</configure>
Die page-Direktive deklariert eine Seite, für die View-Klasse add.AddForm mit dem Namen AddAdresse.html, die jeder aufrufen darf (zope.Public). Damit diese Seite für ein Hinzufügen-Formular benutzt werden kann, muss sie für das Interface zope.app.container.interfaces.IAdding definiert sein.
addMenuItem erstellt einen Eintrag im Hinzufügen-Menü für die Adresse.
Bearbeiten-Formular mit zope.formlib
Das Bearbeiten-Formular ist nicht aufwendiger als das Hinzufügen-Fomular: eine minimale View-Klasse und ein bisschen ZCML.
browser/edit.py
from zope.formlib import form
from adressen.interfaces import IAdresse
class EditView(form.EditForm):
form_fields = form.FormFields(IAdresse).omit('__parent__')
Auch für Bearbeiten-Formulare stellt zope.formlib alles zur Verfügung, es ist nur noch anzugeben, welche Schema-Felder erscheinen sollen. Auch hier soll __parent__ wiederum nicht angezeigt werden.
browser/configure.zcml
Die folgende Direktive ist in browser/configure.zcml (siehe oben im Abschnitt Hinzufügen-Menü) hinzuzufügen:
<page
name="edit.html"
menu="zmi_views"
title="Bearbeiten"
for="adressen.interfaces.IAdresse"
permission="zope.Public"
class=".edit.EditView"
/>
Es wird für Objekte, die adressen.interfaces.IAdresse implementieren eine Seite deklariert, die edit.html heißt, zu der ein Link in den Tabs (Reitern) erscheint und die für jeden zugreifbar ist.
Benutzung von form.FormFields
Wenn Zope3 mittels zope.formlib ein Fomular erstellt, wird auf der View-Klasse das Attribut form_fields ausgewertet. Als Wert dieses Attributs wird eine Instanz vom zope.formlib.form.FormFields erwartet. Anmerkung: Die Klasse Fields ist als ein Alias für FormFields definiert.
FormFields bietet verschiedene Möglichkeiten, die Schema-Felder eines Interfaces zu filtern. Hier folgt nur eine Auswahl.
Parameter im Konstruktor von FormFields
| Parameter | Bedeutung |
|---|---|
| *args | Es kann eine Liste von Interfaces angegeben werden, deren Schema-Felder in der angegebenen Reihenfolge im Formular erscheinen. Achtung: Es werden immer alle Schema-Felder angezeigt,. Wenn der Benutzer keinen Zugriff auf ein Feld hat, weil er zu wenige Rechte hat, kann es zu einem Fehler beim Erzeugen des Formulars kommen. Wenn der Benutzer keinen Schreibzugriff hat oder das Feld als readonly markiert ist, kommt es zu einem beim Speichern einer Änderung in dem entsprechenden Feld. |
| omit_readonly=True | Schema-Felder, die auf readonly (nur lesen) gesetzt sind,. werden im Formular nicht mit angezeigt. |
Nachfiltern der FormFields
Nachdem eine Instanz von FormFields erzeugt wurde kann man diese Nachfiltern, um unerwünschte Felder nicht anzuzeigen.
| Methode | Bedeutung |
|---|---|
| omit(*args) | Liste von Feldnamen (Strings), die in der zurückgegebenen neuen FormFields-Instanz nicht enthalten sein sollen. |
| select(*args) | Liste von Feldnamen (Strings), die in der zurückgegebenen neuen FormFields-Instanz enthalten sein sollen. |
- Category(s)
- Zope 3
Zope3: PAU programmatisch anlegen
Programmatisches anlegen eines Pluggable Authentication Utilities (Austauschbares Authentifizierungs-Utility) in Zope3.
In Zope3 kann man ohne weiteres über das Web-Interface ein Pluggable Authentication Utility, kurz PAU bzw. in der holprig klingenden deutschen Übersetzung "Austauschbares Authentifizierungs-Utility", hinzufügen. Will man das ganze programmatisch machen ist das auch nicht viel schwerer.
Voraussetzung für die hier folgende Anleitung ist folgende Anleitung: Programmatisch einen Zope3-Ordner in eine Site umwandeln.
Die im Folgenden beschriebene Methode createAuthenticationUtils wird innerhalb des try-finally-Blocks aufgerufen und erhält als Parameter event.object.
from zope.app.security.interfaces import IAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.appsetup.bootstrap import ensureUtility
from zope.app.authentication.authentication import \
PluggableAuthentication
from zope.app.authentication.principalfolder import PrincipalFolder
from zope.app.authentication.groupfolder import \
GroupFolder, GroupInformation
from zope.app.component import site
from zope.app import zapi
from myproject import config
def addUtilityToPAU(pau, interface, utility_factory, id, name):
u"""Fügt ein Utility zu einem existierenden PAU hinzu.
pau ... PAU, zu dem das Utility hinzugefügt werden soll
interface ... Interface der neuen Komponente
utility_factory ... Klasse der neuen Komponente
id ... Id der Komponente im PAU.
name ... Name unter dem die Komponente (die ja ein Utility ist,
gefunden werden soll.)
"""
utility = utility_factory()
pau[id] = utility
reg_man = pau.registrationManager
reg = site.UtilityRegistration(name, interface, utility)
reg_man.addRegistration(reg)
reg.status = u'Active'
def _addGroupInformation(group_folder, id, title, description):
u"""Fügt einem Gruppenordner eine Gruppe hinzu.
group_folder ... Objekt des Gruppenordners
id ... Id der Gruppe
title ... title für die Gruppe
description ... Beschreibung für die Gruppe.
"""
group = GroupInformation()
group.title = title
group.description = description
group_folder[id] = group
def createAuthenticationUtils(obj):
u"""Erstellung des PAU und der darin liegenden Objekte.
obj ... Objekt, welches ISite implementiert, in dem PAU
installiert werden soll
"""
# PAU erstellen
ensureUtility(obj, IAuthentication, '', PluggableAuthentication,
copy_to_zlog=False)
auth = zapi.getUtility(IAuthentication)
# Anmeldung geschieht über Sitzungsdaten, weitere Anmeldearten sind
# in Zope3 schon implementiert, dazu in zope/app/authentication in den
# *.zcml Dateien nach 'provides=".interfaces.ICredentialsPlugin"' suchen
# der Eintrag unter name= des zugehörigen Utilities ist auch als
# Anmeldeart möglich.
auth.credentialsPlugins = (u'Session Credentials',)
# Hinzufügen eines Ordners, der die Daten der Nutzungsberechtigenen
# (Principals) enthalten wird (deutsch: "Ordner über
# Nutzungsberechtigten")
addUtilityToPAU(auth, IAuthenticatorPlugin, PrincipalFolder,
'principal_folder', u'Benutzerverwaltung')
# Hinzufügen eines Gruppenordners, der die Zuordnung der
# Nutzungsberechtigten zu Gruppen speichert
addUtilityToPAU(auth, IAuthenticatorPlugin, GroupFolder,
'group_folder', u'Gruppenverwaltung')
# Festlegen, welche der angelegten Ordner für die Authentifizierung
# verwendet werden
auth.authenticatorPlugins = (u'Benutzerverwaltung',
u'Gruppenverwaltung',)
# Anlegen der Gruppen
group_folder = auth['group_folder']
for group in config.groups:
_addGroupInformation(group_folder, group['id'],
group['title'], group['description'])
In myproject/config.py stehen die anzulegenden Gruppen:
groups = [{'id': 'management',
'title': 'Management',
'description': 'Management der Firma XY'},
]
- Category(s)
- Zope 3