[Checkins] SVN: z3ext.preferences/trunk/ - 'schema' field for z3ext:preferenceGroup is required now

Nikolay Kim fafhrd at datacom.kz
Thu Apr 17 04:37:23 EDT 2008


Log message for revision 85451:
  - 'schema' field for z3ext:preferenceGroup is required now
  
  - Added adapter for IPrincipal to 'schema' with automatic binding
  
  
  

Changed:
  U   z3ext.preferences/trunk/CHANGES.txt
  U   z3ext.preferences/trunk/setup.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/README.txt
  U   z3ext.preferences/trunk/src/z3ext/preferences/configure.zcml
  U   z3ext.preferences/trunk/src/z3ext/preferences/interfaces.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/preference.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/preferencetype.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/root.py
  A   z3ext.preferences/trunk/src/z3ext/preferences/storage.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/tests.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/utils.py
  U   z3ext.preferences/trunk/src/z3ext/preferences/zcml.py

-=-
Modified: z3ext.preferences/trunk/CHANGES.txt
===================================================================
--- z3ext.preferences/trunk/CHANGES.txt	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/CHANGES.txt	2008-04-17 08:37:22 UTC (rev 85451)
@@ -2,6 +2,14 @@
 CHANGES
 =======
 
+1.2.0 (2008-04-18)
+------------------
+
+- 'schema' field for z3ext:preferenceGroup is required now
+
+- Added adapter for IPrincipal to 'schema' with automatic binding
+
+
 1.1.1 (2008-04-15)
 ------------------
 
@@ -51,4 +59,4 @@
 0.9.0 (2008-02-01)
 ------------------
 
-- Initial release.
+- Initial release

Modified: z3ext.preferences/trunk/setup.py
===================================================================
--- z3ext.preferences/trunk/setup.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/setup.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -21,7 +21,7 @@
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
-version='1.1.2dev'
+version='1.2.0dev'
 
 
 setup(name = 'z3ext.preferences',

Modified: z3ext.preferences/trunk/src/z3ext/preferences/README.txt
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/README.txt	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/README.txt	2008-04-17 08:37:22 UTC (rev 85451)
@@ -59,6 +59,11 @@
   ...     IZMIUserSettings,
   ...     title=u"ZMI User Settings", description=u"")
 
+We should set 'preferenceID' to schema with preference group id,
+This value is used by data storage to store data in annotations
+
+  >>> IZMIUserSettings.setTaggedValue('preferenceID', 'ZMISettings')
+
 Now we can instantiate the preference group.
 
   >>> settings = settingsClass()
@@ -68,7 +73,7 @@
   >>> settings.__schema__ = IZMIUserSettings
   Traceback (most recent call last):
   ...
-  AttributeError: Can't set __schema__
+  AttributeError: Can't change __schema__
 
 
 Note that the preferences group provides the interface it is representing:
@@ -118,20 +123,25 @@
   >>> class PrincipalAnnotations(dict):
   ...     zope.interface.implements(IAnnotations)
   ...     data = {}
-  ...     def __new__(class_, principal, context):
+  ...     def __new__(class_, principal):
   ...         try:
   ...             annotations = class_.data[principal.id]
   ...         except KeyError:
   ...             annotations = dict.__new__(class_)
   ...             class_.data[principal.id] = annotations
   ...         return annotations
-  ...     def __init__(self, principal, context):
+  ...     def __init__(self, principal):
   ...         pass
 
   >>> from zope import component
+  >>> component.provideAdapter(PrincipalAnnotations, (Principal,), IAnnotations)
+
+Also we need IDataStorage for preferenceGroup schema, because preference group
+doesn't use IAnnotations directly.
+
+  >>> from z3ext.preferences import storage
   >>> component.provideAdapter(
-  ...     PrincipalAnnotations,
-  ...     (Principal, zope.interface.Interface), IAnnotations)
+  ...     storage.getDefaultStorage, (Principal, zope.interface.Interface))
 
 And now we need bind preferences to principal. We can just call __bind__
 method in this case preference will use principal from current interaction.
@@ -146,7 +156,7 @@
 Or we can explicitly set principal, this is usefull when we want to know
 preferences for principal.
 
-  >>> settings = settings.__bind__(principal=principal)
+  >>> settings = settings.__bind__(principal)
   >>> interfaces.IBound.providedBy(nsettings)
   True
 
@@ -199,6 +209,8 @@
   ...     IFolderSettings,
   ...     title=u"Folder Content View Settings")
 
+  >>> IFolderSettings.setTaggedValue('preferenceID', 'ZMISettings.Folder')
+
   >>> folderSettings = folderSettingsClass()
 
 Note that the id was chosen so that the parent id is the prefix of the child's
@@ -284,7 +296,7 @@
   ...     id="ZMISettings"
   ...     schema="z3ext.preferences.README.IZMIUserSettings"
   ...     title="ZMI User Settings" />
-  ... 
+  ...
   ...   <z3ext:preferenceGroup
   ...     id="ZMISettings.Folder"
   ...     schema="z3ext.preferences.README.IFolderSettings"
@@ -296,10 +308,24 @@
 settings as follows:
 
   >>> prefs = component.getUtility(interfaces.IPreferenceGroup)
+  >>> prefs.isAvailable()
+  False
 
+  >>> prefs['ZMISettings']['Folder'].isAvailable()
+  False
+
+
 Don't forget to bind preferences to principal
 
-  >>> prefs = prefs.__bind__(principal=principal)
+  >>> prefs = prefs.__bind__(principal)
+  >>> prefs.isAvailable()
+  True
+
+  >>> p = prefs['ZMISettings']['Folder'].__bind__(parent=prefs)
+
+  >>> prefs['ZMISettings']['Folder'].isAvailable()
+  True
+
   >>> prefs['ZMISettings']['Folder'].sortedBy
   'size'
 
@@ -324,6 +350,11 @@
 
 Let's register the ZMI settings again under a new name via ZCML:
 
+  >>> class IZMIUserSettings2(IZMIUserSettings):
+  ...     pass
+
+  >>> IZMIUserSettings2.setTaggedValue('preferenceID', 'ZMISettings.Folder')
+
   >>> context = xmlconfig.string('''
   ... <configure
   ...   xmlns:z3ext="http://namespaces.zope.org/z3ext"
@@ -411,17 +442,18 @@
   >>> new_prefs.isAvailable()
   True
 
+  >>> prefs['ZMISettings2'].remove('Folder10')
 
+
 Simple Python-Level Access
 --------------------------
 
 If a site is set, getting the user preferences is very simple:
 
-  >>> prefs2 = component.getUtility(interfaces.IPreferenceGroup, 'ZMISettings.Folder')
-  >>> prefs2 = prefs2.__bind__(principal=principal)
+  >>> prefs2 = IFolderSettings(principal)
 
   >>> prefs2.sortedBy
-  'size'
+  'name'
 
 
 Security
@@ -451,8 +483,9 @@
   ...      schema="z3ext.preferences.README.IZMIUserSettings"
   ...      provides="z3ext.preferences.interfaces.IPreferenceCategory"
   ...      permission="zope.View">
+  ...    <allow attributes="email" />
   ...    <require
-  ...      attributes="showZopeLogo" permission="zope.Manage" />
+  ...      attributes="showZopeLogo" permission="zope.Manage" set_attributes="skin" />
   ...   </z3ext:preferenceGroup>
   ...
   ... </configure>''', context)

Modified: z3ext.preferences/trunk/src/z3ext/preferences/configure.zcml
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/configure.zcml	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/configure.zcml	2008-04-17 08:37:22 UTC (rev 85451)
@@ -17,6 +17,9 @@
      permission="z3ext.ModifyPreference"
      role="preference.Owner" />
 
+  <!-- default preference group storage -->
+  <adapter factory=".storage.getDefaultStorage" />
+
   <!-- root preference group -->
   <utility
      provides=".interfaces.IPreferenceGroup"
@@ -48,7 +51,7 @@
      id="portal"
      title="Portal preferences"
      description="These are all the preferences related to common portal settings."
-     provides="z3ext.preferences.interfaces.IPreferenceCategory" />
+     schema="z3ext.preferences.interfaces.IPortalPreferences" />
 
   <!-- browser views -->
   <include package=".browser" />

Modified: z3ext.preferences/trunk/src/z3ext/preferences/interfaces.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/interfaces.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/interfaces.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -19,7 +19,9 @@
 from zope.configuration import fields
 from zope.location.interfaces import ILocation
 
+ANNOTATION_KEY = 'zope.app.user.UserPreferences'
 
+
 class UnboundPreferenceGroup(Exception):
     """ Prefernce group is not bound to principal """
 
@@ -95,3 +97,11 @@
 
     def __bind__(principal=None, parent=None):
         """ bind preferences """
+
+
+class IDataStorage(interface.Interface):
+    """ data storage, set/get values as attributes """
+
+
+class IPortalPreferences(IPreferenceCategory):
+    """ portal preferences """

Modified: z3ext.preferences/trunk/src/z3ext/preferences/preference.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/preference.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/preference.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -15,22 +15,15 @@
 
 $Id$
 """
-from BTrees.OOBTree import OOBTree
-
 from zope import interface
-from zope.interface.common.mapping import IEnumerableMapping
-from zope.component import getGlobalSiteManager
-from zope.component import queryUtility, getMultiAdapter
 from zope.cachedescriptors.property import Lazy
+from zope.interface.common.mapping import IEnumerableMapping
 from zope.security.management import getInteraction
-from zope.annotation.interfaces import IAnnotations
-from zope.app.component.hooks import getSite
-from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
+from zope.component import queryUtility, getMultiAdapter, getGlobalSiteManager
 
-from interfaces import IPreferenceGroup, IBound, UnboundPreferenceGroup
+from interfaces import UnboundPreferenceGroup
+from interfaces import IPreferenceGroup, IBound, IDataStorage
 
-pref_key = 'zope.app.user.UserPreferences'
-
 _marker = object()
 
 
@@ -48,25 +41,15 @@
         clone = self.__class__.__new__(self.__class__)
         clone.__dict__.update(self.__dict__)
 
-        if parent is None:
-            parent = getSite()
-
+        # set parent
         clone.__parent__ = parent
 
+        # set principal
         if principal is None:
             if IBound.providedBy(parent):
                 clone.__principal__ = parent.__principal__
             else:
-                principal = getInteraction().participations[0].principal
-
-                auth = queryUtility(IAuthentication)
-                if auth is not None:
-                    try:
-                        principal = auth.getPrincipal(principal.id)
-                    except PrincipalLookupError:
-                        pass
-
-                clone.__principal__ = principal
+                clone.__principal__ = getInteraction().participations[0].principal
         else:
             clone.__principal__ = principal
 
@@ -78,29 +61,15 @@
         if not IBound.providedBy(self):
             raise UnboundPreferenceGroup()
 
-        ann = getMultiAdapter((self.__principal__, self), IAnnotations)
+        return getMultiAdapter((self.__principal__, self), IDataStorage)
 
-        # 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__]
-
     def isAvailable(self):
         if IPreferenceGroup.providedBy(self.__parent__):
             if not self.__parent__.isAvailable():
                 return False
 
         for test in self.__tests__:
-            if callable(test):
-                if not test(self):
-                    return False
-            elif not bool(test):
+            if not test(self):
                 return False
 
         return True
@@ -118,6 +87,8 @@
                 name = id + grp_id
 
                 group = queryUtility(IPreferenceGroup, name)
+
+                # this code for support z3c.baseregistry package
                 if group is None:
                     group = getGlobalSiteManager().queryUtility(
                         IPreferenceGroup, name)
@@ -139,7 +110,7 @@
         group = queryUtility(IPreferenceGroup, id, default)
         if group is default:
             return default
-        return group.__bind__(parent=self)
+        return group.__bind__(self.__principal__, self)
 
     def items(self):
         id = self.__id__
@@ -151,7 +122,7 @@
             name = id + key
             group = queryUtility(IPreferenceGroup, name)
             if group is not None:
-                items.append((name, group.__bind__(parent=self)))
+                items.append((name, group.__bind__(self.__principal__, self)))
         return items
 
     def __getitem__(self, key):
@@ -175,7 +146,7 @@
             name = id + key
             group = queryUtility(IPreferenceGroup, name)
             if group is not None:
-                yield group.__bind__(parent=self)
+                yield group.__bind__(self.__principal__, self)
 
     def values(self):
         return [group for id, group in self.items()]

Modified: z3ext.preferences/trunk/src/z3ext/preferences/preferencetype.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/preferencetype.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/preferencetype.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -106,7 +106,7 @@
         return self.schema
 
     def __set__(self, inst, value):
-        raise AttributeError("Can't set __schema__")
+        raise AttributeError("Can't change __schema__")
 
 
 class PreferenceProperty(object):
@@ -121,6 +121,8 @@
     ...    default = u'default value')
     >>> field.__name__ = 'attr1'
 
+    >>> from z3ext.preferences.storage import DataStorage
+
     Now we need content class
 
     >>> from z3ext.preferences.preferencetype import PreferenceProperty
@@ -131,7 +133,7 @@
     Lets create class instance and add field values storage
 
     >>> ob = Content()
-    >>> ob.data = {}
+    >>> ob.data = DataStorage({}, None)
     
     By default we should get field default value
 
@@ -151,7 +153,7 @@
 
     If storage contains field value we shuld get it
 
-    >>> ob.data['attr1'] = u'value2'
+    >>> ob.data.attr1 = u'value2'
     >>> ob.attr1
     u'value2'
 
@@ -180,7 +182,7 @@
         if inst is None:
             return self
 
-        value = inst.data.get(self._name, _marker)
+        value = getattr(inst.data, self._name, _marker)
         if value is _marker:
             return self._field.default
 
@@ -189,10 +191,10 @@
     def __set__(self, inst, value):
         field = self._field.bind(inst)
         field.validate(value)
-        if field.readonly and self._name in inst.data:
+        if field.readonly and hasattr(inst.data, self._name):
             raise ValueError(self._name, _(u'Field is readonly'))
-        inst.data[self._name] = value
+        setattr(inst.data, self._name, value)
 
     def __delete__(self, inst):
-        if self._name in inst.data:
-            del inst.data[self._name]
+        if hasattr(inst.data, self._name):
+            delattr(inst.data, self._name)

Modified: z3ext.preferences/trunk/src/z3ext/preferences/root.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/root.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/root.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -16,11 +16,12 @@
 $Id$
 """
 from zope import interface
+from zope.app.component.hooks import getSite
 from zope.app.security.interfaces import IUnauthenticatedPrincipal
 
 from i18n import _
 from preference import PreferenceGroup
-from interfaces import IRootPreferences, IPreferenceCategory
+from interfaces import IBound, IRootPreferences, IPreferenceCategory
 
 
 class PersonalPreferences(PreferenceGroup):
@@ -36,7 +37,13 @@
     def __init__(self):
         self.__subgroups__ = ()
 
+    def __bind__(self, principal=None, parent=None):
+        if parent is None:
+            parent = getSite()
+        return super(PersonalPreferences, self).__bind__(principal, parent)
+
     def isAvailable(self):
-        if IUnauthenticatedPrincipal.providedBy(self.__principal__):
+        if (not IBound.providedBy(self) or
+            IUnauthenticatedPrincipal.providedBy(self.__principal__)):
             return False
         return True

Added: z3ext.preferences/trunk/src/z3ext/preferences/storage.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/storage.py	                        (rev 0)
+++ z3ext.preferences/trunk/src/z3ext/preferences/storage.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from BTrees.OOBTree import OOBTree
+
+from zope import interface, component
+from zope.security.interfaces import IPrincipal
+from zope.annotation.interfaces import IAnnotations
+
+from interfaces import ANNOTATION_KEY, IDataStorage, IPreferenceGroup
+
+
+ at interface.implementer(IDataStorage)
+ at component.adapter(IPrincipal, IPreferenceGroup)
+def getDefaultStorage(principal, preference):
+    ann = IAnnotations(principal)
+
+    # If no preferences exist, create the root preferences object.
+    if  ann.get(ANNOTATION_KEY) is None:
+        ann[ANNOTATION_KEY] = OOBTree()
+    prefs = ann[ANNOTATION_KEY]
+
+    # If no entry for the group exists, create a new entry.
+    id = preference.__schema__.getTaggedValue('preferenceID')
+    if id not in prefs:
+        prefs[id] = OOBTree()
+
+    return DataStorage(prefs[id], preference.__schema__)
+
+
+class DataStorage(object):
+    interface.implements(IDataStorage)
+
+    def __init__(self, btree, schema):
+        self.__dict__['__btree__'] = btree
+        self.__dict__['__schema__'] = schema
+
+    def __getattr__(self, attr):
+        if attr in self.__btree__:
+            return self.__btree__[attr]
+        else:
+            raise AttributeError()
+
+    def __setattr__(self, attr, value):
+        self.__btree__[attr] = value
+
+    def __delattr__(self, attr):
+        if attr in self.__btree__:
+            del self.__btree__[attr]


Property changes on: z3ext.preferences/trunk/src/z3ext/preferences/storage.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: z3ext.preferences/trunk/src/z3ext/preferences/tests.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/tests.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/tests.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -36,6 +36,9 @@
             globs={'pprint': doctestunit.pprint},
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
         doctest.DocTestSuite(
+            'z3ext.preferences.utils',
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        doctest.DocTestSuite(
             'z3ext.preferences.preferencetype',
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
         ))

Modified: z3ext.preferences/trunk/src/z3ext/preferences/utils.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/utils.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/utils.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -14,7 +14,33 @@
 """
 
 $Id$
+
+  >>> from zope import interface
+  >>> from zope.security.interfaces import IPrincipal, IGroup
+  >>> class P(object):
+  ...   interface.implements(IPrincipal)
+
+  >>> class Prefs(object):
+  ...     __principal__ = None
+
+  >>> prefs = Prefs()
+  >>> prefs.__principal__ = P()
+
+  >>> import utils
+  >>> print utils.isUser(prefs)
+  True
+  >>> print utils.isGroup(prefs)
+  False
+
+  >>> interface.directlyProvides(prefs.__principal__, IGroup)
+  >>> print utils.isUser(prefs)
+  False
+  >>> print utils.isGroup(prefs)
+  True
+  >>> print utils.isMemberAwareGroup(prefs)
+  False
 """
+
 from zope import interface
 from zope.security.interfaces import IPrincipal, IGroup, IMemberAwareGroup
 

Modified: z3ext.preferences/trunk/src/z3ext/preferences/zcml.py
===================================================================
--- z3ext.preferences/trunk/src/z3ext/preferences/zcml.py	2008-04-17 05:56:06 UTC (rev 85450)
+++ z3ext.preferences/trunk/src/z3ext/preferences/zcml.py	2008-04-17 08:37:22 UTC (rev 85451)
@@ -22,10 +22,11 @@
 
 from zope.security.zcml import Permission
 from zope.security.checker import Checker, CheckerPublic
+from zope.security.interfaces import IPrincipal
 
 from zope.interface.common.mapping import IEnumerableMapping
 
-from zope.component.zcml import utility
+from zope.component.zcml import utility, adapter
 from zope.component.interface import provideInterface
 
 from zope.configuration import fields
@@ -59,7 +60,7 @@
         title=u"Schema",
         description=u"Schema of the preference group used defining the "
                     u"preferences of the group.",
-        required=False)
+        required=True)
 
     title = fields.MessageID(
         title=u"Title",
@@ -98,11 +99,20 @@
         required = False)
 
 
+class PreferenceGroupAdapter(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __call__(self, principal, context=None):
+        prefs = getUtility(IPreferenceGroup, self.name)
+        return prefs.__bind__(principal, context)
+
+
 class PreferenceGroupDirective(object):
 
-    def __init__(self, _context, id, title,
-                 for_=None, schema=interface.Interface,
-                 description=u'', category=False, class_=None, provides=[],
+    def __init__(self, _context, id, schema, title,
+                 for_=None, description=u'', class_=None, provides=[],
                  permission='z3ext.ModifyPreference', tests=(), order = 9999):
 
         Class = PreferenceType(str(id), schema, class_, title, description)
@@ -114,6 +124,10 @@
         group = Class(tests)
 
         utility(_context, IPreferenceGroup, group, name=id)
+        adapter(_context, (PreferenceGroupAdapter(id),), schema,
+                (for_ or IPrincipal,))
+        adapter(_context, (PreferenceGroupAdapter(id),), schema,
+                (for_ or IPrincipal, interface.Interface))
 
         interface.classImplements(Class, *provides)
 
@@ -126,8 +140,10 @@
         self.require(_context, CheckerPublic,
                      interface=(IEnumerableMapping,), attributes=('isAvailable',))
 
+        schema.setTaggedValue('preferenceID', id)
+
         _context.action(
-            discriminator=('z3ext:preferences', group),
+            discriminator=('z3ext:preferences', schema),
             callable=addSubgroup, args=(group,))
 
     def require(self, _context,



More information about the Checkins mailing list