{ "info": { "author": "Zope Corporation and Contributors", "author_email": "zope-dev@zope.org", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Zope3", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP" ], "description": "This package provides an API to create and maintain hierarchical user\npreferences. Preferences can be easily created by defining schemas.\n\n\n.. contents::\n\n==================\n User Preferences\n==================\n\nImplementing user preferences is usually a painful task, since it requires a\nlot of custom coding and constantly changing preferences makes it hard to\nmaintain the data and UI. The `preference` package\n\n >>> from zope.preference import preference\n\neases this pain by providing a generic user preferences framework that uses\nschemas to categorize and describe the preferences.\n\nPreference Groups\n=================\n\nPreferences are grouped in preference groups and the preferences inside a\ngroup are specified via the preferences group schema:\n\n >>> import zope.interface\n >>> import zope.schema\n >>> class IZMIUserSettings(zope.interface.Interface):\n ... \"\"\"Basic User Preferences\"\"\"\n ...\n ... email = zope.schema.TextLine(\n ... title=u\"E-mail Address\",\n ... description=u\"E-mail Address used to send notifications\")\n ...\n ... skin = zope.schema.Choice(\n ... title=u\"Skin\",\n ... description=u\"The skin that should be used for the ZMI.\",\n ... values=['Rotterdam', 'ZopeTop', 'Basic'],\n ... default='Rotterdam')\n ...\n ... showZopeLogo = zope.schema.Bool(\n ... title=u\"Show Zope Logo\",\n ... description=u\"Specifies whether Zope logo should be displayed \"\n ... u\"at the top of the screen.\",\n ... default=True)\n\nNow we can instantiate the preference group. Each preference group must have an\nID by which it can be accessed and optional title and description fields for UI\npurposes:\n\n >>> settings = preference.PreferenceGroup(\n ... \"ZMISettings\",\n ... schema=IZMIUserSettings,\n ... title=u\"ZMI User Settings\",\n ... description=u\"\")\n\nNote that the preferences group provides the interface it is representing:\n\n >>> IZMIUserSettings.providedBy(settings)\n True\n\nand the id, schema and title of the group are directly available:\n\n >>> settings.__id__\n 'ZMISettings'\n >>> settings.__schema__\n \n >>> settings.__title__\n u'ZMI User Settings'\n\nSo let's ask the preference group for the `skin` setting:\n\n >>> settings.skin #doctest:+ELLIPSIS\n Traceback (most recent call last):\n ...\n zope.security.interfaces.NoInteraction\n\n\nSo why did the lookup fail? Because we have not specified a principal yet, for\nwhich we want to lookup the preferences. To do that, we have to create a new\ninteraction:\n\n >>> class Principal:\n ... def __init__(self, id):\n ... self.id = id\n >>> principal = Principal('zope.user')\n\n >>> class Participation:\n ... interaction = None\n ... def __init__(self, principal):\n ... self.principal = principal\n\n >>> participation = Participation(principal)\n\n >>> import zope.security.management\n >>> zope.security.management.newInteraction(participation)\n\nWe also need an IAnnotations adapter for principals, so we can store the\nsettings:\n\n >>> from zope.annotation.interfaces import IAnnotations\n >>> @zope.interface.implementer(IAnnotations)\n ... class PrincipalAnnotations(dict):\n ... data = {}\n ... def __new__(class_, principal, context):\n ... try:\n ... annotations = class_.data[principal.id]\n ... except KeyError:\n ... annotations = dict.__new__(class_)\n ... class_.data[principal.id] = annotations\n ... return annotations\n ... def __init__(self, principal, context):\n ... pass\n\n >>> from zope.component import provideAdapter\n >>> provideAdapter(PrincipalAnnotations,\n ... (Principal, zope.interface.Interface), IAnnotations)\n\nLet's now try to access the settings again:\n\n >>> settings.skin\n 'Rotterdam'\n\nwhich is the default value, since we have not set it yet. We can now reassign\nthe value:\n\n >>> settings.skin = 'Basic'\n >>> settings.skin\n 'Basic'\n\nHowever, you cannot just enter any value, since it is validated before the\nassignment:\n\n >>> settings.skin = 'MySkin'\n Traceback (most recent call last):\n ...\n ConstraintNotSatisfied: MySkin\n\n\nPreference Group Trees\n======================\n\nThe preferences would not be very powerful, if you could create a full\npreferences. So let's create a sub-group for our ZMI user settings, where we\ncan adjust the look and feel of the folder contents view:\n\n >>> class IFolderSettings(zope.interface.Interface):\n ... \"\"\"Basic User Preferences\"\"\"\n ...\n ... shownFields = zope.schema.Set(\n ... title=u\"Shown Fields\",\n ... description=u\"Fields shown in the table.\",\n ... value_type=zope.schema.Choice(['name', 'size', 'creator']),\n ... default=set(['name', 'size']))\n ...\n ... sortedBy = zope.schema.Choice(\n ... title=u\"Sorted By\",\n ... description=u\"Data field to sort by.\",\n ... values=['name', 'size', 'creator'],\n ... default='name')\n\n >>> folderSettings = preference.PreferenceGroup(\n ... \"ZMISettings.Folder\",\n ... schema=IFolderSettings,\n ... title=u\"Folder Content View Settings\")\n\nNote that the id was chosen so that the parent id is the prefix of the child's\nid. Our new preference sub-group should now be available as an attribute or an\nitem on the parent group ...\n\n >>> settings.Folder\n Traceback (most recent call last):\n ...\n AttributeError: 'Folder' is not a preference or sub-group.\n >>> settings['Folder']\n Traceback (most recent call last):\n ...\n KeyError: 'Folder'\n\nbut not before we register the groups as utilities:\n\n >>> from zope.preference import interfaces\n >>> from zope.component import provideUtility\n\n >>> provideUtility(settings, interfaces.IPreferenceGroup,\n ... name='ZMISettings')\n >>> provideUtility(folderSettings, interfaces.IPreferenceGroup,\n ... name='ZMISettings.Folder')\n\nIf we now try to lookup the sub-group again, we should be successful:\n\n >>> settings.Folder #doctest:+ELLIPSIS\n \n\n >>> settings['Folder'] #doctest:+ELLIPSIS\n \n >>> 'Folder' in settings\n True\n >>> list(settings)\n []\n\nWhile the registry of the preference groups is flat, the careful naming of the\nids allows us to have a tree of preferences. Note that this pattern is very\nsimilar to the way modules are handled in Python; they are stored in a flat\ndictionary in ``sys.modules``, but due to the naming they appear to be in a\nnamespace tree.\n\nWhile we are at it, there are also preference categories that can be compared\nto Python packages. They basically are just a higher level grouping concept\nthat is used by the UI to better organize the preferences. A preference group\ncan be converted to a category by simply providing an additional interface:\n\n >>> zope.interface.alsoProvides(folderSettings, interfaces.IPreferenceCategory)\n\n >>> interfaces.IPreferenceCategory.providedBy(folderSettings)\n True\n\nPreference group objects can also hold arbitrary attributes, but since\nthey're not persistent this must be used with care:\n\n >>> settings.not_in_schema = 1\n >>> settings.not_in_schema\n 1\n >>> del settings.not_in_schema\n >>> settings.not_in_schema\n Traceback (most recent call last):\n ...\n AttributeError: 'not_in_schema' is not a preference or sub-group.\n\nDefault Preferences\n===================\n\nIt is sometimes desirable to define default settings on a site-by-site\nbasis, instead of just using the default value from the schema. The\npreferences package provides a module that implements a default\npreferences provider that can be added as a unnamed utility for each\nsite:\n\n >>> from zope.preference import default\n\nWe'll begin by creating a new root site:\n\n >>> from zope.site.folder import rootFolder\n >>> root = rootFolder()\n >>> from zope.site.site import LocalSiteManager\n >>> rsm = LocalSiteManager(root)\n >>> root.setSiteManager(rsm)\n\nAnd we'll make the new site the current site:\n\n >>> zope.component.hooks.setSite(root)\n\nNow we can register the default preference provider with the root site:\n\n >>> provider = addUtility(\n ... rsm, default.DefaultPreferenceProvider(),\n ... interfaces.IDefaultPreferenceProvider)\n\nSo before we set an explicit default value for a preference, the schema field\ndefault is used:\n\n >>> settings.Folder.sortedBy\n 'name'\n\nBut if we now set a new default value with the provider,\n\n >>> defaultFolder = provider.getDefaultPreferenceGroup('ZMISettings.Folder')\n >>> defaultFolder.sortedBy = 'size'\n\nthen the default of the setting changes:\n\n >>> settings.Folder.sortedBy\n 'size'\n\nBecause the ``ZMISettings.Folder`` was declared as a preference\ncategory, the default implementation is too:\n\n >>> interfaces.IPreferenceCategory.providedBy(defaultFolder)\n True\n\nThe default preference providers also implicitly acquire default\nvalues from parent sites. So if we add a new child folder called\n``folder1``, make it a site and set it as the active site:\n\n >>> from zope.site.folder import Folder\n >>> root[u'folder1'] = Folder()\n >>> folder1 = root['folder1']\n\n >>> from zope.site.site import LocalSiteManager\n >>> sm1 = LocalSiteManager(folder1)\n >>> folder1.setSiteManager(sm1)\n >>> zope.component.hooks.setSite(folder1)\n\nand add a default provider there,\n\n >>> provider1 = addUtility(\n ... sm1, default.DefaultPreferenceProvider(),\n ... interfaces.IDefaultPreferenceProvider)\n\nthen we still get the root's default values, because we have not defined any\nin the higher default provider:\n\n >>> settings.Folder.sortedBy\n 'size'\n\nBut if we provide the new provider with a default value for `sortedBy`,\n\n >>> defaultFolder1 = provider1.getDefaultPreferenceGroup('ZMISettings.Folder')\n >>> defaultFolder1.sortedBy = 'creator'\n\nthen it is used instead:\n\n >>> settings.Folder.sortedBy\n 'creator'\n\nOf course, once the root site becomes our active site again\n\n >>> zope.component.hooks.setSite(root)\n\nthe default value of the root provider is used:\n\n >>> settings.Folder.sortedBy\n 'size'\n\nOf course, all the defaults in the world are not relevant anymore as soon as\nthe user actually provides a value:\n\n >>> settings.Folder.sortedBy = 'name'\n >>> settings.Folder.sortedBy\n 'name'\n\nOh, and have I mentioned that entered values are always validated? So you\ncannot just assign any old value:\n\n >>> settings.Folder.sortedBy = 'foo'\n Traceback (most recent call last):\n ...\n ConstraintNotSatisfied: foo\n\nFinally, if the user deletes his/her explicit setting, we are back to the\ndefault value:\n\n >>> del settings.Folder.sortedBy\n >>> settings.Folder.sortedBy\n 'size'\n\nJust as with regular preference groups, the default preference groups\nare arranged in a matching hierarchy:\n\n >>> defaultSettings = provider.getDefaultPreferenceGroup('ZMISettings')\n >>> defaultSettings.get('Folder')\n \n >>> defaultSettings.Folder\n \n\nThey also report useful AttributeErrors for bad accesses:\n\n >>> defaultSettings.not_in_schema\n Traceback (most recent call last):\n ...\n AttributeError: 'not_in_schema' is not a preference or sub-group.\n\n\nCreating Preference Groups Using ZCML\n=====================================\n\nIf you are using the user preference system in Zope 3, you will not have to\nmanually setup the preference groups as we did above (of course). We will use\nZCML instead. First, we need to register the directives:\n\n >>> from zope.configuration import xmlconfig\n >>> import zope.preference\n >>> context = xmlconfig.file('meta.zcml', zope.preference)\n\nThen the system sets up a root preference group:\n\n >>> context = xmlconfig.string('''\n ... \n ...\n ... \n ...\n ... ''', context)\n\nNow we can use the preference system in its intended way. We access the folder\nsettings as follows:\n\n >>> import zope.component\n >>> prefs = zope.component.getUtility(interfaces.IPreferenceGroup)\n >>> prefs.ZMISettings.Folder.sortedBy\n 'size'\n\nLet's register the ZMI settings again under a new name via ZCML:\n\n >>> context = xmlconfig.string('''\n ... \n ...\n ... \n ...\n ... ''', context)\n\n >>> prefs.ZMISettings2 #doctest:+ELLIPSIS\n \n\n >>> prefs.ZMISettings2.__title__\n u'ZMI Settings NG'\n\n >>> IZMIUserSettings.providedBy(prefs.ZMISettings2)\n True\n >>> interfaces.IPreferenceCategory.providedBy(prefs.ZMISettings2)\n True\n\nAnd the tree can built again by carefully constructing the id:\n\n >>> context = xmlconfig.string('''\n ... \n ...\n ... \n ...\n ... ''', context)\n\n >>> prefs.ZMISettings2 #doctest:+ELLIPSIS\n \n\n >>> prefs.ZMISettings2.Folder.__title__\n u'Folder Settings'\n\n >>> IFolderSettings.providedBy(prefs.ZMISettings2.Folder)\n True\n >>> interfaces.IPreferenceCategory.providedBy(prefs.ZMISettings2.Folder)\n False\n\n\nSimple Python-Level Access\n==========================\n\nIf a site is set, getting the user preferences is very simple:\n\n >>> from zope.preference import UserPreferences\n >>> prefs2 = UserPreferences()\n >>> prefs2.ZMISettings.Folder.sortedBy\n 'size'\n\nThis function is also commonly registered as an adapter,\n\n >>> from zope.location.interfaces import ILocation\n >>> provideAdapter(UserPreferences, [ILocation], interfaces.IUserPreferences)\n\nso that you can adapt any location to the user preferences:\n\n >>> prefs3 = interfaces.IUserPreferences(folder1)\n >>> prefs3.ZMISettings.Folder.sortedBy\n 'creator'\n\n\nTraversal\n=========\n\nOkay, so all these objects are nice, but they do not make it any easier to\naccess the preferences in page templates. Thus, a special traversal namespace\nhas been created that makes it very simple to access the preferences via a\ntraversal path. But before we can use the path expressions, we have to\nregister all necessary traversal components and the special `preferences`\nnamespace:\n\n >>> import zope.traversing.interfaces\n >>> provideAdapter(preference.preferencesNamespace, [None],\n ... zope.traversing.interfaces.ITraversable,\n ... 'preferences')\n\nWe can now access the preferences as follows:\n\n >>> from zope.traversing.api import traverse\n >>> traverse(None, '++preferences++ZMISettings/skin')\n 'Basic'\n >>> traverse(None, '++preferences++/ZMISettings/skin')\n 'Basic'\n\n\nSecurity\n========\n\nYou might already wonder under which permissions the preferences are\navailable. They are actually available publicly (`CheckerPublic`), but that\nis not a problem, since the available values are looked up specifically for\nthe current user. And why should a user not have full access to his/her\npreferences?\n\nLet's create a checker using the function that the security machinery is\nactually using:\n\n >>> checker = preference.PreferenceGroupChecker(settings)\n >>> checker.permission_id('skin')\n Global(CheckerPublic,zope.security.checker)\n >>> checker.setattr_permission_id('skin')\n Global(CheckerPublic,zope.security.checker)\n\nThe id, title, description, and schema are publicly available for access,\nbut are not available for mutation at all:\n\n >>> checker.permission_id('__id__')\n Global(CheckerPublic,zope.security.checker)\n >>> checker.setattr_permission_id('__id__') is None\n True\n\n\nThe only way security could be compromised is when one could override the\nannotations property. However, this property is not available for public\nconsumption at all, including read access:\n\n >>> checker.permission_id('annotation') is None\n True\n >>> checker.setattr_permission_id('annotation') is None\n True\n\n\n=========\n CHANGES\n=========\n\n4.1.0 (2018-09-27)\n==================\n\n- Support newer zope.configuration and persistent. See `issue 2\n `_.\n\n- Add support for Python 3.7 and PyPy3.\n\n- Drop support for Python 3.3.\n\n4.0.0 (2017-05-09)\n==================\n\n- Add support for Python 3.4, 3.5 and 3.6.\n\n- Add support for PyPy.\n\n- Drop support for Python 2.6.\n\n\n4.0.0a1 (2013-02-24)\n====================\n\n- Added support for Python 3.3.\n\n- Replaced deprecated ``zope.interface.implements`` usage with equivalent\n ``zope.interface.implementer`` decorator.\n\n- Dropped support for Python 2.4 and 2.5.\n\n- Refactored tests not to rely on ``zope.app.testing`` anymore.\n\n- Fixed a bug while accessing the parent of a preference group.\n\n\n3.8.0 (2010-06-12)\n==================\n\n- Split out from `zope.app.preference`.\n\n- Removed dependency on `zope.app.component.hooks` by using\n `zope.component.hooks`.\n\n- Removed dependency on `zope.app.container` by using\n `zope.container`.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/zopefoundation/zope.preference", "keywords": "bluebream zope zope3 user preference", "license": "ZPL 2.1", "maintainer": "", "maintainer_email": "", "name": "zope.preference", "package_url": "https://pypi.org/project/zope.preference/", "platform": "", "project_url": "https://pypi.org/project/zope.preference/", "project_urls": { "Homepage": "http://github.com/zopefoundation/zope.preference" }, "release_url": "https://pypi.org/project/zope.preference/4.1.0/", "requires_dist": [ "setuptools", "BTrees", "zope.annotation", "zope.component (>=3.8.0)", "zope.container", "zope.schema", "zope.security", "zope.traversing", "zope.security; extra == 'test'", "zope.site; extra == 'test'", "zope.testing; extra == 'test'", "zope.testrunner; extra == 'test'", "zope.security; extra == 'zcml'" ], "requires_python": "", "summary": "User Preferences Framework", "version": "4.1.0" }, "last_serial": 4315860, "releases": { "3.8.0": [ { "comment_text": "", "digests": { "md5": "bb8b1c9f65387a51be429407528cc453", "sha256": "640fdd16bdbfb2eb1c20370c01aa4e9648eac84448ed6356b7ae6abe115fc829" }, "downloads": -1, "filename": "zope.preference-3.8.0.tar.gz", "has_sig": false, "md5_digest": "bb8b1c9f65387a51be429407528cc453", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14578, "upload_time": "2010-06-12T13:38:13", "url": "https://files.pythonhosted.org/packages/5c/23/3cd5b7334ce1d992d7ebb5e5f6bbbf410ecedf757c51b1949c92d6721a7c/zope.preference-3.8.0.tar.gz" } ], "4.0.0": [ { "comment_text": "", "digests": { "md5": "901a19913721828f2393ecc03b47f12d", "sha256": "509117eebafce9409216ee8c2c3ca7ca41afac29c630f190f2e1017bfaefabe5" }, "downloads": -1, "filename": "zope.preference-4.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "901a19913721828f2393ecc03b47f12d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 28497, "upload_time": "2017-05-09T16:19:50", "url": "https://files.pythonhosted.org/packages/9f/a6/8d5c32dc07cd843ff30da8e174e9e6546210fba266fa26871ba42759cc27/zope.preference-4.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fb61d39a154f16e3ace8525e82016d78", "sha256": "ebfc6238cb0bf77f08f31d2e7a8c5de938bea7030b3c88ab3da55ffea3ee3aa8" }, "downloads": -1, "filename": "zope.preference-4.0.0.tar.gz", "has_sig": false, "md5_digest": "fb61d39a154f16e3ace8525e82016d78", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23170, "upload_time": "2017-05-09T16:19:51", "url": "https://files.pythonhosted.org/packages/3c/34/6b5580d2dfa7f186f39c3db0902e5480fcf7703582affda7486f87354e18/zope.preference-4.0.0.tar.gz" } ], "4.0.0a1": [ { "comment_text": "", "digests": { "md5": "6b7d5fd1c4399290211e65cf26172a61", "sha256": "2151381fca8952c156c50369220ac3080db6ebf8dd5ce6791fa5ed569bdc93fe" }, "downloads": -1, "filename": "zope.preference-4.0.0a1.zip", "has_sig": false, "md5_digest": "6b7d5fd1c4399290211e65cf26172a61", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34200, "upload_time": "2013-02-25T03:04:35", "url": "https://files.pythonhosted.org/packages/43/ff/71dcd8078f0d8954228cd6941814d6e7d8daf3b69671bb7774eacfcf04a9/zope.preference-4.0.0a1.zip" } ], "4.1.0": [ { "comment_text": "", "digests": { "md5": "2efe632b806def22338f68d703595c6e", "sha256": "c833683d90a213e445d2d1263c9ba9935a53088b464dd1dac4ce93cc4d48a0ed" }, "downloads": -1, "filename": "zope.preference-4.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2efe632b806def22338f68d703595c6e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 22593, "upload_time": "2018-09-27T11:16:40", "url": "https://files.pythonhosted.org/packages/38/1a/5541055a7004dab6e8cb1486b642009cbae37ad8ff728761faf7a44ffc54/zope.preference-4.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8de9a585d948cc44ad6e5b9522fa5bea", "sha256": "d0249ff28297b47718ca92ecf6025536e5b631c8b33b28e16f73cb8cec1f5396" }, "downloads": -1, "filename": "zope.preference-4.1.0.tar.gz", "has_sig": false, "md5_digest": "8de9a585d948cc44ad6e5b9522fa5bea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25995, "upload_time": "2018-09-27T11:16:42", "url": "https://files.pythonhosted.org/packages/68/5c/d1df0ff39070f2e61590c076334d564467542f85aac260b4a41c9e758871/zope.preference-4.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2efe632b806def22338f68d703595c6e", "sha256": "c833683d90a213e445d2d1263c9ba9935a53088b464dd1dac4ce93cc4d48a0ed" }, "downloads": -1, "filename": "zope.preference-4.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2efe632b806def22338f68d703595c6e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 22593, "upload_time": "2018-09-27T11:16:40", "url": "https://files.pythonhosted.org/packages/38/1a/5541055a7004dab6e8cb1486b642009cbae37ad8ff728761faf7a44ffc54/zope.preference-4.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8de9a585d948cc44ad6e5b9522fa5bea", "sha256": "d0249ff28297b47718ca92ecf6025536e5b631c8b33b28e16f73cb8cec1f5396" }, "downloads": -1, "filename": "zope.preference-4.1.0.tar.gz", "has_sig": false, "md5_digest": "8de9a585d948cc44ad6e5b9522fa5bea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25995, "upload_time": "2018-09-27T11:16:42", "url": "https://files.pythonhosted.org/packages/68/5c/d1df0ff39070f2e61590c076334d564467542f85aac260b4a41c9e758871/zope.preference-4.1.0.tar.gz" } ] }