zope.preference-3.8.0/000755 000766 000024 00000000000 11404670644 014526 5ustar00macstaff000000 000000 zope.preference-3.8.0/CHANGES.txt000644 000766 000024 00000000411 11404670553 016332 0ustar00macstaff000000 000000 ======= CHANGES ======= 3.8.0 (2010-06-12) ------------------ - Split out from `zope.app.preference`. - Removed dependency on `zope.app.component.hooks` by using `zope.component.hooks`. - Removed dependency on `zope.app.container` by using `zope.container`.zope.preference-3.8.0/COPYRIGHT.txt000644 000766 000024 00000000040 11404670553 016630 0ustar00macstaff000000 000000 Zope Foundation and Contributorszope.preference-3.8.0/LICENSE.txt000644 000766 000024 00000004026 11404670553 016352 0ustar00macstaff000000 000000 Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.preference-3.8.0/PKG-INFO000644 000766 000024 00000046467 11404670644 015644 0ustar00macstaff000000 000000 Metadata-Version: 1.0 Name: zope.preference Version: 3.8.0 Summary: User Preferences Framework Home-page: http://pypi.python.org/pypi/zope.preference Author: Zope Corporation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: This package provides and API to create and maintain hierarchical user preferences. Preferences can be easily created by defining schemas. .. contents:: ================ 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.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.component.hooks >>> zope.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__ >>> 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.component import provideAdapter >>> provideAdapter(PrincipalAnnotations, ... (Principal, zope.interface.Interface), IAnnotations) 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.preference import interfaces >>> from zope.component import provideUtility >>> provideUtility(settings, interfaces.IPreferenceGroup, ... name='ZMISettings') >>> provideUtility(folderSettings, interfaces.IPreferenceGroup, ... name='ZMISettings.Folder') If we now try to lookup the sub-group again, we should be successful: >>> settings.Folder #doctest:+ELLIPSIS >>> settings['Folder'] #doctest:+ELLIPSIS 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.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.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.preference >>> context = xmlconfig.file('meta.zcml', zope.preference) Then the system sets up a root preference group: >>> context = xmlconfig.string(''' ... ... ... ... ... ''', 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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.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 >>> provideAdapter(UserPreferences, [ILocation], interfaces.IUserPreferences) 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 >>> provideAdapter(preference.preferencesNamespace, [None], ... zope.traversing.interfaces.ITraversable, ... '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 ======= CHANGES ======= 3.8.0 (2010-06-12) ------------------ - Split out from `zope.app.preference`. - Removed dependency on `zope.app.component.hooks` by using `zope.component.hooks`. - Removed dependency on `zope.app.container` by using `zope.container`. Keywords: bluebream zope zope3 user preference Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.preference-3.8.0/README.txt000644 000766 000024 00000000213 11404670553 016217 0ustar00macstaff000000 000000 This package provides and API to create and maintain hierarchical user preferences. Preferences can be easily created by defining schemas. zope.preference-3.8.0/bootstrap.py000644 000766 000024 00000003372 11404670553 017121 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. $Id: bootstrap.py 113358 2010-06-11 17:17:44Z icemac $ """ import os, shutil, sys, tempfile, urllib2 tmpeggs = tempfile.mkdtemp() ez = {} exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) import pkg_resources cmd = 'from setuptools.command.easy_install import main; main()' if sys.platform == 'win32': cmd = '"%s"' % cmd # work around spawn lamosity on windows ws = pkg_resources.working_set assert os.spawnle( os.P_WAIT, sys.executable, sys.executable, '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout', dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse('setuptools')).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout') import zc.buildout.buildout zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap']) shutil.rmtree(tmpeggs) zope.preference-3.8.0/buildout.cfg000644 000766 000024 00000000333 11404670553 017034 0ustar00macstaff000000 000000 [buildout] develop = . parts = test importchecker [test] recipe = zc.recipe.testrunner eggs = zope.preference [test] [importchecker] recipe = zc.recipe.egg eggs = importchecker arguments = "${buildout:directory}/src" zope.preference-3.8.0/setup.cfg000644 000766 000024 00000000073 11404670644 016347 0ustar00macstaff000000 000000 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.preference-3.8.0/setup.py000644 000766 000024 00000005406 11404670553 016244 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.preference package $Id: setup.py 113382 2010-06-12 11:33:32Z icemac $ """ import os from setuptools import setup, find_packages def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() setup(name = 'zope.preference', version='3.8.0', author='Zope Corporation and Contributors', author_email='zope-dev@zope.org', description='User Preferences Framework', long_description=( read('README.txt') + '\n\n' + '.. contents::\n\n' + read('src', 'zope', 'preference', 'README.txt') + '\n\n' + read('CHANGES.txt') ), keywords = "bluebream zope zope3 user preference", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope3'], url='http://pypi.python.org/pypi/zope.preference', license='ZPL 2.1', packages=find_packages('src'), package_dir = {'': 'src'}, namespace_packages=['zope'], extras_require=dict(test=[ 'zope.app.testing', ]), install_requires = ['setuptools', 'ZODB3', 'zope.annotation', 'zope.component >= 3.8.0', 'zope.container', 'zope.schema', 'zope.security', 'zope.traversing', ], include_package_data = True, zip_safe = False, ) zope.preference-3.8.0/src/000755 000766 000024 00000000000 11404670644 015315 5ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope/000755 000766 000024 00000000000 11404670644 016272 5ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope.preference.egg-info/000755 000766 000024 00000000000 11404670644 022101 5ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope.preference.egg-info/PKG-INFO000644 000766 000024 00000046467 11404670642 023215 0ustar00macstaff000000 000000 Metadata-Version: 1.0 Name: zope.preference Version: 3.8.0 Summary: User Preferences Framework Home-page: http://pypi.python.org/pypi/zope.preference Author: Zope Corporation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: This package provides and API to create and maintain hierarchical user preferences. Preferences can be easily created by defining schemas. .. contents:: ================ 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.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.component.hooks >>> zope.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__ >>> 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.component import provideAdapter >>> provideAdapter(PrincipalAnnotations, ... (Principal, zope.interface.Interface), IAnnotations) 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.preference import interfaces >>> from zope.component import provideUtility >>> provideUtility(settings, interfaces.IPreferenceGroup, ... name='ZMISettings') >>> provideUtility(folderSettings, interfaces.IPreferenceGroup, ... name='ZMISettings.Folder') If we now try to lookup the sub-group again, we should be successful: >>> settings.Folder #doctest:+ELLIPSIS >>> settings['Folder'] #doctest:+ELLIPSIS 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.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.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.preference >>> context = xmlconfig.file('meta.zcml', zope.preference) Then the system sets up a root preference group: >>> context = xmlconfig.string(''' ... ... ... ... ... ''', 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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.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 >>> provideAdapter(UserPreferences, [ILocation], interfaces.IUserPreferences) 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 >>> provideAdapter(preference.preferencesNamespace, [None], ... zope.traversing.interfaces.ITraversable, ... '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 ======= CHANGES ======= 3.8.0 (2010-06-12) ------------------ - Split out from `zope.app.preference`. - Removed dependency on `zope.app.component.hooks` by using `zope.component.hooks`. - Removed dependency on `zope.app.container` by using `zope.container`. Keywords: bluebream zope zope3 user preference Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.preference-3.8.0/src/zope.preference.egg-info/SOURCES.txt000644 000766 000024 00000001347 11404670642 023770 0ustar00macstaff000000 000000 CHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/zope/__init__.py src/zope.preference.egg-info/PKG-INFO src/zope.preference.egg-info/SOURCES.txt src/zope.preference.egg-info/dependency_links.txt src/zope.preference.egg-info/namespace_packages.txt src/zope.preference.egg-info/not-zip-safe src/zope.preference.egg-info/requires.txt src/zope.preference.egg-info/top_level.txt src/zope/preference/README.txt src/zope/preference/__init__.py src/zope/preference/configure.zcml src/zope/preference/default.py src/zope/preference/interfaces.py src/zope/preference/meta.zcml src/zope/preference/metaconfigure.py src/zope/preference/metadirectives.py src/zope/preference/preference.py src/zope/preference/tests.pyzope.preference-3.8.0/src/zope.preference.egg-info/dependency_links.txt000644 000766 000024 00000000001 11404670642 026145 0ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope.preference.egg-info/namespace_packages.txt000644 000766 000024 00000000005 11404670642 026425 0ustar00macstaff000000 000000 zope zope.preference-3.8.0/src/zope.preference.egg-info/not-zip-safe000644 000766 000024 00000000001 11404670554 024327 0ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope.preference.egg-info/requires.txt000644 000766 000024 00000000212 11404670642 024472 0ustar00macstaff000000 000000 setuptools ZODB3 zope.annotation zope.component >= 3.8.0 zope.container zope.schema zope.security zope.traversing [test] zope.app.testingzope.preference-3.8.0/src/zope.preference.egg-info/top_level.txt000644 000766 000024 00000000005 11404670642 024624 0ustar00macstaff000000 000000 zope zope.preference-3.8.0/src/zope/__init__.py000644 000766 000024 00000000070 11404670553 020377 0ustar00macstaff000000 000000 __import__('pkg_resources').declare_namespace(__name__) zope.preference-3.8.0/src/zope/preference/000755 000766 000024 00000000000 11404670644 020410 5ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope/preference/README.txt000644 000766 000024 00000035377 11404670553 022124 0ustar00macstaff000000 000000 ================ 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.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.component.hooks >>> zope.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__ >>> 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.component import provideAdapter >>> provideAdapter(PrincipalAnnotations, ... (Principal, zope.interface.Interface), IAnnotations) 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.preference import interfaces >>> from zope.component import provideUtility >>> provideUtility(settings, interfaces.IPreferenceGroup, ... name='ZMISettings') >>> provideUtility(folderSettings, interfaces.IPreferenceGroup, ... name='ZMISettings.Folder') If we now try to lookup the sub-group again, we should be successful: >>> settings.Folder #doctest:+ELLIPSIS >>> settings['Folder'] #doctest:+ELLIPSIS 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.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.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.preference >>> context = xmlconfig.file('meta.zcml', zope.preference) Then the system sets up a root preference group: >>> context = xmlconfig.string(''' ... ... ... ... ... ''', 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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(''' ... ... ... ... ... ''', context) >>> prefs.ZMISettings2 #doctest:+ELLIPSIS >>> 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.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 >>> provideAdapter(UserPreferences, [ILocation], interfaces.IUserPreferences) 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 >>> provideAdapter(preference.preferencesNamespace, [None], ... zope.traversing.interfaces.ITraversable, ... '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 zope.preference-3.8.0/src/zope/preference/__init__.py000644 000766 000024 00000000111 11404670553 022511 0ustar00macstaff000000 000000 # Make a package from zope.preference.preference import UserPreferences zope.preference-3.8.0/src/zope/preference/configure.zcml000644 000766 000024 00000002735 11404670553 023266 0ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope/preference/default.py000644 000766 000024 00000010234 11404670553 022405 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Default Preferences Provider $Id: default.py 113372 2010-06-12 10:04:58Z icemac $ """ __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.component from zope.container.contained import Contained from zope.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.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) zope.preference-3.8.0/src/zope/preference/interfaces.py000644 000766 000024 00000005666 11404670553 023121 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """User Preferences Interfaces $Id: interfaces.py 113358 2010-06-11 17:17:44Z icemac $ """ __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") zope.preference-3.8.0/src/zope/preference/meta.zcml000644 000766 000024 00000000650 11404670553 022225 0ustar00macstaff000000 000000 zope.preference-3.8.0/src/zope/preference/metaconfigure.py000644 000766 000024 00000002315 11404670553 023612 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """This module handles the 'preference' namespace directives. $Id: metaconfigure.py 113366 2010-06-11 17:51:08Z icemac $ """ __docformat__ = 'restructuredtext' from zope.component.zcml import utility from zope.preference.interfaces import IPreferenceGroup from zope.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) zope.preference-3.8.0/src/zope/preference/metadirectives.py000644 000766 000024 00000003751 11404670553 023777 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """``apidoc:preferencesgroup`` ZCML directive interface $Id: metadirectives.py 113358 2010-06-11 17:17:44Z icemac $ """ __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 ) zope.preference-3.8.0/src/zope/preference/preference.py000644 000766 000024 00000021443 11404670553 023103 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """User Preferences System $Id: preference.py 113373 2010-06-12 10:09:05Z icemac $ """ __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 from zope.security.management import getInteraction from zope.traversing.interfaces import IContainmentRoot from zope.location import Location from zope.annotation.interfaces import IAnnotations import zope.component.hooks from zope.container.interfaces import IReadContainer from zope.preference.interfaces import IPreferenceGroup from zope.preference.interfaces import IPreferenceCategory from zope.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.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 zope.preference-3.8.0/src/zope/preference/tests.py000644 000766 000024 00000002266 11404670553 022131 0ustar00macstaff000000 000000 ############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Tests for the Preferences System $Id: tests.py 113366 2010-06-11 17:51:08Z icemac $ """ from zope.app.testing import setup from zope.component import testing import doctest def setUp(test): testing.setUp(test) setup.setUpTestAsModule(test, 'zope.preference.README') def tearDown(test): testing.tearDown(test) setup.tearDownTestAsModule(test) def test_suite(): return doctest.DocFileSuite('README.txt', setUp=setUp, tearDown=tearDown, optionflags=doctest.NORMALIZE_WHITESPACE)