Standardwerte, Vokabularien und Autovervollständigung

Standardwerte

Oft vereinfacht es die Bedienung deutlich, wenn in Feldern Standardwerte eingetragen werden. Diese Werte werden im Hinzufügen-Formular gesetzt.

In unserem Beispiel sollen die Standardwerte für den Beginn und das Ende einer Veranstaltung eine Woche in der Zukunft liegen. Hierzu fügen wir in registration.py folgendes hinzu:

import datetime
…
@form.default_value(field=IRegistration['start'])
def startDefaultValue(data):
    # To get hold of the folder, do: context = data.context
    return datetime.datetime.today() + datetime.timedelta(7)

@form.default_value(field=IRegistration['end'])
def endDefaultValue(data):
    # To get hold of the folder, do: context = data.context
    return datetime.datetime.today() + datetime.timedelta(10)

Der Decorator kann ein oder mehrere Unterscheidungskriterien haben. Folgende Unterscheidungskriterien sind möglich:

context
Der Kontexttyp, z.B. ein Interface
request
Der Request-Tp, z.B. ein Layer Marker Interface
view
Der Formulartyp, z.B. eine Formularinstanz oder ein Interface.
field
Die Feld-Instanz oder das Interface eines Feldes

Neben dem default_value-Decorator gibt es noch zwei weitere Decorators:

widget
Der Widget-Typ, z.B. ein Interface
widget_label
bietet ein dynamische Label für Widgets wobei es dieselben Unterscheidungskriterien zulässt wie default_value.
button_label
bietet dynamische Label für Tasten mit den Unterscheidungskriterien content, ``request, form, manager und button.

In der Dokumentation zu plone.directives.form finden Sie weitere Informationen hierzu.

Vokabularien

Vokabularien werden üblicherweise zusammen mit Auswahlfeldern verwendet. Um nur eine Auswahl zuzulassen, kann das Choise-Feld direkt verwendet werden:

class IMySchema(form.Schema):
    myChoice = schema.Choice(...)

Für Multiple-Choice-Felder können List, Tuple, Set oder Frozenset mit value_type=schema.Choice verwendet werden, also z.B.:

class IMySchema(form.Schema):
    myList = schema.List(
        title=u"My list",
        value_type=schema.Choice(values=['red', 'green', 'blue', 'yellow']))

Ein Choice-Feld kann eines der folgenden Argumente erhalten:

  • Werte aus einer Liste statischer Werte
  • Werte aus einer Quelle, die mit IContextSourceBinder oder einer ISource-Intanz angegeben werden
  • Werte können aus einem Vokabular stammen, das als ÌVocabulary-Instanz oder als Name eines IVocabularyFactory-Utility angegeben wird
term
Eintrag in ein Vokabular
token
ASCII-Zeichenkette, die beim Abschicken eines Formulars übermittelt wird um den Term eindeutig zu identifizieren.
value
Der aktuelle Wert, der in einem Objekt gespeichert wird
title
Übersetzbare Unicode-Zeichenkette

Verfügbare Vokabularien

In Plone werden Ihnen bereits eine ganze Reihe von Vokabularien in plone.app.vocabularies zur Verfügung gestellt. Die gebräuchlichsten sind:

plone.app.vocabularies.AvailableContentLanguages
Eine Liste aller verfügbaren Sprachen
plone.app.vocabularies.SupportedContentLanguages
Eine Liste aller aktuell unterstützten Sprachen
plone.app.vocabularies.Roles
Die in der Site verfügbaren Rollen
plone.app.vocabularies.PortalTypes
Eine Liste der im Portal Types Tool registrierten Artikeltypen
plone.app.vocabularies.ReallyUserFriendlyTypes
Eine Liste derjenigen Artikeltypen, die für Nutzer von Bedeutung sind
plone.app.vocabularies.Workflows
Eine Liste aller Arbeitsabläufe
plone.app.vocabularies.WorkflowStates
Eine Liste aller Arbeitsablaufstadien
plone.app.vocabularies.WorkflowTransitions
Eine Liste aller Übergänge zwischen Arbeitsablaufstadien

Mit plone.principalsource steht uns ein weiteres Paket mit verschiedenen Vokabularien bereit, das zur Auswahl von Nutzern und Gruppen hilfreich ist:

plone.principalsource.Users
Eine Liste aller Nutzer
plone.principalsource.Groups
Eine Liste aller Gruppen
plone.principalsource.Principals
Eine Liste aller Berechtigungen für Nutzer und Gruppen

Statische Vokabularien

Hier ein Beispiel für ein statisches Vokabular:

from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm

organisers = SimpleVocabulary(
    [SimpleTerm(value=u'vsc', title=_(u'Veit Schiele Communications')),
     SimpleTerm(value=u'zopyx', title=_(u'Zopyx Limited'))]
    )

organiser = schema.Choice(
            title=_(u"Organiser"),
            vocabulary=organisers,
            required=False,
        )

Dynamische Vokabularien

Statische Vokabularien sind in zweierlei Hinsicht beschränkt: zum einen sind sie hartkodiert in Python, zum anderen werden die gespeicherten Werte nicht getrennt von den Labels gespeichert.

Ein dynamische Vokabular kann nun erzeugt werden indem ein sog. Context Source Binder verwendet wird. Ein solcher kann einfach aufgerufen werden durch eine Funktion oder ein Objekt mit einer __call__-Methode, die das IContextSourceBinder-Interface zusammen mit einem Kontext-Argument bereitstellt. Der Aufruf soll ein Vokabular ausgeben, das am einfachsten zu bekommen ist, wenn die SimpleVocabulary-Klasse aus zope.schema verwendet wird.

Im folgenden nun ein Beispiel für eine Funktion, die alle Nutzer einer bestimmten Gruppe zurückgibt:

from zope.schema.interfaces import IContextSourceBinder
from zope.schema.vocabulary import SimpleVocabulary
from Products.CMFCore.utils import getToolByName

@grok.provider(IContextSourceBinder)
def possibleOrganisers(context):
    acl_users = getToolByName(context, 'acl_users')
    group = acl_users.getGroupById('organisers')
    terms = []

    if group is not None:
        for member_id in group.getMemberIds():
            user = acl_users.getUserById(member_id)
            if user is not None:
                member_name = user.getProperty('fullname') or member_id
                terms.append(SimpleVocabulary.createTerm(member_id, str(member_id), member_name))

   return SimpleVocabulary(terms)

Parametriesierte Vokabularien

Das obige Beispiel kann erweitert werden indem der Gruppenname aus der Funktion herausgenommen wird und sich dann für jedes Feld unabhängig setzen lässt. Hierfür wird dann IContextSourceBinder in eine eigene Klasse ausgelagert, die mit dem Gruppennamen initialisiert wird:

class GroupMembers(object):
    """Context source binder to provide a vocabulary of users in a given
    group.
    """

    grok.implements(IContextSourceBinder)

    def __init__(self, group_name):
        self.group_name = group_name

    def __call__(self, context):
        acl_users = getToolByName(context, 'acl_users')
        group = acl_users.getGroupById(self.group_name)
        terms = []

        if group is not None:
            for member_id in group.getMemberIds():
                user = acl_users.getUserById(member_id)
                if user is not None:
                    member_name = user.getProperty('fullname') or member_id
                    terms.append(SimpleVocabulary.createTerm(member_id, str(member_id), member_name))

        return SimpleVocabulary(terms)

Benannte Vokabularien

Sollen Vokabularien nicht nur im Kontext verfügbar sein sondern als Komponenten, werden sog. Named Vocabularies erstellt. Diese werden als named utilities registriert werden und sind anschließend in einem Schema mit ihrem Namen referenziert werden. Damit lassen sich Vokabularien in eigenständigen Paketen erstellen.

VDEX-Vokabularien

collective.vdexvocabulary erlaubt die Verwendung von IMS VDEX-Vokabularien und bietet darüberhinaus noch weitere Vorteile wie:

  • i18n-Unterstützung, wie sie im IMS VDEX-Standard definiert ist.
  • Unterstützung für Sortierung auch von Unicode-Zeichen. sofern zope.ucol installiert ist
  • Einfache Registrierung mit zcml
  • Relationen wie sie im IMS VDEX-Standard spezifiziert sind

collective.elephantvocabulary

collective.elephantvocabulary ist ein Wrapper für zope.schema-Vokabularien wodurch diese keinen ihre Einträge mehr vergessen.

Autovervollständigung

plone.formwidget.autocomplete erweitert z3c.formwidget.query um ein nutzerfreundlicheres Interface für Felder bereitzustellen, bei dem nach der Eingabe von wenigen Zeichen bereits die möglichen Werte angezeigt werden.

Das Widget wird bereits mit plone.app.dexterity mitgeliefert, sodass wir es einfach z.B. in registration.py verwenden können, mit:

form.widget(organiser=AutocompleteFieldWidget)
organiser = schema.Choice(
        title=_(u"Organiser"),
        vocabulary=u"plone.principalsource.Users",
        required=False,
    )