[Checkins] SVN: zope.app.preference/trunk/ merged icemac-zope.preference branch (using split out zope.preference)

Michael Howitz mh at gocept.com
Sat Jun 12 07:45:38 EDT 2010


Log message for revision 113388:
  merged icemac-zope.preference branch (using split out zope.preference)
  

Changed:
  U   zope.app.preference/trunk/CHANGES.txt
  U   zope.app.preference/trunk/README.txt
  U   zope.app.preference/trunk/setup.py
  D   zope.app.preference/trunk/src/zope/app/preference/README.txt
  A   zope.app.preference/trunk/src/zope/app/preference/README.txt
  U   zope.app.preference/trunk/src/zope/app/preference/__init__.py
  U   zope.app.preference/trunk/src/zope/app/preference/browser.py
  U   zope.app.preference/trunk/src/zope/app/preference/configure.zcml
  U   zope.app.preference/trunk/src/zope/app/preference/default.py
  U   zope.app.preference/trunk/src/zope/app/preference/ftesting.zcml
  U   zope.app.preference/trunk/src/zope/app/preference/interfaces.py
  U   zope.app.preference/trunk/src/zope/app/preference/meta.zcml
  U   zope.app.preference/trunk/src/zope/app/preference/metaconfigure.py
  U   zope.app.preference/trunk/src/zope/app/preference/metadirectives.py
  U   zope.app.preference/trunk/src/zope/app/preference/preference.py
  U   zope.app.preference/trunk/src/zope/app/preference/tests.py
  D   zope.app.preference/trunk/src/zope/app/preference/zmi.txt

-=-
Modified: zope.app.preference/trunk/CHANGES.txt
===================================================================
--- zope.app.preference/trunk/CHANGES.txt	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/CHANGES.txt	2010-06-12 11:45:38 UTC (rev 113388)
@@ -2,10 +2,10 @@
 CHANGES
 =======
 
-3.7.1 (unreleased)
+3.8.0 (unreleased)
 ------------------
 
-- Nothing changed yet.
+- Use split out `zope.preference`.
 
 
 3.7.0 (2010-06-11)

Modified: zope.app.preference/trunk/README.txt
===================================================================
--- zope.app.preference/trunk/README.txt	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/README.txt	2010-06-12 11:45:38 UTC (rev 113388)
@@ -1,2 +1,2 @@
-This package provides and API and UI to create and maintain hierarchical user
-preferences. Preferences can be easily created by defining schemas.
+This package provides a UI to maintain hierarchical user preferences
+in the ZMI.
\ No newline at end of file

Modified: zope.app.preference/trunk/setup.py
===================================================================
--- zope.app.preference/trunk/setup.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/setup.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -27,16 +27,14 @@
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
 setup(name = 'zope.app.preference',
-      version='3.7.1dev',
+      version='3.8.0dev',
       author='Zope Corporation and Contributors',
       author_email='zope-dev at zope.org',
-      description='User Preferences Framework',
+      description='User Preferences Framework ZMI UI',
       long_description=(
           read('README.txt')
           + '\n\n' +
-          'Detailed Dcoumentation\n' +
-          '======================\n'
-          + '\n\n' +
+          '.. contents::\n\n' +
           read('src', 'zope', 'app', 'preference', 'README.txt')
           + '\n\n' +
           read('CHANGES.txt')
@@ -60,29 +58,10 @@
       extras_require=dict(test=[
           'zope.app.testing',
           'zope.app.zcmlfiles',
-          'zope.securitypolicy',
-          'zope.testbrowser',
-          'zope.testing',
           ]),
       install_requires = ['setuptools',
-                          'ZODB3',
-                          'zope.annotation',
-                          'zope.app.basicskin',
-                          'zope.app.component',
-                          'zope.app.form',
-                          'zope.app.pagetemplate',
-                          'zope.app.renderer',
+                          'zope.preference',
                           'zope.app.tree',
-                          'zope.component',
-                          'zope.configuration',
-                          'zope.container',
-                          'zope.i18n',
-                          'zope.i18nmessageid',
-                          'zope.interface',
-                          'zope.location',
-                          'zope.schema',
-                          'zope.security',
-                          'zope.traversing',
                           ],
       include_package_data = True,
       zip_safe = False,

Deleted: zope.app.preference/trunk/src/zope/app/preference/README.txt
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/README.txt	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/README.txt	2010-06-12 11:45:38 UTC (rev 113388)
@@ -1,481 +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 zope.app.preference import preference
-
-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.interface
-  >>> import zope.schema
-  >>> 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)
-
-Now we can instantiate the preference group. Each preference group must have an
-ID by which it can be accessed and optional title and description fields for UI
-purposes:
-
-  >>> settings = preference.PreferenceGroup(
-  ...     "ZMISettings",
-  ...     schema=IZMIUserSettings,
-  ...     title=u"ZMI User Settings",
-  ...     description=u"")
-
-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__
-  'ZMISettings'
-  >>> settings.__schema__
-  <InterfaceClass zope.app.preference.README.IZMIUserSettings>
-  >>> settings.__title__
-  u'ZMI User Settings'
-
-So let's ask the preference group for the `skin` setting:
-
-  >>> settings.skin #doctest:+ELLIPSIS
-  Traceback (most recent call last):
-  ...
-  NoInteraction
-
-
-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 new
-interaction:
-
-  >>> 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, context):
-  ...         try:
-  ...             annotations = class_.data[principal.id]
-  ...         except KeyError:
-  ...             annotations = dict.__new__(class_)
-  ...             class_.data[principal.id] = annotations
-  ...         return annotations
-  ...     def __init__(self, principal, context):
-  ...         pass
-
-  >>> from zope.app.testing import ztapi
-  >>> ztapi.provideAdapter((Principal, zope.interface.Interface), IAnnotations,
-  ...                      PrincipalAnnotations)
-
-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.Set(
-  ...         title=u"Shown Fields",
-  ...         description=u"Fields shown in the table.",
-  ...         value_type=zope.schema.Choice(['name', 'size', 'creator']),
-  ...         default=set(['name', 'size']))
-  ...
-  ...     sortedBy = zope.schema.Choice(
-  ...         title=u"Sorted By",
-  ...         description=u"Data field to sort by.",
-  ...         values=['name', 'size', 'creator'],
-  ...         default='name')
-
-  >>> folderSettings = preference.PreferenceGroup(
-  ...     "ZMISettings.Folder",
-  ...     schema=IFolderSettings,
-  ...     title=u"Folder Content View Settings")
-
-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):
-  ...
-  AttributeError: 'Folder' is not a preference or sub-group.
-
-... but not before we register the groups as utilities:
-
-  >>> from zope.app.preference import interfaces
-  >>> from zope.app.testing import ztapi
-
-  >>> ztapi.provideUtility(interfaces.IPreferenceGroup, settings,
-  ...                      name='ZMISettings')
-  >>> ztapi.provideUtility(interfaces.IPreferenceGroup, folderSettings,
-  ...                      name='ZMISettings.Folder')
-
-If we now try to lookup the sub-group again, we should be successful:
-
-  >>> settings.Folder #doctest:+ELLIPSIS
-  <zope.app.preference.preference.PreferenceGroup object at ...>
-
-  >>> settings['Folder'] #doctest:+ELLIPSIS
-  <zope.app.preference.preference.PreferenceGroup object at ...>
-
-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
-
-
-Default Preferences
--------------------
-
-It sometimes desirable to define default settings on a site-by-site basis,
-instead of just using the default value from the schema. The preferences
-package provides a module
- 
-  >>> from zope.app.preference import default
-
-that implements a default preferences provider that can be added as a unnamed
-utility for each site. So the first step is to create a site:
-  
-  >>> root = setup.buildSampleFolderTree()
-  >>> rsm = setup.createSiteManager(root, True)
-
-Now we can register the default preference provider with the root site:
-
-  >>> provider = setup.addUtility(rsm, '', 
-  ...                             interfaces.IDefaultPreferenceProvider, 
-  ...                             default.DefaultPreferenceProvider())
-
-So before we set an explicit default value for a preference, the schema field
-default is used:
-
-  >>> settings.Folder.sortedBy
-  'name'
-
-But if we now set a new default value with the provider,
-
-  >>> defaultFolder = provider.getDefaultPreferenceGroup('ZMISettings.Folder')
-  >>> defaultFolder.sortedBy = 'size'
-
-then the default of the setting changes:
-  
-  >>> settings.Folder.sortedBy
-  'size'
-
-The default preference providers also implicitly acquire default values from
-parent sites. So if we make `folder1` a site and set it as the active site
-
-  >>> folder1 = root['folder1']
-  >>> sm1 = setup.createSiteManager(folder1, True)
-
-and add a default provider there,
-
-  >>> provider1 = setup.addUtility(sm1, '', 
-  ...                              interfaces.IDefaultPreferenceProvider, 
-  ...                              default.DefaultPreferenceProvider())
-
-then we still get the root's default values, because we have not defined any
-in the higher default provider:
-
-  >>> settings.Folder.sortedBy
-  'size'
-
-But if we provide the new provider with a default value for `sortedBy`,
-
-  >>> defaultFolder1 = provider1.getDefaultPreferenceGroup('ZMISettings.Folder')
-  >>> defaultFolder1.sortedBy = 'creator'
-
-then it is used instead:
-
-  >>> settings.Folder.sortedBy
-  'creator'
-
-Of course, once the root site becomes our active site again
-
-  >>> zope.app.component.hooks.setSite(root)
-
-the default value of the root provider is used:
-
-  >>> settings.Folder.sortedBy
-  'size'
-
-Of course, all the defaults in the world are not relevant anymore as soon as
-the user actually provides a value:
-
-  >>> settings.Folder.sortedBy = 'name'
-  >>> settings.Folder.sortedBy
-  'name'
-
-Oh, and have I mentioned that entered values are always validated? So you
-cannot just assign any old value:
-
-  >>> settings.Folder.sortedBy = 'foo'
-  Traceback (most recent call last):
-  ...
-  ConstraintNotSatisfied: foo
-
-Finally, if the user deletes his/her explicit setting, we are back to the
-default value:
-
-  >>> del settings.Folder.sortedBy
-  >>> settings.Folder.sortedBy
-  'size'
-
-
-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 zope.app.preference
-  >>> context = xmlconfig.file('meta.zcml', zope.app.preference)
-
-Then the system sets up a root preference group:
-
-  >>> context = xmlconfig.string('''
-  ...     <configure
-  ...         xmlns="http://namespaces.zope.org/zope"
-  ...         i18n_domain="test">
-  ...
-  ...       <preferenceGroup
-  ...           id=""
-  ...           title="User Preferences" 
-  ...           />
-  ...
-  ...     </configure>''', context)
-
-Now we can use the preference system in its intended way. We access the folder
-settings as follows:
-
-  >>> import zope.component
-  >>> prefs = zope.component.getUtility(interfaces.IPreferenceGroup)
-  >>> prefs.ZMISettings.Folder.sortedBy
-  'size'
-
-Let's register the ZMI settings again under a new name via ZCML:
-
-  >>> context = xmlconfig.string('''
-  ...     <configure
-  ...         xmlns="http://namespaces.zope.org/zope"
-  ...         i18n_domain="test">
-  ...
-  ...       <preferenceGroup
-  ...           id="ZMISettings2"
-  ...           title="ZMI Settings NG"
-  ...           schema="zope.app.preference.README.IZMIUserSettings"
-  ...           category="true"
-  ...           />
-  ...
-  ...     </configure>''', context)
-
-  >>> prefs.ZMISettings2 #doctest:+ELLIPSIS
-  <zope.app.preference.preference.PreferenceGroup object at ...>
-
-  >>> 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="http://namespaces.zope.org/zope"
-  ...         i18n_domain="test">
-  ...
-  ...       <preferenceGroup
-  ...           id="ZMISettings2.Folder"
-  ...           title="Folder Settings"
-  ...           schema="zope.app.preference.README.IFolderSettings"
-  ...           />
-  ...
-  ...     </configure>''', context)
-
-  >>> prefs.ZMISettings2 #doctest:+ELLIPSIS
-  <zope.app.preference.preference.PreferenceGroup object at ...>
-
-  >>> prefs.ZMISettings2.Folder.__title__
-  u'Folder Settings'
-
-  >>> IFolderSettings.providedBy(prefs.ZMISettings2.Folder)
-  True
-  >>> interfaces.IPreferenceCategory.providedBy(prefs.ZMISettings2.Folder)
-  False
-
-
-Simple Python-Level Access
---------------------------
-
-If a site is set, getting the user preferences is very simple:
-
-  >>> from zope.app.preference import UserPreferences
-  >>> prefs2 = UserPreferences()
-  >>> prefs2.ZMISettings.Folder.sortedBy
-  'size'
-
-This function is also commonly registered as an adapter,
-
-  >>> from zope.location.interfaces import ILocation
-  >>> ztapi.provideAdapter(ILocation, interfaces.IUserPreferences, 
-  ...                      UserPreferences)
-
-so that you can adapt any location to the user preferences:
-
-  >>> prefs3 = interfaces.IUserPreferences(folder1)
-  >>> prefs3.ZMISettings.Folder.sortedBy
-  'creator'
-
-
-Traversal
----------
-
-Okay, so all these objects are nice, but they do not make it any easier to
-access the preferences in page templates. Thus, a special traversal namespace
-has been created that makes it very simple to access the preferences via a
-traversal path. But before we can use the path expressions, we have to
-register all necessary traversal components and the special `preferences`
-namespace:
-
-  >>> import zope.traversing.interfaces
-  >>> ztapi.provideAdapter(None,
-  ...                      zope.traversing.interfaces.ITraversable,
-  ...                      preference.preferencesNamespace,
-  ...                      'preferences')
-
-We can now access the preferences as follows:
-
-  >>> from zope.traversing.api import traverse
-  >>> traverse(None, '++preferences++ZMISettings/skin')
-  'Basic'
-  >>> traverse(None, '++preferences++/ZMISettings/skin')
-  'Basic'
-
-
-Security
---------
-
-You might already wonder under which permissions the preferences are
-available. They are actually available publicly (`CheckerPublic`), but that
-is not a problem, since the available values are looked up specifically for
-the current user. And why should a user not have full access to his/her
-preferences? 
-
-Let's create a checker using the function that the security machinery is
-actually using:
-
-  >>> checker = preference.PreferenceGroupChecker(settings)
-  >>> checker.permission_id('skin')
-  Global(CheckerPublic,zope.security.checker)
-  >>> checker.setattr_permission_id('skin')
-  Global(CheckerPublic,zope.security.checker)
-
-The id, title, description, and schema are publicly available for access,
-but are not available for mutation at all:
-
-  >>> checker.permission_id('__id__')
-  Global(CheckerPublic,zope.security.checker)
-  >>> checker.setattr_permission_id('__id__') is None
-  True
-
-
-The only way security could be compromised is when one could override the
-annotations property. However, this property is not available for public
-consumption at all, including read access:
-
-  >>> checker.permission_id('annotation') is None
-  True
-  >>> checker.setattr_permission_id('annotation') is None
-  True

Copied: zope.app.preference/trunk/src/zope/app/preference/README.txt (from rev 113387, zope.app.preference/branches/icemac-zope.preference/src/zope/app/preference/README.txt)
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/README.txt	                        (rev 0)
+++ zope.app.preference/trunk/src/zope/app/preference/README.txt	2010-06-12 11:45:38 UTC (rev 113388)
@@ -0,0 +1,153 @@
+===================
+zope.app.preference
+===================
+
+This package provides a user interface in th ZMI, so the user can edit
+the preferences.
+
+Set up
+------
+
+To show the user interface functions we need some setup beforehand:
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+
+As the preferences cannot be defined through the web we have to define
+them in python code:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> 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)
+
+The preference schema is usually registered using a ZCML statement:
+
+  >>> from zope.configuration import xmlconfig
+  >>> import zope.app.preference
+  >>> context = xmlconfig.file('meta.zcml', zope.app.preference)
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns="http://namespaces.zope.org/zope"
+  ...         i18n_domain="test">
+  ...
+  ...       <preferenceGroup
+  ...           id="ZMISettings"
+  ...           title="ZMI Settings"
+  ...           schema="zope.app.preference.README.IZMIUserSettings"
+  ...           category="true"
+  ...           />
+  ...
+  ...     </configure>''', context)
+
+Editing Preferences
+-------------------
+
+The preferences are accessable in the ``++preferences++`` namespace:
+
+  >>> browser.open('http://localhost/++preferences++')
+
+The page shows a form which allows editing the preference values:
+
+  >>> browser.getControl('E-mail').value = 'hans at example.com'
+  >>> browser.getControl('Skin').displayOptions
+  ['Rotterdam', 'ZopeTop', 'Basic']
+  >>> browser.getControl('Skin').displayValue = ['ZopeTop']
+  >>> browser.getControl('Show Zope Logo').selected
+  True
+  >>> browser.getControl('Show Zope Logo').click()
+
+After selecting `Change` the values get persisted:
+
+  >>> browser.getControl('Change').click()
+  >>> browser.url
+  'http://localhost/++preferences++/@@index.html'
+  >>> browser.getControl('E-mail').value
+  'hans at example.com'
+  >>> browser.getControl('Skin').displayValue
+  ['ZopeTop']
+  >>> browser.getControl('Show Zope Logo').selected
+  False
+
+The preference group is shown in a tree. It has a link to the form:
+
+  >>> browser.getLink('ZMISettings').click()
+  >>> browser.url
+  'http://localhost/++preferences++/ZMISettings/@@index.html'
+  >>> browser.getControl('E-mail').value
+  'hans at example.com'
+
+
+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 Folder Settings"""
+  ...
+  ...     shownFields = zope.schema.Set(
+  ...         title=u"Shown Fields",
+  ...         description=u"Fields shown in the table.",
+  ...         value_type=zope.schema.Choice(['name', 'size', 'creator']),
+  ...         default=set(['name', 'size']))
+  ...
+  ...     sortedBy = zope.schema.Choice(
+  ...         title=u"Sorted By",
+  ...         description=u"Data field to sort by.",
+  ...         values=['name', 'size', 'creator'],
+  ...         default='name')
+
+And register it:
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns="http://namespaces.zope.org/zope"
+  ...         i18n_domain="test">
+  ...
+  ...       <preferenceGroup
+  ...           id="ZMISettings.Folder"
+  ...           title="Folder Content View Settings"
+  ...           schema="zope.app.preference.README.IFolderSettings"
+  ...           />
+  ...
+  ...     </configure>''', context)
+
+The sub-group is displayed inside the parent group as a form:
+
+  >>> browser.reload()
+  >>> browser.getControl('Shown Fields').displayOptions
+  ['name', 'size', 'creator']
+  >>> browser.getControl('Shown Fields').displayValue
+  ['name', 'size']
+  >>> browser.getControl('Shown Fields').displayValue = ['size', 'creator']
+  >>> browser.getControl('Sorted By').displayOptions
+  ['name', 'size', 'creator']
+  >>> browser.getControl('Sorted By').displayValue = ['creator']
+
+Selecing `Change` persists these values, too:
+
+  >>> browser.getControl('Change').click()
+  >>> browser.getControl('Shown Fields').displayValue
+  ['size', 'creator']
+  >>> browser.getControl('Sorted By').displayValue
+  ['creator']

Modified: zope.app.preference/trunk/src/zope/app/preference/__init__.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/__init__.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/__init__.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -1,3 +1,4 @@
 # Make a package
 
-from zope.app.preference.preference import UserPreferences
+# BBB
+from zope.preference.preference import UserPreferences

Modified: zope.app.preference/trunk/src/zope/app/preference/browser.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/browser.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/browser.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -20,7 +20,6 @@
 import re
 import zope.component
 import zope.interface
-import zope.schema
 from zope.container.interfaces import IObjectFindFilter
 from zope.i18n import translate
 from zope.i18nmessageid import Message
@@ -29,12 +28,10 @@
 
 from zope.app.basicskin.standardmacros import StandardMacros
 from zope.app.form.browser.editview import EditView
-from zope.app.pagetemplate.simpleviewclass import simple
-from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
 from zope.app.tree.browser.cookie import CookieTreeView
 from zope.i18nmessageid import ZopeMessageFactory as _
 
-from zope.app.preference import interfaces
+from zope.preference import interfaces
 
 
 NoneInterface = zope.interface.interface.InterfaceClass('None')

Modified: zope.app.preference/trunk/src/zope/app/preference/configure.zcml
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/configure.zcml	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/configure.zcml	2010-06-12 11:45:38 UTC (rev 113388)
@@ -6,38 +6,12 @@
     i18n_domain="zope"
     >
 
-  <view
-      name="preferences"
-      for="*"
-      type="*"
-      provides="zope.traversing.interfaces.ITraversable"
-      factory=".preference.preferencesNamespace"
-      />
+  <include package="zope.preference" />
 
-  <adapter
-      name="preferences"
-      for="*"
-      provides="zope.traversing.interfaces.ITraversable"
-      factory=".preference.preferencesNamespace"
-      />
-
-  <adapter
-      for="zope.location.interfaces.ILocation"
-      provides=".interfaces.IUserPreferences"
-      factory=".preference.UserPreferences"
-      />
-
-  <!-- Root preference group -->
-  <preferenceGroup
-      id=""
-      title="User Preferences"
-      />
-
-
   <!-- Preference Groups -->
   <browser:page
       name="index.html"
-      for=".interfaces.IPreferenceGroup"
+      for="zope.preference.interfaces.IPreferenceGroup"
       class=".browser.EditPreferenceGroup"
       template="index.pt"
       permission="zope.Public"
@@ -45,34 +19,14 @@
 
   <browser:page
       name="editAsSubGroup"
-      for=".interfaces.IPreferenceGroup"
+      for="zope.preference.interfaces.IPreferenceGroup"
       class=".browser.EditPreferenceGroup"
       template="subgroup.pt"
       permission="zope.Public"
       />
 
-
-  <!-- Default Preference Provider -->
-  <class class=".default.DefaultPreferenceProvider">
-    <implements
-        interface="zope.annotation.interfaces.IAttributeAnnotatable"
-        />
-    <require
-        permission="zope.ManageSite"
-        interface=".interfaces.IDefaultPreferenceProvider"
-        />
-  </class>
-
-  <view
-      name="preferences"
-      for=".interfaces.IDefaultPreferenceProvider"
-      type="*"
-      provides="zope.interface.Interface"
-      factory=".default.DefaultPreferences"
-      />
-
   <browser:addMenuItem
-      class=".default.DefaultPreferenceProvider"
+      class="zope.preference.default.DefaultPreferenceProvider"
       title="Default User Preferences Provider"
       description="A Default User Preferences Provider"
       permission="zope.ManageSite"
@@ -99,7 +53,7 @@
 
   <browser:page
       name="tree"
-      for=".interfaces.IPreferenceGroup"
+      for="zope.preference.interfaces.IPreferenceGroup"
       class=".browser.PreferencesTree"
       permission="zope.View"
       attribute="tree"

Modified: zope.app.preference/trunk/src/zope/app/preference/default.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/default.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/default.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -11,104 +11,11 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Default Preferences Provider
+"""Default Preferences Provider"""
 
-$Id$
-"""
-__docformat__ = "reStructuredText"
-import persistent
-from BTrees.OOBTree import OOBTree
-
-import zope.interface
-import zope.component
-from zope.security.checker import defineChecker
-from zope.traversing.interfaces import IContainmentRoot
-from zope.location import locate
-
-import zope.app.component
-from zope.app.container.contained import Contained
-from zope.app.preference import preference, interfaces
-
-class DefaultPreferenceProvider(persistent.Persistent, Contained):
-    zope.interface.implements(interfaces.IDefaultPreferenceProvider)
-
-    def __init__(self):
-        self.data = OOBTree()
-
-    def getDefaultPreferenceGroup(self, id=''):
-        group = zope.component.getUtility(interfaces.IPreferenceGroup, name=id)
-        group = group.__bind__(self)
-        default = DefaultPreferenceGroup(group, self)
-        zope.interface.alsoProvides(default, IContainmentRoot)
-        locate(default, self, 'preferences')
-        return default
-
-    preferences = property(getDefaultPreferenceGroup)
-
-
-def DefaultPreferences(context, request):
-    return context.preferences
-
-
-class DefaultPreferenceGroup(preference.PreferenceGroup):
-    """A preference group representing the site-wide default values."""
-
-    def __init__(self, group, provider):
-        self.provider = provider
-        super(DefaultPreferenceGroup, self).__init__(
-            group.__id__, group.__schema__,
-            group.__title__, group.__description__)
-
-        # Make sure that we also mark the default group as category if the
-        # actual group is one; this is important for the UI.
-        if interfaces.IPreferenceCategory.providedBy(group):
-            zope.interface.alsoProvides(self, interfaces.IPreferenceCategory)
-
-    def get(self, key, default=None):
-        group = super(DefaultPreferenceGroup, self).get(key, default)
-        if group is default:
-            return default
-        return DefaultPreferenceGroup(group, self.provider).__bind__(self)
-    
-    def items(self):
-        return [
-            (id, DefaultPreferenceGroup(group, self.provider).__bind__(self))
-            for id, group in super(DefaultPreferenceGroup, self).items()]
-
-    def __getattr__(self, key):
-        # Try to find a sub-group of the given id
-        group = self.get(key)
-        if group is not None:
-            return group
-
-        # Try to find a preference of the given name
-        if self.__schema__ and key in self.__schema__:
-            marker = object()
-            value = self.data.get(key, marker)
-            if value is not marker:
-                return value
-
-            # There is currently no local entry, so let's go to the next
-            # provider and lookup the group and value there.
-            nextProvider = zope.app.component.queryNextUtility(
-                self.provider, interfaces.IDefaultPreferenceProvider)
-
-            # No more providers found, so return the schema's default
-            if nextProvider is None: 
-                return self.__schema__[key].default
-
-            nextGroup = nextProvider.getDefaultPreferenceGroup(self.__id__)
-            return getattr(nextGroup, key, self.__schema__[key].default)
-
-        # Nothing found, raise an attribute error
-        raise AttributeError("'%s' is not a preference or sub-group." % key)
-
-    def data(self):
-        if self.__id__ not in self.provider.data:
-            self.provider.data[self.__id__] = OOBTree()
-
-        return self.provider.data[self.__id__]
-    data = property(data)
-
-
-defineChecker(DefaultPreferenceGroup, preference.PreferenceGroupChecker)
+# BBB
+from zope.preferences.default import (
+    DefaultPreferenceGroup,
+    DefaultPreferenceProvider,
+    DefaultPreferences,
+    )

Modified: zope.app.preference/trunk/src/zope/app/preference/ftesting.zcml
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/ftesting.zcml	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/ftesting.zcml	2010-06-12 11:45:38 UTC (rev 113388)
@@ -4,10 +4,12 @@
   <!-- used for functional testing setup -->
 
   <include package="zope.app.zcmlfiles" />
+
+  <include package="zope.preference" file="meta.zcml"/>
+  <include package="zope.preference" />
+
+  <include package="zope.app.preference" />
   <include package="zope.app.tree" />
   <include package="zope.app.renderer" />
 
-  <include package="zope.app.preference" file="meta.zcml"/>
-  <include package="zope.app.preference" />
-
 </configure>

Modified: zope.app.preference/trunk/src/zope/app/preference/interfaces.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/interfaces.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/interfaces.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -11,75 +11,12 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""User Preferences Interfaces
+"""User Preferences Interfaces"""
 
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-import zope.interface
-import zope.schema
-from zope.configuration.fields import MessageID
-from zope.location.interfaces import ILocation
-
-class IPreferenceGroup(ILocation):
-    """A group of preferences.
-
-    This component represents a logical group of preferences. The preferences
-    contained by this group is defined through the schema. The group has also
-    a name by which it can be accessed.
-
-    The fields specified in the schema *must* be available as attributes and
-    items of the group instance. It is up to the implementation how this is
-    realized, however, most often one will implement __setattr__ and
-    __getattr__ as well as the common mapping API. 
-
-    The reason all the API fields are doubly underlined is to avoid name
-    clashes.
-    """
-
-    __id__ = zope.schema.TextLine(
-        title=u"Id",
-        description=u"The id of the group.",
-        required=True)
-
-    __schema__ = zope.schema.InterfaceField(
-        title=u"Schema",
-        description=u"Schema describing the preferences of the group.",
-        required=False)
-
-    __title__ = MessageID(
-        title=u"Title",
-        description=u"The title of the group used in the UI.",
-        required=True)
-
-    __description__ = MessageID(
-        title=u"Description",
-        description=u"The description of the group used in the UI.",
-        required=False)
-
-
-class IPreferenceCategory(zope.interface.Interface):
-    """A collection of preference groups.
-
-    Objects providing this interface serve as groups of preference
-    groups. This allows UIs to distinguish between high- and low-level
-    prefernce groups.
-    """
-
-class IUserPreferences(zope.interface.Interface):
-    """Objects providing this interface have to provide the root preference
-    group API as well."""
-
-
-class IDefaultPreferenceProvider(zope.interface.Interface):
-    """A root object providing default values for the entire preferences tree.
-
-    Default preference providers are responsible for providing default values
-    for all preferences. The way they get these values are up to the
-    implementation.
-    """
-
-    preferences = zope.schema.Field(
-        title = u"Default Preferences Root",
-        description = u"Link to the default preferences")
+# BBB
+from zope.preferences.interfaces import (
+    IDefaultPreferenceProvider,
+    IPreferenceCategory,
+    IPreferenceGroup,
+    IUserPreferences,
+    )

Modified: zope.app.preference/trunk/src/zope/app/preference/meta.zcml
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/meta.zcml	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/meta.zcml	2010-06-12 11:45:38 UTC (rev 113388)
@@ -3,16 +3,6 @@
     xmlns:meta="http://namespaces.zope.org/meta"
     >
 
-  <meta:provides feature="preference" />
+  <include package="zope.preference" file="meta.zcml" />
 
-  <meta:directives namespace="http://namespaces.zope.org/zope">
-
-    <meta:directive
-        name="preferenceGroup"
-        schema=".metadirectives.IPreferenceGroupDirective"
-        handler=".metaconfigure.preferenceGroup"
-        />
-
-  </meta:directives>
-
 </configure>

Modified: zope.app.preference/trunk/src/zope/app/preference/metaconfigure.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/metaconfigure.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/metaconfigure.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -11,20 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""This module handles the 'apidoc' namespace directives.
+"""This module handles the 'preference' namespace directives."""
 
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-from zope.component.zcml import utility
-
-from zope.app.preference.interfaces import IPreferenceGroup
-from zope.app.preference.preference import PreferenceGroup
-
-
-def preferenceGroup(_context, id=None, schema=None,
-                    title=u'', description=u'', category=False):
-    if id is None:
-        id = ''
-    group = PreferenceGroup(id, schema, title, description, category)
-    utility(_context, IPreferenceGroup, group, name=id)
+# BBB
+from zope.preference.metaconfigure import preferenceGroup

Modified: zope.app.preference/trunk/src/zope/app/preference/metadirectives.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/metadirectives.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/metadirectives.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -11,48 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""``apidoc:preferencesgroup`` ZCML directive interface
+"""``apidoc:preferencesgroup`` ZCML directive interface"""
 
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-from zope.interface import Interface
-from zope.configuration import fields
-
-class IPreferenceGroupDirective(Interface):
-    """Register a preference group."""
-
-    # The id is not required, since the root group has an empty id.
-    id = fields.PythonIdentifier(
-        title=u"Id",
-        description=u"""
-            Id of the preference group used to access the group. The id should
-            be a valid path in the preferences tree.""",
-        required=False,
-        )
-
-    schema = fields.GlobalInterface(
-        title=u"Schema",
-        description=u"Schema of the preference group used defining the "
-                    u"preferences of the group.",
-        required=False        
-        )
-
-    title = fields.MessageID(
-        title=u"Title",
-        description=u"Title of the preference group used in UIs.",
-        required=True
-        )
-
-    description = fields.MessageID(
-        title=u"Description",
-        description=u"Description of the preference group used in UIs.",
-        required=False
-        )
-
-    category = fields.Bool(
-        title=u"Is Group a Category",
-        description=u"Denotes whether this preferences group is a category.",
-        required=False,
-        default=False
-        )
+# BBB
+from zope.preferences.metadirectives import IPreferenceGroupDirective

Modified: zope.app.preference/trunk/src/zope/app/preference/preference.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/preference.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/preference.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -11,240 +11,12 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""User Preferences System
+"""User Preferences System"""
 
-$Id$
-"""
-__docformat__ = "reStructuredText"
-from BTrees.OOBTree import OOBTree
-
-import zope.interface
-import zope.component
-from zope.schema import getFields
-from zope.security.checker import CheckerPublic, Checker, defineChecker
-from zope.security.management import getInteraction
-from zope.traversing.interfaces import IContainmentRoot
-from zope.location import LocationProxy, locate, Location
-from zope.annotation.interfaces import IAnnotations
-
-import zope.app.component.hooks
-from zope.container.contained import Contained
-from zope.container.interfaces import IReadContainer
-
-from zope.app.preference.interfaces import IPreferenceGroup
-from zope.app.preference.interfaces import IPreferenceCategory
-from zope.app.preference.interfaces import IDefaultPreferenceProvider
-
-pref_key = 'zope.app.user.UserPreferences'
-
-
-class PreferenceGroup(Location):
-    """A feature-rich ``IPreferenceGroup`` implementation.
-
-    This class implements the
-    """
-    zope.interface.implements(IPreferenceGroup, IReadContainer)
-
-    # Declare attributes here, so that they are always available.
-    __id__ = ''
-    __schema__ = None
-    __title__ = None
-    __description__ = None
-
-    def __init__(self, id, schema=None, title=u'', description=u'',
-                 isCategory=False):
-        self.__id__ = id
-        self.__schema__ = schema
-        self.__title__ = title
-        self.__description__ = description
-
-        # The last part of the id is the name.
-        self.__name__ = id.split('.')[-1]
-
-        # Make sure this group provides all important interfaces.
-        directlyProvided = ()
-        if isCategory:
-            directlyProvided += (IPreferenceCategory,)
-        if schema:
-            directlyProvided += (schema,)
-        zope.interface.directlyProvides(self, directlyProvided)
-
-    # Store the actual parent in ``__parent``. Usually we would just override
-    # the property to an actual value during binding, but because we overrode
-    # ``__setattr__`` this is not possible anymore.
-    __parent = None
-    def __parent__(self):
-        return self.__parent or zope.app.component.hooks.getSite()
-    __parent__ = property(__parent__)
-
-
-    def __bind__(self, parent):
-        clone = self.__class__.__new__(self.__class__)
-        clone.__dict__.update(self.__dict__)
-        clone.__parent = parent
-        return clone
-
-
-    def get(self, key, default=None):
-        id = self.__id__ and self.__id__ + '.' + key or key
-        group = zope.component.queryUtility(IPreferenceGroup, id, default)
-        if group is default:
-            return default
-        return group.__bind__(self)
-
-
-    def items(self):
-        cutoff = self.__id__ and len(self.__id__)+1 or 0
-        utilities = zope.component.getUtilitiesFor(IPreferenceGroup)
-        return [(id[cutoff:], group.__bind__(self))
-                for id, group in utilities
-                if id != self.__id__ and \
-                   id.startswith(self.__id__) and \
-                   id[cutoff:].find('.') == -1]
-
-    def __getitem__(self, key):
-        """See zope.container.interfaces.IReadContainer"""
-        default = object()
-        obj = self.get(key, default)
-        if obj is default:
-            raise KeyError(key)
-        return obj
-
-    def __contains__(self, key):
-        """See zope.container.interfaces.IReadContainer"""
-        return self.get(key) is not None
-
-    def keys(self):
-        """See zope.container.interfaces.IReadContainer"""
-        return [id for id, group in self.items()]
-
-    def __iter__(self):
-        """See zope.container.interfaces.IReadContainer"""
-        return self.values().__iter__()
-
-    def values(self):
-        """See zope.container.interfaces.IReadContainer"""
-        return [group for id, group in self.items()]
-
-    def __len__(self):
-        """See zope.container.interfaces.IReadContainer"""
-        return len(self.items())
-
-    def __getattr__(self, key):
-        # Try to find a sub-group of the given id
-        group = self.get(key)
-        if group is not None:
-            return group
-
-        # Try to find a preference of the given name
-        if self.__schema__ and key in self.__schema__:
-            marker = object()
-            value = self.data.get(key, marker)
-            if value is marker:
-                # Try to find a default preference provider
-                provider = zope.component.queryUtility(
-                    IDefaultPreferenceProvider,
-                    context=self
-                    )
-                if provider is None:
-                    return self.__schema__[key].default
-                defaultGroup = provider.getDefaultPreferenceGroup(self.__id__)
-                return getattr(defaultGroup, key)
-            return value
-
-        # Nothing found, raise an attribute error
-        raise AttributeError("'%s' is not a preference or sub-group." % key)
-
-    def __setattr__(self, key, value):
-        if self.__schema__ and key in self.__schema__:
-            # Validate the value
-            bound = self.__schema__[key].bind(self)
-            bound.validate(value)
-            # Assign value
-            self.data[key] = value
-        else:
-            self.__dict__[key] = value
-            # If the schema changed, we really need to change the security
-            # checker as well.
-            if key is '__schema__':
-                checker = PreferenceGroupChecker(self)
-                self.__dict__['__Security_checker__'] = checker
-
-    def __delattr__(self, key):
-        if self.__schema__ and key in self.__schema__:
-            del self.data[key]
-        else:
-            del self.__dict__[key]
-
-    def data(self):
-        # TODO: what if we have multiple participations?
-        principal = getInteraction().participations[0].principal
-        ann = zope.component.getMultiAdapter((principal, self), IAnnotations)
-
-        # If no preferences exist, create the root preferences object.
-        if  ann.get(pref_key) is None:
-            ann[pref_key] = OOBTree()
-        prefs = ann[pref_key]
-
-        # If no entry for the group exists, create a new entry.
-        if self.__id__ not in prefs.keys():
-            prefs[self.__id__] = OOBTree()
-
-        return prefs[self.__id__]
-    data = property(data)
-
-
-def PreferenceGroupChecker(instance):
-    """A function that generates a custom security checker.
-
-    The attributes available in a preference group are dynamically generated
-    based on the group schema and the available sub-groups. Thus, the
-    permission dictionaries have to be generated at runtime and are unique for
-    each preference group instance.
-    """
-    read_perm_dict = {}
-    write_perm_dict = {}
-
-    # Make sure that the attributes from IPreferenceGroup and IReadContainer
-    # are public.
-    for attrName in ('__id__', '__schema__', '__title__', '__description__',
-                     'get', 'items', 'keys', 'values',
-                     '__getitem__', '__contains__', '__iter__', '__len__'):
-        read_perm_dict[attrName] = CheckerPublic
-
-    # Make the attributes generated from the schema available as well.
-    if instance.__schema__ is not None:
-        for name in getFields(instance.__schema__):
-            read_perm_dict[name] = CheckerPublic
-            write_perm_dict[name] = CheckerPublic
-
-    # Make all sub-groups available as well.
-    for name in instance.keys():
-        read_perm_dict[name] = CheckerPublic
-        write_perm_dict[name] = CheckerPublic
-
-    return Checker(read_perm_dict, write_perm_dict)
-
-
-def UserPreferences(context=None):
-    """Adapts an ``ILocation`` object to the ``IUserPreferences`` interface."""
-    if context is None:
-        context = zope.component.getSiteManager()
-    rootGroup = zope.component.getUtility(IPreferenceGroup)
-    rootGroup = rootGroup.__bind__(context)
-    rootGroup.__name__ = '++preferences++'
-    zope.interface.alsoProvides(rootGroup, IContainmentRoot)
-    return rootGroup
-
-class preferencesNamespace(object):
-    """Used to traverse to the root preferences group."""
-
-    def __init__(self, ob, request=None):
-        self.context = ob
-
-    def traverse(self, name, ignore):
-        rootGroup = zope.component.getUtility(IPreferenceGroup)
-        rootGroup = rootGroup.__bind__(self.context)
-        rootGroup.__name__ = '++preferences++'
-        zope.interface.alsoProvides(rootGroup, IContainmentRoot)
-        return name and rootGroup[name] or rootGroup
+# BBB
+from zope.preference.preference import (
+    PreferenceGroup,
+    PreferenceGroupChecker,
+    UserPreferences,
+    preferencesNamespace,
+    )

Modified: zope.app.preference/trunk/src/zope/app/preference/tests.py
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/tests.py	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/tests.py	2010-06-12 11:45:38 UTC (rev 113388)
@@ -17,33 +17,21 @@
 """
 from zope.app.testing import setup, functional
 from zope.component import testing
-import doctest
 import unittest
 import zope.app.preference.testing
 
 
 def setUp(test):
-    testing.setUp(test)
     setup.setUpTestAsModule(test, 'zope.app.preference.README')
 
 
-def ftest_setUp(test):
-    setup.setUpTestAsModule(test, 'zope.app.preference.zmi')
-
-
 def tearDown(test):
     testing.tearDown(test)
     setup.tearDownTestAsModule(test)
 
 
 def test_suite():
-    browser_tests = functional.FunctionalDocFileSuite(
-        'zmi.txt', setUp=ftest_setUp, tearDown=tearDown)
-    browser_tests.layer = zope.app.preference.testing.PreferencesLayer
-
-    return unittest.TestSuite((
-        doctest.DocFileSuite('README.txt',
-                             setUp=setUp, tearDown=tearDown,
-                             optionflags=doctest.NORMALIZE_WHITESPACE),
-        browser_tests,
-        ))
+    tests = functional.FunctionalDocFileSuite(
+        'README.txt', setUp=setUp, tearDown=tearDown)
+    tests.layer = zope.app.preference.testing.PreferencesLayer
+    return unittest.TestSuite((tests,))

Deleted: zope.app.preference/trunk/src/zope/app/preference/zmi.txt
===================================================================
--- zope.app.preference/trunk/src/zope/app/preference/zmi.txt	2010-06-12 11:44:21 UTC (rev 113387)
+++ zope.app.preference/trunk/src/zope/app/preference/zmi.txt	2010-06-12 11:45:38 UTC (rev 113388)
@@ -1,153 +0,0 @@
-=======
-Browser
-=======
-
-This package also provides a user interface, so the user can edit the
-preferences.
-
-Set up
-------
-
-To show the user interface functions we need some setup beforehand:
-
-  >>> from zope.testbrowser.testing import Browser
-  >>> browser = Browser()
-
-As the preferences cannot be defined through the web we have to define
-them in python code:
-
-  >>> import zope.interface
-  >>> import zope.schema
-  >>> 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)
-
-The preference schema is usually registered using a ZCML statement:
-
-  >>> from zope.configuration import xmlconfig
-  >>> import zope.app.preference
-  >>> context = xmlconfig.file('meta.zcml', zope.app.preference)
-
-  >>> context = xmlconfig.string('''
-  ...     <configure
-  ...         xmlns="http://namespaces.zope.org/zope"
-  ...         i18n_domain="test">
-  ...
-  ...       <preferenceGroup
-  ...           id="ZMISettings"
-  ...           title="ZMI Settings"
-  ...           schema="zope.app.preference.zmi.IZMIUserSettings"
-  ...           category="true"
-  ...           />
-  ...
-  ...     </configure>''', context)
-
-Editing Preferences
--------------------
-
-The preferences are accessable in the ``++preferences++`` namespace:
-
-  >>> browser.open('http://localhost/++preferences++')
-
-The page shows a form which allows editing the preference values:
-
-  >>> browser.getControl('E-mail').value = 'hans at example.com'
-  >>> browser.getControl('Skin').displayOptions
-  ['Rotterdam', 'ZopeTop', 'Basic']
-  >>> browser.getControl('Skin').displayValue = ['ZopeTop']
-  >>> browser.getControl('Show Zope Logo').selected
-  True
-  >>> browser.getControl('Show Zope Logo').click()
-
-After selecting `Change` the values get persisted:
-
-  >>> browser.getControl('Change').click()
-  >>> browser.url
-  'http://localhost/++preferences++/@@index.html'
-  >>> browser.getControl('E-mail').value
-  'hans at example.com'
-  >>> browser.getControl('Skin').displayValue
-  ['ZopeTop']
-  >>> browser.getControl('Show Zope Logo').selected
-  False
-
-The preference group is shown in a tree. It has a link to the form:
-
-  >>> browser.getLink('ZMISettings').click()
-  >>> browser.url
-  'http://localhost/++preferences++/ZMISettings/@@index.html'
-  >>> browser.getControl('E-mail').value
-  'hans at example.com'
-
-
-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 Folder Settings"""
-  ...
-  ...     shownFields = zope.schema.Set(
-  ...         title=u"Shown Fields",
-  ...         description=u"Fields shown in the table.",
-  ...         value_type=zope.schema.Choice(['name', 'size', 'creator']),
-  ...         default=set(['name', 'size']))
-  ...
-  ...     sortedBy = zope.schema.Choice(
-  ...         title=u"Sorted By",
-  ...         description=u"Data field to sort by.",
-  ...         values=['name', 'size', 'creator'],
-  ...         default='name')
-
-And register it:
-
-  >>> context = xmlconfig.string('''
-  ...     <configure
-  ...         xmlns="http://namespaces.zope.org/zope"
-  ...         i18n_domain="test">
-  ...
-  ...       <preferenceGroup
-  ...           id="ZMISettings.Folder"
-  ...           title="Folder Content View Settings"
-  ...           schema="zope.app.preference.zmi.IFolderSettings"
-  ...           />
-  ...
-  ...     </configure>''', context)
-
-The sub-group is displayed inside the parent group as a form:
-
-  >>> browser.reload()
-  >>> browser.getControl('Shown Fields').displayOptions
-  ['name', 'size', 'creator']
-  >>> browser.getControl('Shown Fields').displayValue
-  ['name', 'size']
-  >>> browser.getControl('Shown Fields').displayValue = ['size', 'creator']
-  >>> browser.getControl('Sorted By').displayOptions
-  ['name', 'size', 'creator']
-  >>> browser.getControl('Sorted By').displayValue = ['creator']
-
-Selecing `Change` persists these values, too:
-
-  >>> browser.getControl('Change').click()
-  >>> browser.getControl('Shown Fields').displayValue
-  ['size', 'creator']
-  >>> browser.getControl('Sorted By').displayValue
-  ['creator']



More information about the checkins mailing list