[Checkins] SVN: z3ext.preferences/trunk/s Move doctest to tests folder.
Dan Korostelev
nadako at gmail.com
Wed Oct 28 10:17:44 EDT 2009
Log message for revision 105330:
Move doctest to tests folder.
Changed:
U z3ext.preferences/trunk/setup.py
D z3ext.preferences/trunk/src/z3ext/preferences/README.txt
A z3ext.preferences/trunk/src/z3ext/preferences/tests/README.txt
U z3ext.preferences/trunk/src/z3ext/preferences/tests/tests.py
-=-
Modified: z3ext.preferences/trunk/setup.py
===================================================================
--- z3ext.preferences/trunk/setup.py 2009-10-28 14:15:06 UTC (rev 105329)
+++ z3ext.preferences/trunk/setup.py 2009-10-28 14:17:43 UTC (rev 105330)
@@ -33,7 +33,7 @@
'Detailed Documentation\n' +
'======================\n'
+ '\n\n' +
- read('src', 'z3ext', 'preferences', 'README.txt')
+ read('src', 'z3ext', 'preferences', 'tests', 'README.txt')
+ '\n\n' +
read('CHANGES.txt')
),
Deleted: z3ext.preferences/trunk/src/z3ext/preferences/README.txt
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/README.txt 2009-10-28 14:15:06 UTC (rev 105329)
+++ z3ext.preferences/trunk/src/z3ext/preferences/README.txt 2009-10-28 14:17:43 UTC (rev 105330)
@@ -1,495 +0,0 @@
-================
-User Preferences
-================
-
-Implementing user preferences is usually a painful task, since it requires a
-lot of custom coding and constantly changing preferences makes it hard to
-maintain the data and UI. The `preference` package
-
- >>> from z3ext.preferences import interfaces, preference, preferencetype
-
-eases this pain by providing a generic user preferences framework that uses
-schemas to categorize and describe the preferences.
-
-We also have to do some additional setup beforehand:
-
- >>> from zope.app.testing import setup
-
- >>> import zope.app.component.hooks
- >>> zope.app.component.hooks.setHooks()
- >>> setup.setUpTraversal()
- >>> setup.setUpSiteManagerLookup()
-
-
-Preference Groups
-------------------
-
-Preferences are grouped in preference groups and the preferences inside a
-group are specified via the preferences group schema:
-
- >>> import zope.schema
- >>> import zope.interface
-
- >>> class IZMIUserSettings(zope.interface.Interface):
- ... """Basic User Preferences"""
- ...
- ... email = zope.schema.TextLine(
- ... title=u"E-mail Address",
- ... description=u"E-mail Address used to send notifications")
- ...
- ... skin = zope.schema.Choice(
- ... title=u"Skin",
- ... description=u"The skin that should be used for the ZMI.",
- ... values=['Rotterdam', 'ZopeTop', 'Basic'],
- ... default='Rotterdam')
- ...
- ... showZopeLogo = zope.schema.Bool(
- ... title=u"Show Zope Logo",
- ... description=u"Specifies whether Zope logo should be displayed "
- ... u"at the top of the screen.",
- ... default=True)
-
-Each preference group must have an
-ID by which it can be accessed and optional title and description fields for UI
-purposes. Before create preference group we should create unique class for
-our preferences:
-
- >>> settingsClass = preferencetype.PreferenceType(
- ... "ZMISettings",
- ... IZMIUserSettings,
- ... title=u"ZMI User Settings", description=u"")
-
-We should set 'preferenceID' to schema with preference group id,
-This value is used by data storage to store data in annotations
-
- >>> IZMIUserSettings.setTaggedValue('preferenceID', 'ZMISettings')
-
-Now we can instantiate the preference group.
-
- >>> settings = settingsClass()
-
-We can't change schema for preference group:
-
- >>> settings.__schema__ = IZMIUserSettings
- Traceback (most recent call last):
- ...
- AttributeError: Can't change __schema__
-
-
-Note that the preferences group provides the interface it is representing:
-
- >>> IZMIUserSettings.providedBy(settings)
- True
-
-and the id, schema and title of the group are directly available:
-
- >>> settings.__id__
- u'ZMISettings'
- >>> settings.__schema__
- <InterfaceClass z3ext.preferences.README.IZMIUserSettings>
- >>> settings.__title__
- u'ZMI User Settings'
-
-So let's ask the preference group for the `skin` setting:
-
- >>> settings.skin
- Traceback (most recent call last):
- ...
- UnboundPreferenceGroup
-
-
-So why did the lookup fail? Because we have not specified a principal yet, for
-which we want to lookup the preferences. To do that, we have to create a principal:
-
- >>> class Principal:
- ... def __init__(self, id):
- ... self.id = id
- >>> principal = Principal('zope.user')
-
- >>> class Participation:
- ... interaction = None
- ... def __init__(self, principal):
- ... self.principal = principal
-
- >>> participation = Participation(principal)
-
- >>> import zope.security.management
- >>> zope.security.management.newInteraction(participation)
-
-We also need an IAnnotations adapter for principals, so we can store the
-settings:
-
- >>> from zope.annotation.interfaces import IAnnotations
- >>> class PrincipalAnnotations(dict):
- ... zope.interface.implements(IAnnotations)
- ... data = {}
- ... def __new__(class_, principal):
- ... try:
- ... annotations = class_.data[principal.id]
- ... except KeyError:
- ... annotations = dict.__new__(class_)
- ... class_.data[principal.id] = annotations
- ... return annotations
- ... def __init__(self, principal):
- ... pass
-
- >>> from zope import component
- >>> component.provideAdapter(PrincipalAnnotations, (Principal,), IAnnotations)
-
-Also we need IDataStorage for preferenceGroup schema, because preference group
-doesn't use IAnnotations directly.
-
- >>> from z3ext.preferences import storage
- >>> component.provideAdapter(
- ... storage.getDefaultStorage, (Principal, zope.interface.Interface))
-
-And now we need bind preferences to principal. We can just call __bind__
-method in this case preference will use principal from current interaction.
-
- >>> nsettings = settings.__bind__()
- >>> interfaces.IBound.providedBy(nsettings)
- True
-
- >>> nsettings.__principal__ == principal
- True
-
-Or we can explicitly set principal, this is usefull when we want to know
-preferences for principal.
-
- >>> settings = settings.__bind__(principal)
- >>> interfaces.IBound.providedBy(nsettings)
- True
-
-Let's now try to access the settings again:
-
- >>> settings.skin
- 'Rotterdam'
-
-which is the default value, since we have not set it yet. We can now reassign
-the value:
-
- >>> settings.skin = 'Basic'
- >>> settings.skin
- 'Basic'
-
-However, you cannot just enter any value, since it is validated before the
-assignment:
-
- >>> settings.skin = 'MySkin'
- Traceback (most recent call last):
- ...
- ConstraintNotSatisfied: MySkin
-
-
-Preference Group Trees
-----------------------
-
-The preferences would not be very powerful, if you could create a full
-preferences. So let's create a sub-group for our ZMI user settings, where we
-can adjust the look and feel of the folder contents view:
-
- >>> class IFolderSettings(zope.interface.Interface):
- ... """Basic User Preferences"""
- ...
- ... shownFields = zope.schema.Tuple(
- ... title=u"Shown Fields",
- ... description=u"Fields shown in the table.",
- ... value_type=zope.schema.Choice(['name', 'size', 'creator']),
- ... default=('name', 'size'))
- ...
- ... sortedBy = zope.schema.Choice(
- ... title=u"Sorted By",
- ... description=u"Data field to sort by.",
- ... values=['name', 'size', 'creator'],
- ... default='name')
-
- >>> folderSettingsClass = preferencetype.PreferenceType(
- ... "ZMISettings.Folder",
- ... IFolderSettings,
- ... title=u"Folder Content View Settings")
-
- >>> IFolderSettings.setTaggedValue('preferenceID', 'ZMISettings.Folder')
-
- >>> folderSettings = folderSettingsClass()
-
-Note that the id was chosen so that the parent id is the prefix of the child's
-id. Our new preference sub-group should now be available as an attribute or an
-item on the parent group ...
-
- >>> settings['Folder']
- Traceback (most recent call last):
- ...
- KeyError: 'Folder'
-
-but not before we register the groups as utilities:
-
- >>> from zope import component
- >>> siteManager = component.getSiteManager()
-
- >>> siteManager.registerUtility(
- ... settings, interfaces.IPreferenceGroup, 'ZMISettings')
- >>> siteManager.registerUtility(
- ... folderSettings, interfaces.IPreferenceGroup, 'ZMISettings.Folder')
-
-If we now try to lookup the sub-group again, we should be successful:
-
- >>> settings['Folder']
- <z3ext.preferences.preferencetype.Preference<ZMISettings.Folder> ...>
-
-In z3ext.preferences we can't access to subfolder as attribute, this
-is one of difference from zope.app.preference.
-
- >>> settings['Folder'].sortedBy = 'size'
- >>> settings['Folder'].sortedBy
- 'size'
-
-While the registry of the preference groups is flat, the careful naming of the
-ids allows us to have a tree of preferences. Note that this pattern is very
-similar to the way modules are handled in Python; they are stored in a flat
-dictionary in ``sys.modules``, but due to the naming they appear to be in a
-namespace tree.
-
-While we are at it, there are also preference categories that can be compared
-to Python packages. They basically are just a higher level grouping concept
-that is used by the UI to better organize the preferences. A preference group
-can be converted to a category by simply providing an additional interface:
-
- >>> zope.interface.alsoProvides(settings, interfaces.IPreferenceCategory)
-
- >>> interfaces.IPreferenceCategory.providedBy(settings)
- True
-
-Clear:
-
- >>> t = siteManager.unregisterUtility(
- ... settings, interfaces.IPreferenceGroup, 'ZMISettings')
- >>> t = siteManager.unregisterUtility(
- ... folderSettings, interfaces.IPreferenceGroup, 'ZMISettings.Folder')
-
-
-Creating Preference Groups Using ZCML
--------------------------------------
-
-If you are using the user preference system in Zope 3, you will not have to
-manually setup the preference groups as we did above (of course). We will use
-ZCML instead. First, we need to register the directives:
-
- >>> from zope.configuration import xmlconfig
- >>> import z3ext.preferences
- >>> context = xmlconfig.file('meta.zcml', z3ext.preferences)
-
-Second we need root preference group:
-
- >>> from z3ext.preferences.root import PersonalPreferences
-
- >>> siteManager.registerUtility(
- ... PersonalPreferences(), interfaces.IPreferenceGroup)
-
-Then the system sets up a root preference group:
-
- >>> context = xmlconfig.string('''
- ... <configure
- ... xmlns:z3ext="http://namespaces.zope.org/z3ext" i18n_domain="test">
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings"
- ... schema="z3ext.preferences.README.IZMIUserSettings"
- ... title="ZMI User Settings"
- ... permission="zope.Public" />
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings.Folder"
- ... schema="z3ext.preferences.README.IFolderSettings"
- ... title="Folder Content View Settings"
- ... permission="zope.Public" />
- ...
- ... </configure>''', context)
-
-Now we can use the preference system in its intended way. We access the folder
-settings as follows:
-
- >>> prefs = component.getUtility(interfaces.IPreferenceGroup)
- >>> prefs.isAvailable()
- False
-
- >>> prefs['ZMISettings']['Folder'].isAvailable()
- False
-
-
-Don't forget to bind preferences to principal
-
- >>> prefs = prefs.__bind__(principal)
- >>> prefs.isAvailable()
- True
-
- >>> p = prefs['ZMISettings']['Folder'].__bind__(parent=prefs)
-
- >>> prefs['ZMISettings']['Folder'].isAvailable()
- True
-
- >>> prefs['ZMISettings']['Folder'].sortedBy
- 'size'
-
- >>> prefs.items()
- [(u'ZMISettings', <z3ext.preferences.preferencetype.Preference<ZMISettings> ...>)]
-
- >>> u'ZMISettings' in prefs
- True
-
- >>> prefs.keys()
- (u'ZMISettings',)
-
- >>> prefs.values()
- [<z3ext.preferences.preferencetype.Preference<ZMISettings> ...>]
-
- >>> list(iter(prefs))
- [<z3ext.preferences.preferencetype.Preference<ZMISettings> ...>]
-
- >>> len(prefs)
- 1
-
-
-Let's register the ZMI settings again under a new name via ZCML:
-
- >>> class IZMIUserSettings2(IZMIUserSettings):
- ... pass
-
- >>> IZMIUserSettings2.setTaggedValue('preferenceID', 'ZMISettings.Folder')
-
- >>> context = xmlconfig.string('''
- ... <configure
- ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
- ... i18n_domain="test">
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings2"
- ... title="ZMI Settings NG"
- ... schema="z3ext.preferences.README.IZMIUserSettings"
- ... permission="zope.Public"
- ... provides="z3ext.preferences.interfaces.IPreferenceCategory" />
- ...
- ... </configure>''', context)
-
- >>> prefs['ZMISettings2']
- <z3ext.preferences.preferencetype.Preference<ZMISettings2> ...>
-
- >>> prefs['ZMISettings2'].__title__
- u'ZMI Settings NG'
-
- >>> IZMIUserSettings.providedBy(prefs['ZMISettings2'])
- True
- >>> interfaces.IPreferenceCategory.providedBy(prefs['ZMISettings2'])
- True
-
-And the tree can built again by carefully constructing the id:
-
- >>> context = xmlconfig.string('''
- ... <configure
- ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
- ... i18n_domain="test">
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings2.Folder"
- ... title="Folder Settings"
- ... schema="z3ext.preferences.README.IFolderSettings" />
- ...
- ... </configure>''', context)
-
- >>> prefs['ZMISettings2']
- <z3ext.preferences.preferencetype.Preference<ZMISettings2> ...>
-
- >>> prefs['ZMISettings2'].items()
- [(u'ZMISettings2.Folder', <z3ext.preferences.preferencetype.Preference<ZMISettings2.Folder> ...)]
-
- >>> list(iter(prefs['ZMISettings2']))
- [<z3ext.preferences.preferencetype.Preference<ZMISettings2.Folder> ...>]
-
- >>> prefs['ZMISettings2']['Folder'].__title__
- u'Folder Settings'
-
- >>> IFolderSettings.providedBy(prefs['ZMISettings2']['Folder'])
- True
- >>> interfaces.IPreferenceCategory.providedBy(prefs['ZMISettings2']['Folder'])
- False
-
-We can define preference group for principal type
-
- >>> class IMyPrincipal(zope.interface.Interface):
- ... pass
-
-Now let's register preference for for this type of principal
-
- >>> context = xmlconfig.string('''
- ... <configure
- ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
- ... i18n_domain="test">
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings2.Folder10"
- ... for="z3ext.preferences.README.IMyPrincipal"
- ... title="Folder Settings"
- ... permission="zope.Public"
- ... schema="z3ext.preferences.README.IFolderSettings" />
- ...
- ... </configure>''', context)
-
- >>> p = component.getUtility(interfaces.IPreferenceGroup, 'ZMISettings2.Folder10')
- >>> new_prefs = p.__bind__()
- >>> new_prefs.isAvailable()
- False
-
-Now let's mark our principal
-
- >>> zope.interface.alsoProvides(principal, IMyPrincipal)
- >>> new_prefs = p.__bind__()
- >>> new_prefs.isAvailable()
- True
-
- >>> prefs['ZMISettings2'].remove('Folder10')
-
-
-Simple Python-Level Access
---------------------------
-
-If a site is set, getting the user preferences is very simple:
-
- >>> prefs2 = IFolderSettings(principal)
-
- >>> prefs2.sortedBy
- 'name'
-
-
-Security
---------
-
-You might already wonder under which permissions the preferences are
-available. They are actually available with z3ext.ManagePreference
-permission. But sometimes we need preferences which can be changed
-only by manager. In this case we can provide default permission or
-even set security checks on attribute level, like in <class /> directive.
-
- >>> import zope.security
- >>> context = xmlconfig.file('meta.zcml', zope.security, context)
-
- >>> context = xmlconfig.string('''
- ... <configure
- ... xmlns="http://namespaces.zope.org/zope"
- ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
- ... i18n_domain="z3ext">
- ...
- ... <permission id="zope.View" title="zope view" />
- ... <permission id="zope.Manage" title="zope manage" />
- ...
- ... <z3ext:preferenceGroup
- ... id="ZMISettings3"
- ... title="ZMI Settings 3"
- ... schema="z3ext.preferences.README.IZMIUserSettings"
- ... provides="z3ext.preferences.interfaces.IPreferenceCategory"
- ... permission="zope.View">
- ... <allow attributes="email" />
- ... <require
- ... attributes="showZopeLogo" permission="zope.Manage" set_attributes="skin" />
- ... </z3ext:preferenceGroup>
- ...
- ... </configure>''', context)
-
Copied: z3ext.preferences/trunk/src/z3ext/preferences/tests/README.txt (from rev 105328, z3ext.preferences/trunk/src/z3ext/preferences/README.txt)
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/tests/README.txt (rev 0)
+++ z3ext.preferences/trunk/src/z3ext/preferences/tests/README.txt 2009-10-28 14:17:43 UTC (rev 105330)
@@ -0,0 +1,495 @@
+================
+User Preferences
+================
+
+Implementing user preferences is usually a painful task, since it requires a
+lot of custom coding and constantly changing preferences makes it hard to
+maintain the data and UI. The `preference` package
+
+ >>> from z3ext.preferences import interfaces, preference, preferencetype
+
+eases this pain by providing a generic user preferences framework that uses
+schemas to categorize and describe the preferences.
+
+We also have to do some additional setup beforehand:
+
+ >>> from zope.app.testing import setup
+
+ >>> import zope.app.component.hooks
+ >>> zope.app.component.hooks.setHooks()
+ >>> setup.setUpTraversal()
+ >>> setup.setUpSiteManagerLookup()
+
+
+Preference Groups
+------------------
+
+Preferences are grouped in preference groups and the preferences inside a
+group are specified via the preferences group schema:
+
+ >>> import zope.schema
+ >>> import zope.interface
+
+ >>> class IZMIUserSettings(zope.interface.Interface):
+ ... """Basic User Preferences"""
+ ...
+ ... email = zope.schema.TextLine(
+ ... title=u"E-mail Address",
+ ... description=u"E-mail Address used to send notifications")
+ ...
+ ... skin = zope.schema.Choice(
+ ... title=u"Skin",
+ ... description=u"The skin that should be used for the ZMI.",
+ ... values=['Rotterdam', 'ZopeTop', 'Basic'],
+ ... default='Rotterdam')
+ ...
+ ... showZopeLogo = zope.schema.Bool(
+ ... title=u"Show Zope Logo",
+ ... description=u"Specifies whether Zope logo should be displayed "
+ ... u"at the top of the screen.",
+ ... default=True)
+
+Each preference group must have an
+ID by which it can be accessed and optional title and description fields for UI
+purposes. Before create preference group we should create unique class for
+our preferences:
+
+ >>> settingsClass = preferencetype.PreferenceType(
+ ... "ZMISettings",
+ ... IZMIUserSettings,
+ ... title=u"ZMI User Settings", description=u"")
+
+We should set 'preferenceID' to schema with preference group id,
+This value is used by data storage to store data in annotations
+
+ >>> IZMIUserSettings.setTaggedValue('preferenceID', 'ZMISettings')
+
+Now we can instantiate the preference group.
+
+ >>> settings = settingsClass()
+
+We can't change schema for preference group:
+
+ >>> settings.__schema__ = IZMIUserSettings
+ Traceback (most recent call last):
+ ...
+ AttributeError: Can't change __schema__
+
+
+Note that the preferences group provides the interface it is representing:
+
+ >>> IZMIUserSettings.providedBy(settings)
+ True
+
+and the id, schema and title of the group are directly available:
+
+ >>> settings.__id__
+ u'ZMISettings'
+ >>> settings.__schema__
+ <InterfaceClass z3ext.preferences.README.IZMIUserSettings>
+ >>> settings.__title__
+ u'ZMI User Settings'
+
+So let's ask the preference group for the `skin` setting:
+
+ >>> settings.skin
+ Traceback (most recent call last):
+ ...
+ UnboundPreferenceGroup
+
+
+So why did the lookup fail? Because we have not specified a principal yet, for
+which we want to lookup the preferences. To do that, we have to create a principal:
+
+ >>> class Principal:
+ ... def __init__(self, id):
+ ... self.id = id
+ >>> principal = Principal('zope.user')
+
+ >>> class Participation:
+ ... interaction = None
+ ... def __init__(self, principal):
+ ... self.principal = principal
+
+ >>> participation = Participation(principal)
+
+ >>> import zope.security.management
+ >>> zope.security.management.newInteraction(participation)
+
+We also need an IAnnotations adapter for principals, so we can store the
+settings:
+
+ >>> from zope.annotation.interfaces import IAnnotations
+ >>> class PrincipalAnnotations(dict):
+ ... zope.interface.implements(IAnnotations)
+ ... data = {}
+ ... def __new__(class_, principal):
+ ... try:
+ ... annotations = class_.data[principal.id]
+ ... except KeyError:
+ ... annotations = dict.__new__(class_)
+ ... class_.data[principal.id] = annotations
+ ... return annotations
+ ... def __init__(self, principal):
+ ... pass
+
+ >>> from zope import component
+ >>> component.provideAdapter(PrincipalAnnotations, (Principal,), IAnnotations)
+
+Also we need IDataStorage for preferenceGroup schema, because preference group
+doesn't use IAnnotations directly.
+
+ >>> from z3ext.preferences import storage
+ >>> component.provideAdapter(
+ ... storage.getDefaultStorage, (Principal, zope.interface.Interface))
+
+And now we need bind preferences to principal. We can just call __bind__
+method in this case preference will use principal from current interaction.
+
+ >>> nsettings = settings.__bind__()
+ >>> interfaces.IBound.providedBy(nsettings)
+ True
+
+ >>> nsettings.__principal__ == principal
+ True
+
+Or we can explicitly set principal, this is usefull when we want to know
+preferences for principal.
+
+ >>> settings = settings.__bind__(principal)
+ >>> interfaces.IBound.providedBy(nsettings)
+ True
+
+Let's now try to access the settings again:
+
+ >>> settings.skin
+ 'Rotterdam'
+
+which is the default value, since we have not set it yet. We can now reassign
+the value:
+
+ >>> settings.skin = 'Basic'
+ >>> settings.skin
+ 'Basic'
+
+However, you cannot just enter any value, since it is validated before the
+assignment:
+
+ >>> settings.skin = 'MySkin'
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: MySkin
+
+
+Preference Group Trees
+----------------------
+
+The preferences would not be very powerful, if you could create a full
+preferences. So let's create a sub-group for our ZMI user settings, where we
+can adjust the look and feel of the folder contents view:
+
+ >>> class IFolderSettings(zope.interface.Interface):
+ ... """Basic User Preferences"""
+ ...
+ ... shownFields = zope.schema.Tuple(
+ ... title=u"Shown Fields",
+ ... description=u"Fields shown in the table.",
+ ... value_type=zope.schema.Choice(['name', 'size', 'creator']),
+ ... default=('name', 'size'))
+ ...
+ ... sortedBy = zope.schema.Choice(
+ ... title=u"Sorted By",
+ ... description=u"Data field to sort by.",
+ ... values=['name', 'size', 'creator'],
+ ... default='name')
+
+ >>> folderSettingsClass = preferencetype.PreferenceType(
+ ... "ZMISettings.Folder",
+ ... IFolderSettings,
+ ... title=u"Folder Content View Settings")
+
+ >>> IFolderSettings.setTaggedValue('preferenceID', 'ZMISettings.Folder')
+
+ >>> folderSettings = folderSettingsClass()
+
+Note that the id was chosen so that the parent id is the prefix of the child's
+id. Our new preference sub-group should now be available as an attribute or an
+item on the parent group ...
+
+ >>> settings['Folder']
+ Traceback (most recent call last):
+ ...
+ KeyError: 'Folder'
+
+but not before we register the groups as utilities:
+
+ >>> from zope import component
+ >>> siteManager = component.getSiteManager()
+
+ >>> siteManager.registerUtility(
+ ... settings, interfaces.IPreferenceGroup, 'ZMISettings')
+ >>> siteManager.registerUtility(
+ ... folderSettings, interfaces.IPreferenceGroup, 'ZMISettings.Folder')
+
+If we now try to lookup the sub-group again, we should be successful:
+
+ >>> settings['Folder']
+ <z3ext.preferences.preferencetype.Preference<ZMISettings.Folder> ...>
+
+In z3ext.preferences we can't access to subfolder as attribute, this
+is one of difference from zope.app.preference.
+
+ >>> settings['Folder'].sortedBy = 'size'
+ >>> settings['Folder'].sortedBy
+ 'size'
+
+While the registry of the preference groups is flat, the careful naming of the
+ids allows us to have a tree of preferences. Note that this pattern is very
+similar to the way modules are handled in Python; they are stored in a flat
+dictionary in ``sys.modules``, but due to the naming they appear to be in a
+namespace tree.
+
+While we are at it, there are also preference categories that can be compared
+to Python packages. They basically are just a higher level grouping concept
+that is used by the UI to better organize the preferences. A preference group
+can be converted to a category by simply providing an additional interface:
+
+ >>> zope.interface.alsoProvides(settings, interfaces.IPreferenceCategory)
+
+ >>> interfaces.IPreferenceCategory.providedBy(settings)
+ True
+
+Clear:
+
+ >>> t = siteManager.unregisterUtility(
+ ... settings, interfaces.IPreferenceGroup, 'ZMISettings')
+ >>> t = siteManager.unregisterUtility(
+ ... folderSettings, interfaces.IPreferenceGroup, 'ZMISettings.Folder')
+
+
+Creating Preference Groups Using ZCML
+-------------------------------------
+
+If you are using the user preference system in Zope 3, you will not have to
+manually setup the preference groups as we did above (of course). We will use
+ZCML instead. First, we need to register the directives:
+
+ >>> from zope.configuration import xmlconfig
+ >>> import z3ext.preferences
+ >>> context = xmlconfig.file('meta.zcml', z3ext.preferences)
+
+Second we need root preference group:
+
+ >>> from z3ext.preferences.root import PersonalPreferences
+
+ >>> siteManager.registerUtility(
+ ... PersonalPreferences(), interfaces.IPreferenceGroup)
+
+Then the system sets up a root preference group:
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns:z3ext="http://namespaces.zope.org/z3ext" i18n_domain="test">
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings"
+ ... schema="z3ext.preferences.README.IZMIUserSettings"
+ ... title="ZMI User Settings"
+ ... permission="zope.Public" />
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings.Folder"
+ ... schema="z3ext.preferences.README.IFolderSettings"
+ ... title="Folder Content View Settings"
+ ... permission="zope.Public" />
+ ...
+ ... </configure>''', context)
+
+Now we can use the preference system in its intended way. We access the folder
+settings as follows:
+
+ >>> prefs = component.getUtility(interfaces.IPreferenceGroup)
+ >>> prefs.isAvailable()
+ False
+
+ >>> prefs['ZMISettings']['Folder'].isAvailable()
+ False
+
+
+Don't forget to bind preferences to principal
+
+ >>> prefs = prefs.__bind__(principal)
+ >>> prefs.isAvailable()
+ True
+
+ >>> p = prefs['ZMISettings']['Folder'].__bind__(parent=prefs)
+
+ >>> prefs['ZMISettings']['Folder'].isAvailable()
+ True
+
+ >>> prefs['ZMISettings']['Folder'].sortedBy
+ 'size'
+
+ >>> prefs.items()
+ [(u'ZMISettings', <z3ext.preferences.preferencetype.Preference<ZMISettings> ...>)]
+
+ >>> u'ZMISettings' in prefs
+ True
+
+ >>> prefs.keys()
+ (u'ZMISettings',)
+
+ >>> prefs.values()
+ [<z3ext.preferences.preferencetype.Preference<ZMISettings> ...>]
+
+ >>> list(iter(prefs))
+ [<z3ext.preferences.preferencetype.Preference<ZMISettings> ...>]
+
+ >>> len(prefs)
+ 1
+
+
+Let's register the ZMI settings again under a new name via ZCML:
+
+ >>> class IZMIUserSettings2(IZMIUserSettings):
+ ... pass
+
+ >>> IZMIUserSettings2.setTaggedValue('preferenceID', 'ZMISettings.Folder')
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
+ ... i18n_domain="test">
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings2"
+ ... title="ZMI Settings NG"
+ ... schema="z3ext.preferences.README.IZMIUserSettings"
+ ... permission="zope.Public"
+ ... provides="z3ext.preferences.interfaces.IPreferenceCategory" />
+ ...
+ ... </configure>''', context)
+
+ >>> prefs['ZMISettings2']
+ <z3ext.preferences.preferencetype.Preference<ZMISettings2> ...>
+
+ >>> prefs['ZMISettings2'].__title__
+ u'ZMI Settings NG'
+
+ >>> IZMIUserSettings.providedBy(prefs['ZMISettings2'])
+ True
+ >>> interfaces.IPreferenceCategory.providedBy(prefs['ZMISettings2'])
+ True
+
+And the tree can built again by carefully constructing the id:
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
+ ... i18n_domain="test">
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings2.Folder"
+ ... title="Folder Settings"
+ ... schema="z3ext.preferences.README.IFolderSettings" />
+ ...
+ ... </configure>''', context)
+
+ >>> prefs['ZMISettings2']
+ <z3ext.preferences.preferencetype.Preference<ZMISettings2> ...>
+
+ >>> prefs['ZMISettings2'].items()
+ [(u'ZMISettings2.Folder', <z3ext.preferences.preferencetype.Preference<ZMISettings2.Folder> ...)]
+
+ >>> list(iter(prefs['ZMISettings2']))
+ [<z3ext.preferences.preferencetype.Preference<ZMISettings2.Folder> ...>]
+
+ >>> prefs['ZMISettings2']['Folder'].__title__
+ u'Folder Settings'
+
+ >>> IFolderSettings.providedBy(prefs['ZMISettings2']['Folder'])
+ True
+ >>> interfaces.IPreferenceCategory.providedBy(prefs['ZMISettings2']['Folder'])
+ False
+
+We can define preference group for principal type
+
+ >>> class IMyPrincipal(zope.interface.Interface):
+ ... pass
+
+Now let's register preference for for this type of principal
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
+ ... i18n_domain="test">
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings2.Folder10"
+ ... for="z3ext.preferences.README.IMyPrincipal"
+ ... title="Folder Settings"
+ ... permission="zope.Public"
+ ... schema="z3ext.preferences.README.IFolderSettings" />
+ ...
+ ... </configure>''', context)
+
+ >>> p = component.getUtility(interfaces.IPreferenceGroup, 'ZMISettings2.Folder10')
+ >>> new_prefs = p.__bind__()
+ >>> new_prefs.isAvailable()
+ False
+
+Now let's mark our principal
+
+ >>> zope.interface.alsoProvides(principal, IMyPrincipal)
+ >>> new_prefs = p.__bind__()
+ >>> new_prefs.isAvailable()
+ True
+
+ >>> prefs['ZMISettings2'].remove('Folder10')
+
+
+Simple Python-Level Access
+--------------------------
+
+If a site is set, getting the user preferences is very simple:
+
+ >>> prefs2 = IFolderSettings(principal)
+
+ >>> prefs2.sortedBy
+ 'name'
+
+
+Security
+--------
+
+You might already wonder under which permissions the preferences are
+available. They are actually available with z3ext.ManagePreference
+permission. But sometimes we need preferences which can be changed
+only by manager. In this case we can provide default permission or
+even set security checks on attribute level, like in <class /> directive.
+
+ >>> import zope.security
+ >>> context = xmlconfig.file('meta.zcml', zope.security, context)
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:z3ext="http://namespaces.zope.org/z3ext"
+ ... i18n_domain="z3ext">
+ ...
+ ... <permission id="zope.View" title="zope view" />
+ ... <permission id="zope.Manage" title="zope manage" />
+ ...
+ ... <z3ext:preferenceGroup
+ ... id="ZMISettings3"
+ ... title="ZMI Settings 3"
+ ... schema="z3ext.preferences.README.IZMIUserSettings"
+ ... provides="z3ext.preferences.interfaces.IPreferenceCategory"
+ ... permission="zope.View">
+ ... <allow attributes="email" />
+ ... <require
+ ... attributes="showZopeLogo" permission="zope.Manage" set_attributes="skin" />
+ ... </z3ext:preferenceGroup>
+ ...
+ ... </configure>''', context)
+
Modified: z3ext.preferences/trunk/src/z3ext/preferences/tests/tests.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/tests/tests.py 2009-10-28 14:15:06 UTC (rev 105329)
+++ z3ext.preferences/trunk/src/z3ext/preferences/tests/tests.py 2009-10-28 14:17:43 UTC (rev 105330)
@@ -91,7 +91,7 @@
return unittest.TestSuite((
testbrowser,
doctest.DocFileSuite(
- '../README.txt',
+ 'README.txt',
setUp=setUp, tearDown=tearDown,
globs={'pprint': doctestunit.pprint},
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
More information about the checkins
mailing list