schema.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4759 af2e6c377c71
child 4834 b718626a0e60
--- a/schema.py	Fri Feb 12 15:18:00 2010 +0100
+++ b/schema.py	Wed Mar 24 10:23:31 2010 +0100
@@ -15,14 +15,14 @@
 
 from logilab.common.decorators import cached, clear_cache, monkeypatch
 from logilab.common.logging_ext import set_log_methods
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import deprecated, class_moved
 from logilab.common.graph import get_cycles
 from logilab.common.compat import any
 
 from yams import BadSchemaDefinition, buildobjs as ybo
-from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema
-from yams.constraints import (BaseConstraint, StaticVocabularyConstraint,
-                              FormatConstraint)
+from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \
+     RelationDefinitionSchema, PermissionMixIn
+from yams.constraints import BaseConstraint, FormatConstraint
 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
                          obsolete as yobsolete, cleanup_sys_modules)
 
@@ -31,11 +31,6 @@
 import cubicweb
 from cubicweb import ETYPE_NAME_MAP, ValidationError, Unauthorized
 
-# XXX <3.2 bw compat
-from yams import schema
-schema.use_py_datetime()
-nodes.use_py_datetime()
-
 PURE_VIRTUAL_RTYPES = set(('identity', 'has_text',))
 VIRTUAL_RTYPES = set(('eid', 'identity', 'has_text',))
 
@@ -54,6 +49,13 @@
     'constrained_by', 'cstrtype',
     ))
 
+WORKFLOW_TYPES = set(('Transition', 'State', 'TrInfo', 'Workflow',
+                      'WorkflowTransition', 'BaseTransition',
+                      'SubWorkflowExitPoint'))
+
+INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri'))
+
+
 _LOGGER = getLogger('cubicweb.schemaloader')
 
 # schema entities created from serialized schema have an eid rproperty
@@ -62,6 +64,31 @@
 ybo.RDEF_PROPERTIES += ('eid',)
 
 
+PUB_SYSTEM_ENTITY_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    ('managers',),
+    'delete': ('managers',),
+    'update': ('managers',),
+    }
+PUB_SYSTEM_REL_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    ('managers',),
+    'delete': ('managers',),
+    }
+PUB_SYSTEM_ATTR_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'update':    ('managers',),
+    }
+RO_REL_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    (),
+    'delete': (),
+    }
+RO_ATTR_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'update': (),
+    }
+
 # XXX same algorithm as in reorder_cubes and probably other place,
 # may probably extract a generic function
 def order_eschemas(eschemas):
@@ -117,7 +144,7 @@
     else:
         return unicode(req._(key)).lower()
 
-__builtins__['display_name'] = deprecated('display_name should be imported from cubicweb.schema')(display_name)
+__builtins__['display_name'] = deprecated('[3.4] display_name should be imported from cubicweb.schema')(display_name)
 
 
 # rql expression utilities function ############################################
@@ -151,15 +178,15 @@
 
 # Schema objects definition ###################################################
 
-def ERSchema_display_name(self, req, form=''):
+def ERSchema_display_name(self, req, form='', context=None):
     """return a internationalized string for the entity/relation type name in
     a given form
     """
-    return display_name(req, self.type, form)
+    return display_name(req, self.type, form, context)
 ERSchema.display_name = ERSchema_display_name
 
 @cached
-def ERSchema_get_groups(self, action):
+def get_groups(self, action):
     """return the groups authorized to perform <action> on entities of
     this type
 
@@ -172,28 +199,13 @@
     assert action in self.ACTIONS, action
     #assert action in self._groups, '%s %s' % (self, action)
     try:
-        return frozenset(g for g in self._groups[action] if isinstance(g, basestring))
+        return frozenset(g for g in self.permissions[action] if isinstance(g, basestring))
     except KeyError:
         return ()
-ERSchema.get_groups = ERSchema_get_groups
-
-def ERSchema_set_groups(self, action, groups):
-    """set the groups allowed to perform <action> on entities of this type. Don't
-    change rql expressions for the same action.
-
-    :type action: str
-    :param action: the name of a permission
-
-    :type groups: list or tuple
-    :param groups: names of the groups granted to do the given action
-    """
-    assert action in self.ACTIONS, action
-    clear_cache(self, 'ERSchema_get_groups')
-    self._groups[action] = tuple(groups) + self.get_rqlexprs(action)
-ERSchema.set_groups = ERSchema_set_groups
+PermissionMixIn.get_groups = get_groups
 
 @cached
-def ERSchema_get_rqlexprs(self, action):
+def get_rqlexprs(self, action):
     """return the rql expressions representing queries to check the user is allowed
     to perform <action> on entities of this type
 
@@ -206,27 +218,13 @@
     assert action in self.ACTIONS, action
     #assert action in self._rqlexprs, '%s %s' % (self, action)
     try:
-        return tuple(g for g in self._groups[action] if not isinstance(g, basestring))
+        return tuple(g for g in self.permissions[action] if not isinstance(g, basestring))
     except KeyError:
         return ()
-ERSchema.get_rqlexprs = ERSchema_get_rqlexprs
-
-def ERSchema_set_rqlexprs(self, action, rqlexprs):
-    """set the rql expression allowing to perform <action> on entities of this type. Don't
-    change groups for the same action.
-
-    :type action: str
-    :param action: the name of a permission
+PermissionMixIn.get_rqlexprs = get_rqlexprs
 
-    :type rqlexprs: list or tuple
-    :param rqlexprs: the rql expressions allowing the given action
-    """
-    assert action in self.ACTIONS, action
-    clear_cache(self, 'ERSchema_get_rqlexprs')
-    self._groups[action] = tuple(self.get_groups(action)) + tuple(rqlexprs)
-ERSchema.set_rqlexprs = ERSchema_set_rqlexprs
-
-def ERSchema_set_permissions(self, action, permissions):
+orig_set_action_permissions = PermissionMixIn.set_action_permissions
+def set_action_permissions(self, action, permissions):
     """set the groups and rql expressions allowing to perform <action> on
     entities of this type
 
@@ -236,22 +234,12 @@
     :type permissions: tuple
     :param permissions: the groups and rql expressions allowing the given action
     """
-    assert action in self.ACTIONS, action
-    clear_cache(self, 'ERSchema_get_rqlexprs')
-    clear_cache(self, 'ERSchema_get_groups')
-    self._groups[action] = tuple(permissions)
-ERSchema.set_permissions = ERSchema_set_permissions
+    orig_set_action_permissions(self, action, tuple(permissions))
+    clear_cache(self, 'get_rqlexprs')
+    clear_cache(self, 'get_groups')
+PermissionMixIn.set_action_permissions = set_action_permissions
 
-def ERSchema_has_perm(self, session, action, *args, **kwargs):
-    """return true if the action is granted globaly or localy"""
-    try:
-        self.check_perm(session, action, *args, **kwargs)
-        return True
-    except Unauthorized:
-        return False
-ERSchema.has_perm = ERSchema_has_perm
-
-def ERSchema_has_local_role(self, action):
+def has_local_role(self, action):
     """return true if the action *may* be granted localy (eg either rql
     expressions or the owners group are used in security definition)
 
@@ -262,9 +250,73 @@
     if self.get_rqlexprs(action):
         return True
     if action in ('update', 'delete'):
-        return self.has_group(action, 'owners')
+        return 'owners' in self.get_groups(action)
     return False
-ERSchema.has_local_role = ERSchema_has_local_role
+PermissionMixIn.has_local_role = has_local_role
+
+def may_have_permission(self, action, req):
+    if action != 'read' and not (self.has_local_role('read') or
+                                 self.has_perm(req, 'read')):
+        return False
+    return self.has_local_role(action) or self.has_perm(req, action)
+PermissionMixIn.may_have_permission = may_have_permission
+
+def has_perm(self, session, action, **kwargs):
+    """return true if the action is granted globaly or localy"""
+    try:
+        self.check_perm(session, action, **kwargs)
+        return True
+    except Unauthorized:
+        return False
+PermissionMixIn.has_perm = has_perm
+
+def check_perm(self, session, action, **kwargs):
+    # NB: session may be a server session or a request object check user is
+    # in an allowed group, if so that's enough internal sessions should
+    # always stop there
+    groups = self.get_groups(action)
+    if session.user.matching_groups(groups):
+        return
+    # if 'owners' in allowed groups, check if the user actually owns this
+    # object, if so that's enough
+    if 'owners' in groups and (
+          kwargs.get('creating')
+          or ('eid' in kwargs and session.user.owns(kwargs['eid']))):
+        return
+    # else if there is some rql expressions, check them
+    if any(rqlexpr.check(session, **kwargs)
+           for rqlexpr in self.get_rqlexprs(action)):
+        return
+    raise Unauthorized(action, str(self))
+PermissionMixIn.check_perm = check_perm
+
+
+RelationDefinitionSchema._RPROPERTIES['eid'] = None
+
+def rql_expression(self, expression, mainvars=None, eid=None):
+    """rql expression factory"""
+    if self.rtype.final:
+        return ERQLExpression(expression, mainvars, eid)
+    return RRQLExpression(expression, mainvars, eid)
+RelationDefinitionSchema.rql_expression = rql_expression
+
+orig_check_permission_definitions = RelationDefinitionSchema.check_permission_definitions
+def check_permission_definitions(self):
+    orig_check_permission_definitions(self)
+    schema = self.subject.schema
+    for action, groups in self.permissions.iteritems():
+        for group_or_rqlexpr in groups:
+            if action == 'read' and \
+                   isinstance(group_or_rqlexpr, RQLExpression):
+                msg = "can't use rql expression for read permission of %s"
+                raise BadSchemaDefinition(msg % self)
+            if self.final and isinstance(group_or_rqlexpr, RRQLExpression):
+                msg = "can't use RRQLExpression on %s, use an ERQLExpression"
+                raise BadSchemaDefinition(msg % self)
+            if not self.final and isinstance(group_or_rqlexpr, ERQLExpression):
+                msg = "can't use ERQLExpression on %s, use a RRQLExpression"
+                raise BadSchemaDefinition(msg % self)
+RelationDefinitionSchema.check_permission_definitions = check_permission_definitions
 
 
 class CubicWebEntitySchema(EntitySchema):
@@ -277,13 +329,14 @@
         if eid is None and edef is not None:
             eid = getattr(edef, 'eid', None)
         self.eid = eid
-        # take care: no _groups attribute when deep-copying
-        if getattr(self, '_groups', None):
-            for groups in self._groups.itervalues():
-                for group_or_rqlexpr in groups:
-                    if isinstance(group_or_rqlexpr, RRQLExpression):
-                        msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)"
-                        raise BadSchemaDefinition(msg % self.type)
+
+    def check_permission_definitions(self):
+        super(CubicWebEntitySchema, self).check_permission_definitions()
+        for groups in self.permissions.itervalues():
+            for group_or_rqlexpr in groups:
+                if isinstance(group_or_rqlexpr, RRQLExpression):
+                    msg = "can't use RRQLExpression on %s, use an ERQLExpression"
+                    raise BadSchemaDefinition(msg % self.type)
 
     def attribute_definitions(self):
         """return an iterator on attribute definitions
@@ -326,7 +379,7 @@
             if rschema.final:
                 if rschema == 'has_text':
                     has_has_text = True
-                elif self.rproperty(rschema, 'fulltextindexed'):
+                elif self.rdef(rschema).get('fulltextindexed'):
                     may_need_has_text = True
             elif rschema.fulltext_container:
                 if rschema.fulltext_container == 'subject':
@@ -342,7 +395,8 @@
         if need_has_text is None:
             need_has_text = may_need_has_text
         if need_has_text and not has_has_text and not deletion:
-            rdef = ybo.RelationDefinition(self.type, 'has_text', 'String')
+            rdef = ybo.RelationDefinition(self.type, 'has_text', 'String',
+                                          __permissions__=RO_ATTR_PERMS)
             self.schema.add_relation_def(rdef)
         elif not need_has_text and has_has_text:
             self.schema.del_relation_def(self.type, 'has_text', 'String')
@@ -351,32 +405,12 @@
         """return True if this entity type is used to build the schema"""
         return self.type in SCHEMA_TYPES
 
-    def check_perm(self, session, action, eid=None):
-        # NB: session may be a server session or a request object
-        user = session.user
-        # check user is in an allowed group, if so that's enough
-        # internal sessions should always stop there
-        if user.matching_groups(self.get_groups(action)):
-            return
-        # if 'owners' in allowed groups, check if the user actually owns this
-        # object, if so that's enough
-        if eid is not None and 'owners' in self.get_groups(action) and \
-               user.owns(eid):
-            return
-        # else if there is some rql expressions, check them
-        if any(rqlexpr.check(session, eid)
-               for rqlexpr in self.get_rqlexprs(action)):
-            return
-        raise Unauthorized(action, str(self))
-
     def rql_expression(self, expression, mainvars=None, eid=None):
         """rql expression factory"""
         return ERQLExpression(expression, mainvars, eid)
 
 
 class CubicWebRelationSchema(RelationSchema):
-    RelationSchema._RPROPERTIES['eid'] = None
-    _perms_checked = False
 
     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
         if rdef is not None:
@@ -391,80 +425,69 @@
     def meta(self):
         return self.type in META_RTYPES
 
-    def update(self, subjschema, objschema, rdef):
-        super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
-        if not self._perms_checked and self._groups:
-            for action, groups in self._groups.iteritems():
-                for group_or_rqlexpr in groups:
-                    if action == 'read' and \
-                           isinstance(group_or_rqlexpr, RQLExpression):
-                        msg = "can't use rql expression for read permission of "\
-                              "a relation type (%s)"
-                        raise BadSchemaDefinition(msg % self.type)
-                    elif self.final and isinstance(group_or_rqlexpr, RRQLExpression):
-                        if self.schema.reading_from_database:
-                            # we didn't have final relation earlier, so turn
-                            # RRQLExpression into ERQLExpression now
-                            rqlexpr = group_or_rqlexpr
-                            newrqlexprs = [x for x in self.get_rqlexprs(action) if not x is rqlexpr]
-                            newrqlexprs.append(ERQLExpression(rqlexpr.expression,
-                                                              rqlexpr.mainvars,
-                                                              rqlexpr.eid))
-                            self.set_rqlexprs(action, newrqlexprs)
-                        else:
-                            msg = "can't use RRQLExpression on a final relation "\
-                                  "type (eg attribute relation), use an ERQLExpression (%s)"
-                            raise BadSchemaDefinition(msg % self.type)
-                    elif not self.final and \
-                             isinstance(group_or_rqlexpr, ERQLExpression):
-                        msg = "can't use ERQLExpression on a relation type, use "\
-                              "a RRQLExpression (%s)"
-                        raise BadSchemaDefinition(msg % self.type)
-            self._perms_checked = True
-
-    def cardinality(self, subjtype, objtype, target):
-        card = self.rproperty(subjtype, objtype, 'cardinality')
-        return (target == 'subject' and card[0]) or \
-               (target == 'object' and card[1])
-
     def schema_relation(self):
         """return True if this relation type is used to build the schema"""
         return self.type in SCHEMA_TYPES
 
-    def physical_mode(self):
-        """return an appropriate mode for physical storage of this relation type:
-        * 'subjectinline' if every possible subject cardinalities are 1 or ?
-        * 'objectinline' if 'subjectinline' mode is not possible but every
-          possible object cardinalities are 1 or ?
-        * None if neither 'subjectinline' and 'objectinline'
-        """
-        assert not self.final
-        return self.inlined and 'subjectinline' or None
+    def may_have_permission(self, action, req, eschema=None, role=None):
+        if eschema is not None:
+            for tschema in self.targets(eschema, role):
+                rdef = self.role_rdef(eschema, tschema, role)
+                if rdef.may_have_permission(action, req):
+                    return True
+        else:
+            for rdef in self.rdefs.itervalues():
+                if rdef.may_have_permission(action, req):
+                    return True
+        return False
 
-    def check_perm(self, session, action, *args, **kwargs):
-        # NB: session may be a server session or a request object check user is
-        # in an allowed group, if so that's enough internal sessions should
-        # always stop there
-        if session.user.matching_groups(self.get_groups(action)):
-            return
-        # else if there is some rql expressions, check them
-        if any(rqlexpr.check(session, *args, **kwargs)
-               for rqlexpr in self.get_rqlexprs(action)):
-            return
-        raise Unauthorized(action, str(self))
+    def has_perm(self, session, action, **kwargs):
+        """return true if the action is granted globaly or localy"""
+        if self.final:
+            assert not ('fromeid' in kwargs or 'toeid' in kwargs), kwargs
+            assert action in ('read', 'update')
+            if 'eid' in kwargs:
+                subjtype = session.describe(kwargs['eid'])[0]
+            else:
+                subjtype = objtype = None
+        else:
+            assert not 'eid' in kwargs, kwargs
+            assert action in ('read', 'add', 'delete')
+            if 'fromeid' in kwargs:
+                subjtype = session.describe(kwargs['fromeid'])[0]
+            else:
+                subjtype = None
+            if 'toeid' in kwargs:
+                objtype = session.describe(kwargs['toeid'])[0]
+            else:
+                objtype = None
+        if objtype and subjtype:
+            return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs)
+        elif subjtype:
+            for tschema in self.targets(subjtype, 'subject'):
+                rdef = self.rdef(subjtype, tschema)
+                if not rdef.has_perm(session, action, **kwargs):
+                    return False
+        elif objtype:
+            for tschema in self.targets(objtype, 'object'):
+                rdef = self.rdef(tschema, objtype)
+                if not rdef.has_perm(session, action, **kwargs):
+                    return False
+        else:
+            for rdef in self.rdefs.itervalues():
+                if not rdef.has_perm(session, action, **kwargs):
+                    return False
+        return True
 
-    def rql_expression(self, expression, mainvars=None, eid=None):
-        """rql expression factory"""
-        if self.final:
-            return ERQLExpression(expression, mainvars, eid)
-        return RRQLExpression(expression, mainvars, eid)
+    @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)')
+    def cardinality(self, subjtype, objtype, target):
+        return self.rdef(subjtype, objtype).role_cardinality(target)
 
 
 class CubicWebSchema(Schema):
     """set of entities and relations schema defining the possible data sets
     used in an application
 
-
     :type name: str
     :ivar name: name of the schema, usually the instance identifier
 
@@ -482,13 +505,10 @@
         ybo.register_base_types(self)
         rschema = self.add_relation_type(ybo.RelationType('eid'))
         rschema.final = True
-        rschema.set_default_groups()
         rschema = self.add_relation_type(ybo.RelationType('has_text'))
         rschema.final = True
-        rschema.set_default_groups()
         rschema = self.add_relation_type(ybo.RelationType('identity'))
         rschema.final = False
-        rschema.set_default_groups()
 
     def add_entity_type(self, edef):
         edef.name = edef.name.encode()
@@ -498,9 +518,11 @@
         if not eschema.final:
             # automatically add the eid relation to non final entity types
             rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Int',
-                                          cardinality='11', uid=True)
+                                          cardinality='11', uid=True,
+                                          __permissions__=RO_ATTR_PERMS)
             self.add_relation_def(rdef)
-            rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type)
+            rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type,
+                                          __permissions__=RO_REL_PERMS)
             self.add_relation_def(rdef)
         self._eid_index[eschema.eid] = eschema
         return eschema
@@ -530,13 +552,13 @@
         rdef.name = rdef.name.lower()
         rdef.subject = bw_normalize_etype(rdef.subject)
         rdef.object = bw_normalize_etype(rdef.object)
-        if super(CubicWebSchema, self).add_relation_def(rdef):
+        rdefs = super(CubicWebSchema, self).add_relation_def(rdef)
+        if rdefs:
             try:
-                self._eid_index[rdef.eid] = (self.eschema(rdef.subject),
-                                             self.rschema(rdef.name),
-                                             self.eschema(rdef.object))
+                self._eid_index[rdef.eid] = rdefs
             except AttributeError:
                 pass # not a serialized schema
+        return rdefs
 
     def del_relation_type(self, rtype):
         rschema = self.rschema(rtype)
@@ -545,7 +567,9 @@
 
     def del_relation_def(self, subjtype, rtype, objtype):
         for k, v in self._eid_index.items():
-            if v == (subjtype, rtype, objtype):
+            if not isinstance(v, RelationDefinitionSchema):
+                continue
+            if v.subject == subjtype and v.rtype == rtype and v.object == objtype:
                 del self._eid_index[k]
                 break
         super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype)
@@ -730,6 +754,11 @@
     def __repr__(self):
         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
 
+    def __cmp__(self, other):
+        if hasattr(other, 'expression'):
+            return cmp(other.expression, self.expression)
+        return -1
+
     def __deepcopy__(self, memo):
         return self.__class__(self.expression, self.mainvars)
     def __getstate__(self):
@@ -765,7 +794,7 @@
                     rqlst.add_selected(objvar)
                 else:
                     colindex = selected.index(objvar.name)
-                found.append((action, objvar, colindex))
+                found.append((action, colindex))
                 # remove U eid %(u)s if U is not used in any other relation
                 uvrefs = rqlst.defined_vars['U'].references()
                 if len(uvrefs) == 1:
@@ -787,20 +816,25 @@
 
         session may actually be a request as well
         """
-        if self.eid is not None:
+        creating = kwargs.get('creating')
+        if not creating and self.eid is not None:
             key = (self.eid, tuple(sorted(kwargs.iteritems())))
             try:
                 return session.local_perm_cache[key]
             except KeyError:
                 pass
         rql, has_perm_defs, keyarg = self.transform_has_permission()
+        if creating:
+            # when creating an entity, consider has_*_permission satisfied
+            if has_perm_defs:
+                return True
+            return False
         if keyarg is None:
             # on the server side, use unsafe_execute, but this is not available
             # on the client side (session is actually a request)
             execute = getattr(session, 'unsafe_execute', session.execute)
-            # XXX what if 'u' in kwargs
+            kwargs.setdefault('u', session.user.eid)
             cachekey = kwargs.keys()
-            kwargs['u'] = session.user.eid
             try:
                 rset = execute(rql, kwargs, cachekey, build_descr=True)
             except NotImplementedError:
@@ -828,10 +862,10 @@
             # check every special has_*_permission relation is satisfied
             get_eschema = session.vreg.schema.eschema
             try:
-                for eaction, var, col in has_perm_defs:
+                for eaction, col in has_perm_defs:
                     for i in xrange(len(rset)):
                         eschema = get_eschema(rset.description[i][col])
-                        eschema.check_perm(session, eaction, rset[i][col])
+                        eschema.check_perm(session, eaction, eid=rset[i][col])
                 if self.eid is not None:
                     session.local_perm_cache[key] = True
                 return True
@@ -864,12 +898,15 @@
             rql += ', U eid %(u)s'
         return rql
 
-    def check(self, session, eid=None):
+    def check(self, session, eid=None, creating=False, **kwargs):
         if 'X' in self.rqlst.defined_vars:
             if eid is None:
+                if creating:
+                    return self._check(session, creating=True, **kwargs)
                 return False
-            return self._check(session, x=eid)
-        return self._check(session)
+            assert creating == False
+            return self._check(session, x=eid, **kwargs)
+        return self._check(session, **kwargs)
 
 
 class RRQLExpression(RQLExpression):
@@ -918,6 +955,11 @@
             kwargs['o'] = toeid
         return self._check(session, **kwargs)
 
+# in yams, default 'update' perm for attributes granted to managers and owners.
+# Within cw, we want to default to users who may edit the entity holding the
+# attribute.
+ybo.DEFAULT_ATTRPERMS['update'] = (
+    'managers', ERQLExpression('U has_update_permission X'))
 
 # workflow extensions #########################################################
 
@@ -1036,13 +1078,21 @@
 
 @monkeypatch(FormatConstraint)
 def vocabulary(self, entity=None, form=None):
-    req = None
+    cw = None
     if form is None and entity is not None:
-        req = entity.req
+        cw = entity._cw
     elif form is not None:
-        req = form.req
-    if req is not None and req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
-        return self.regular_formats + tuple(NEED_PERM_FORMATS)
+        cw = form._cw
+    if cw is not None:
+        if hasattr(cw, 'is_super_session'):
+            # cw is a server session
+            hasperm = cw.is_super_session or \
+                      not cw.vreg.config.is_hook_category_activated('integrity') or \
+                      cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
+        else:
+            hasperm = cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
+        if hasperm:
+            return self.regular_formats + tuple(NEED_PERM_FORMATS)
     return self.regular_formats
 
 # XXX monkey patch PyFileReader.import_erschema until bw_normalize_etype is
@@ -1076,10 +1126,14 @@
 
 # XXX deprecated
 
-from yams.constraints import format_constraint
 from yams.buildobjs import RichString
+from yams.constraints import StaticVocabularyConstraint
+
+RichString = class_moved(RichString)
+
+StaticVocabularyConstraint = class_moved(StaticVocabularyConstraint)
+FormatConstraint = class_moved(FormatConstraint)
 
 PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression)
 PyFileReader.context['RRQLExpression'] = yobsolete(RRQLExpression)
 PyFileReader.context['WorkflowableEntityType'] = WorkflowableEntityType
-PyFileReader.context['format_constraint'] = format_constraint