# HG changeset patch # User Sylvain Thénault # Date 1266255887 -3600 # Node ID 70d47389630cf0948dae530f12bbe58c499301a9 # Parent bf34537898874e0dc43f1f3196fd0c9bbce5447d# Parent 440e340c61fe95f954c95ac6cd84a6a935a69498 backport stable diff -r bf3453789887 -r 70d47389630c appobject.py --- a/appobject.py Thu Feb 11 12:19:08 2010 +0100 +++ b/appobject.py Mon Feb 15 18:44:47 2010 +0100 @@ -404,7 +404,7 @@ def format_date(self, date, date_format=None, time=False): return self._cw.format_date(date, date_format, time) - @deprecated('[3.6] use self._cw.format_timoe') + @deprecated('[3.6] use self._cw.format_time') def format_time(self, time): return self._cw.format_time(time) diff -r bf3453789887 -r 70d47389630c hooks/metadata.py --- a/hooks/metadata.py Thu Feb 11 12:19:08 2010 +0100 +++ b/hooks/metadata.py Mon Feb 15 18:44:47 2010 +0100 @@ -55,7 +55,13 @@ events = ('before_update_entity',) def __call__(self): - self.entity.setdefault('modification_date', datetime.now()) + # repairing is true during c-c upgrade/shell and similar commands. We + # usually don't want to update modification date in such cases. + # + # XXX to be really clean, we should turn off modification_date update + # explicitly on each command where we do not want that behaviour. + if not self._cw.vreg.config.repairing: + self.entity.setdefault('modification_date', datetime.now()) class _SetCreatorOp(hook.Operation): diff -r bf3453789887 -r 70d47389630c hooks/notification.py --- a/hooks/notification.py Thu Feb 11 12:19:08 2010 +0100 +++ b/hooks/notification.py Mon Feb 15 18:44:47 2010 +0100 @@ -126,7 +126,7 @@ rqlsel.append(var) rqlrestr.append('X %s %s' % (attr, var)) rql = 'Any %s WHERE %s' % (','.join(rqlsel), ','.join(rqlrestr)) - rset = session.execute(rql, {'x': self.entity.eid}, 'x') + rset = session.unsafe_execute(rql, {'x': self.entity.eid}, 'x') for i, attr in enumerate(attrs): oldvalue = rset[0][i] newvalue = self.entity[attr] diff -r bf3453789887 -r 70d47389630c hooks/security.py --- a/hooks/security.py Thu Feb 11 12:19:08 2010 +0100 +++ b/hooks/security.py Mon Feb 15 18:44:47 2010 +0100 @@ -12,30 +12,31 @@ from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook -def check_entity_attributes(session, entity): +def check_entity_attributes(session, entity, editedattrs=None): eid = entity.eid eschema = entity.e_schema # ._default_set is only there on entity creation to indicate unspecified # attributes which has been set to a default value defined in the schema defaults = getattr(entity, '_default_set', ()) - try: - editedattrs = entity.edited_attributes - except AttributeError: - editedattrs = entity + if editedattrs is None: + try: + editedattrs = entity.edited_attributes + except AttributeError: + editedattrs = entity for attr in editedattrs: if attr in defaults: continue rdef = eschema.rdef(attr) if rdef.final: # non final relation are checked by other hooks # add/delete should be equivalent (XXX: unify them into 'update' ?) - rdef.check_perm(session, 'add', eid=eid) + rdef.check_perm(session, 'update', eid=eid) class _CheckEntityPermissionOp(hook.LateOperation): def precommit_event(self): #print 'CheckEntityPermissionOp', self.session.user, self.entity, self.action self.entity.check_perm(self.action) - check_entity_attributes(self.session, self.entity) + check_entity_attributes(self.session, self.entity, self.editedattrs) def commit_event(self): pass @@ -63,7 +64,9 @@ events = ('after_add_entity',) def __call__(self): - _CheckEntityPermissionOp(self._cw, entity=self.entity, action='add') + _CheckEntityPermissionOp(self._cw, entity=self.entity, + editedattrs=tuple(self.entity.edited_attributes), + action='add') class AfterUpdateEntitySecurityHook(SecurityHook): @@ -77,7 +80,12 @@ check_entity_attributes(self._cw, self.entity) except Unauthorized: self.entity.clear_local_perm_cache('update') - _CheckEntityPermissionOp(self._cw, entity=self.entity, action='update') + # save back editedattrs in case the entity is reedited later in the + # same transaction, which will lead to edited_attributes being + # overwritten + _CheckEntityPermissionOp(self._cw, entity=self.entity, + editedattrs=tuple(self.entity.edited_attributes), + action='update') class BeforeDelEntitySecurityHook(SecurityHook): diff -r bf3453789887 -r 70d47389630c hooks/syncschema.py --- a/hooks/syncschema.py Thu Feb 11 12:19:08 2010 +0100 +++ b/hooks/syncschema.py Mon Feb 15 18:44:47 2010 +0100 @@ -716,6 +716,9 @@ return if isinstance(erschema, RelationSchema): # XXX 3.6 migration return + if isinstance(erschema, RelationDefinitionSchema) and \ + self.action in ('delete', 'add'): # XXX 3.6.1 migration + return perms = list(erschema.action_permissions(self.action)) if hasattr(self, 'group_eid'): perm = self.session.entity_from_eid(self.group_eid).name diff -r bf3453789887 -r 70d47389630c hooks/syncsession.py --- a/hooks/syncsession.py Thu Feb 11 12:19:08 2010 +0100 +++ b/hooks/syncsession.py Mon Feb 15 18:44:47 2010 +0100 @@ -153,7 +153,7 @@ raise ValidationError(self.entity.eid, {'value': session._(str(ex))}) if not session.user.matching_groups('managers'): - session.add_relation(entity.eid, 'for_user', session.user.eid) + session.add_relation(self.entity.eid, 'for_user', session.user.eid) else: _AddCWPropertyOp(session, cwprop=self.entity) @@ -178,7 +178,7 @@ if entity.for_user: for session_ in get_user_sessions(session.repo, entity.for_user[0].eid): _ChangeCWPropertyOp(session, cwpropdict=session_.user.properties, - key=key, value=value) + key=key, value=value) else: # site wide properties _ChangeCWPropertyOp(session, cwpropdict=session.vreg['propertyvalues'], diff -r bf3453789887 -r 70d47389630c mail.py --- a/mail.py Thu Feb 11 12:19:08 2010 +0100 +++ b/mail.py Mon Feb 15 18:44:47 2010 +0100 @@ -200,7 +200,8 @@ continue except Exception, ex: # shouldn't make the whole transaction fail because of rendering - # error (unauthorized or such) + # error (unauthorized or such) XXX check it doesn't actually + # occurs due to rollback on such error self.exception(str(ex)) continue msg = format_mail(self.user_data, [emailaddr], content, subject, diff -r bf3453789887 -r 70d47389630c migration.py --- a/migration.py Thu Feb 11 12:19:08 2010 +0100 +++ b/migration.py Mon Feb 15 18:44:47 2010 +0100 @@ -70,8 +70,11 @@ ability to show the script's content """ while True: - answer = ASK.ask('Execute %r ?' % scriptpath, ('Y','n','show'), 'Y') - if answer == 'n': + answer = ASK.ask('Execute %r ?' % scriptpath, + ('Y','n','show','abort'), 'Y') + if answer == 'abort': + raise SystemExit(1) + elif answer == 'n': return False elif answer == 'show': stream = open(scriptpath) diff -r bf3453789887 -r 70d47389630c misc/migration/3.6.1_Any.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/migration/3.6.1_Any.py Mon Feb 15 18:44:47 2010 +0100 @@ -0,0 +1,1 @@ +sync_schema_props_perms(syncprops=False) diff -r bf3453789887 -r 70d47389630c misc/migration/bootstrapmigration_repository.py --- a/misc/migration/bootstrapmigration_repository.py Thu Feb 11 12:19:08 2010 +0100 +++ b/misc/migration/bootstrapmigration_repository.py Mon Feb 15 18:44:47 2010 +0100 @@ -10,24 +10,35 @@ applcubicwebversion, cubicwebversion = versions_map['cubicweb'] -if applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0): - from cubicweb.server import schemaserial as ss +from cubicweb.server import schemaserial as ss +def _add_relation_definition_no_perms(subjtype, rtype, objtype): + rschema = fsschema.rschema(rtype) + for query, args in ss.rdef2rql(rschema, subjtype, objtype, groupmap=None): + rql(query, args, ask_confirm=False) + commit(ask_confirm=False) + +if applcubicwebversion == (3, 6, 0) and cubicwebversion >= (3, 6, 0): + _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'CWGroup') + _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'RQLExpression') + session.set_pool() + session.unsafe_execute('SET X update_permission Y WHERE X is CWAttribute, X add_permission Y') + drop_relation_definition('CWAttribute', 'add_permission', 'CWGroup') + drop_relation_definition('CWAttribute', 'add_permission', 'RQLExpression') + drop_relation_definition('CWAttribute', 'delete_permission', 'CWGroup') + drop_relation_definition('CWAttribute', 'delete_permission', 'RQLExpression') + +elif applcubicwebversion < (3, 6, 0) and cubicwebversion >= (3, 6, 0): session.set_pool() session.execute = session.unsafe_execute permsdict = ss.deserialize_ertype_permissions(session) - def _add_relation_definition_no_perms(subjtype, rtype, objtype): - rschema = fsschema.rschema(rtype) - for query, args in ss.rdef2rql(rschema, subjtype, objtype, groupmap=None): - rql(query, args, ask_confirm=False) - commit(ask_confirm=False) config.disabled_hooks_categories.add('integrity') for rschema in repo.schema.relations(): rpermsdict = permsdict.get(rschema.eid, {}) for rdef in rschema.rdefs.values(): - for action in ('read', 'add', 'delete'): + for action in rdef.ACTIONS: actperms = [] - for something in rpermsdict.get(action, ()): + for something in rpermsdict.get(action == 'update' and 'add' or action, ()): if isinstance(something, tuple): actperms.append(rdef.rql_expression(*something)) else: # group name @@ -36,18 +47,32 @@ for action in ('read', 'add', 'delete'): _add_relation_definition_no_perms('CWRelation', '%s_permission' % action, 'CWGroup') _add_relation_definition_no_perms('CWRelation', '%s_permission' % action, 'RQLExpression') + for action in ('read', 'update'): _add_relation_definition_no_perms('CWAttribute', '%s_permission' % action, 'CWGroup') _add_relation_definition_no_perms('CWAttribute', '%s_permission' % action, 'RQLExpression') for action in ('read', 'add', 'delete'): - rql('SET X %s_permission Y WHERE X is IN (CWAttribute, CWRelation), ' + rql('SET X %s_permission Y WHERE X is CWRelation, ' 'RT %s_permission Y, X relation_type RT, Y is CWGroup' % (action, action)) rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' - 'X %s_permission Y WHERE X is IN (CWAttribute, CWRelation), ' + 'X %s_permission Y WHERE X is CWRelation, ' 'X relation_type RT, RT %s_permission Y2, Y2 exprtype YET, ' 'Y2 mainvars YMV, Y2 expression YEX' % (action, action)) + rql('SET X read_permission Y WHERE X is CWAttribute, ' + 'RT read_permission Y, X relation_type RT, Y is CWGroup') + rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' + 'X read_permission Y WHERE X is CWAttribute, ' + 'X relation_type RT, RT read_permission Y2, Y2 exprtype YET, ' + 'Y2 mainvars YMV, Y2 expression YEX') + rql('SET X update_permission Y WHERE X is CWAttribute, ' + 'RT add_permission Y, X relation_type RT, Y is CWGroup') + rql('INSERT RQLExpression Y: Y exprtype YET, Y mainvars YMV, Y expression YEX, ' + 'X update_permission Y WHERE X is CWAttribute, ' + 'X relation_type RT, RT add_permission Y2, Y2 exprtype YET, ' + 'Y2 mainvars YMV, Y2 expression YEX') + for action in ('read', 'add', 'delete'): drop_relation_definition('CWRType', '%s_permission' % action, 'CWGroup', commit=False) drop_relation_definition('CWRType', '%s_permission' % action, 'RQLExpression') - config.disabled_hooks_categories.add('integrity') + config.disabled_hooks_categories.remove('integrity') if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0): diff -r bf3453789887 -r 70d47389630c schema.py --- a/schema.py Thu Feb 11 12:19:08 2010 +0100 +++ b/schema.py Mon Feb 15 18:44:47 2010 +0100 @@ -283,22 +283,10 @@ isinstance(group_or_rqlexpr, RQLExpression): msg = "can't use rql expression for read permission of %s" raise BadSchemaDefinition(msg % self) - elif self.final and isinstance(group_or_rqlexpr, RRQLExpression): - if 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 %s, use an ERQLExpression" - raise BadSchemaDefinition(msg % self) - elif not self.final and \ - isinstance(group_or_rqlexpr, ERQLExpression): + 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 @@ -314,13 +302,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, 'permissions', None): - for groups in self.permissions.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 @@ -426,14 +415,24 @@ def has_perm(self, session, action, **kwargs): """return true if the action is granted globaly or localy""" - if 'fromeid' in kwargs: - subjtype = session.describe(kwargs['fromeid'])[0] + 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: - subjtype = None - if 'toeid' in kwargs: - objtype = session.describe(kwargs['toeid'])[0] - else: - objtype = None + 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: @@ -919,6 +918,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 ######################################################### diff -r bf3453789887 -r 70d47389630c schemas/bootstrap.py --- a/schemas/bootstrap.py Thu Feb 11 12:19:08 2010 +0100 +++ b/schemas/bootstrap.py Mon Feb 15 18:44:47 2010 +0100 @@ -9,7 +9,7 @@ _ = unicode from yams.buildobjs import (EntityType, RelationType, SubjectRelation, - ObjectRelation, RichString, String, Boolean, Int) + RichString, String, Boolean, Int) from cubicweb.schema import RQLConstraint from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS @@ -131,15 +131,6 @@ 'relation\'subject, object and to ' 'the request user. ')) - read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', - description=_('rql expression allowing to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', - description=_('rql expression allowing to add entities/relations of this type')) - delete_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='*?', composite='subject', - description=_('rql expression allowing to delete entities/relations of this type')) - update_permission = ObjectRelation('CWEType', cardinality='*?', composite='subject', - description=_('rql expression allowing to update entities of this type')) - class CWConstraint(EntityType): """define a schema constraint""" @@ -162,16 +153,6 @@ name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) - read_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), cardinality='**', - description=_('groups allowed to read entities/relations of this type')) - add_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), - description=_('groups allowed to add entities/relations of this type')) - delete_permission = ObjectRelation(('CWEType', 'CWAttribute', 'CWRelation'), - description=_('groups allowed to delete entities/relations of this type')) - update_permission = ObjectRelation('CWEType', - description=_('groups allowed to update entities of this type')) - - class CWProperty(EntityType): """used for cubicweb configuration. Once a property has been created you @@ -215,27 +196,44 @@ inlined = True class read_permission(RelationType): - """core relation giving to a group the permission to read an entity or - relation type + """grant permission to read entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWAttribute', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class add_permission(RelationType): - """core relation giving to a group the permission to add an entity or - relation type + """grant permission to add entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class delete_permission(RelationType): - """core relation giving to a group the permission to delete an entity or - relation type + """grant permission to delete entity or relation through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWRelation') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class update_permission(RelationType): - """core relation giving to a group the permission to update an entity type + """grant permission to update entity or attribute through a group or rql + expression """ __permissions__ = META_RTYPE_PERMS + subject = ('CWEType', 'CWAttribute') + object = ('CWGroup', 'RQLExpression') + cardinality = '*?' + composite = 'subject' class is_(RelationType): diff -r bf3453789887 -r 70d47389630c server/migractions.py --- a/server/migractions.py Thu Feb 11 12:19:08 2010 +0100 +++ b/server/migractions.py Mon Feb 15 18:44:47 2010 +0100 @@ -1166,6 +1166,10 @@ if not isinstance(rql, (tuple, list)): rql = ( (rql, kwargs), ) res = None + try: + execute = self._cw.unsafe_execute + except AttributeError: + execute = self._cw.execute for rql, kwargs in rql: if kwargs: msg = '%s (%s)' % (rql, kwargs) @@ -1173,7 +1177,7 @@ msg = rql if not ask_confirm or self.confirm('execute rql: %s ?' % msg): try: - res = self._cw.execute(rql, kwargs, cachekey) + res = execute(rql, kwargs, cachekey) except Exception, ex: if self.confirm('error: %s\nabort?' % ex): raise diff -r bf3453789887 -r 70d47389630c server/schemaserial.py --- a/server/schemaserial.py Thu Feb 11 12:19:08 2010 +0100 +++ b/server/schemaserial.py Mon Feb 15 18:44:47 2010 +0100 @@ -189,13 +189,17 @@ definition dictionary as built by deserialize_ertype_permissions for a given erschema's eid """ + # reset erschema permissions here to avoid getting yams default anyway + erschema.permissions = dict((action, ()) for action in erschema.ACTIONS) try: thispermsdict = permsdict[erschema.eid] except KeyError: return - permissions = erschema.permissions for action, somethings in thispermsdict.iteritems(): - permissions[action] = tuple( + # XXX cw < 3.6.1 bw compat + if isinstance(erschema, schemamod.RelationDefinitionSchema) and erschema.final and action == 'add': + action = 'update' + erschema.permissions[action] = tuple( isinstance(p, tuple) and erschema.rql_expression(*p) or p for p in somethings) diff -r bf3453789887 -r 70d47389630c server/sources/ldapuser.py --- a/server/sources/ldapuser.py Thu Feb 11 12:19:08 2010 +0100 +++ b/server/sources/ldapuser.py Mon Feb 15 18:44:47 2010 +0100 @@ -78,7 +78,7 @@ ('auth-realm', {'type' : 'string', 'default': None, - 'help': 'realm to use when using gssapp/kerberos authentication.', + 'help': 'realm to use when using gssapi/kerberos authentication.', 'group': 'ldap-source', 'inputlevel': 1, }), diff -r bf3453789887 -r 70d47389630c server/test/data/schema.py --- a/server/test/data/schema.py Thu Feb 11 12:19:08 2010 +0100 +++ b/server/test/data/schema.py Mon Feb 15 18:44:47 2010 +0100 @@ -137,14 +137,13 @@ class para(RelationType): __permissions__ = { 'read': ('managers', 'users', 'guests'), - 'add': ('managers', ERQLExpression('X in_state S, S name "todo"')), - 'delete': ('managers', ERQLExpression('X in_state S, S name "todo"')), + 'update': ('managers', ERQLExpression('X in_state S, S name "todo"')), } class test(RelationType): __permissions__ = {'read': ('managers', 'users', 'guests'), - 'delete': ('managers',), - 'add': ('managers',)} + 'update': ('managers',), + } class multisource_rel(RelationDefinition): subject = ('Card', 'Note') diff -r bf3453789887 -r 70d47389630c toolsutils.py --- a/toolsutils.py Thu Feb 11 12:19:08 2010 +0100 +++ b/toolsutils.py Mon Feb 15 18:44:47 2010 +0100 @@ -10,6 +10,7 @@ # XXX move most of this in logilab.common (shellutils ?) import os, sys +import subprocess from os import listdir, makedirs, environ, chmod, walk, remove from os.path import exists, join, abspath, normpath @@ -75,8 +76,8 @@ user decision """ import shutil - p_output = os.popen('diff -u %s %s' % (appl_file, ref_file), 'r') - diffs = p_output.read() + pipe = subprocess.Popen(['diff', '-u', appl_file, ref_file], stdout=subprocess.PIPE) + diffs = pipe.stdout.read() if diffs: if askconfirm: print diff -r bf3453789887 -r 70d47389630c vregistry.py --- a/vregistry.py Thu Feb 11 12:19:08 2010 +0100 +++ b/vregistry.py Mon Feb 15 18:44:47 2010 +0100 @@ -365,7 +365,6 @@ def initialization_completed(self): for regname, reg in self.iteritems(): - self.debug('available in registry %s: %s', regname, sorted(reg)) reg.initialization_completed() def load_file(self, filepath, modname, force_reload=False): @@ -406,7 +405,6 @@ if objname.startswith('_'): continue self._load_ancestors_then_object(module.__name__, obj) - self.debug('loaded %s', module) def _load_ancestors_then_object(self, modname, appobjectcls): """handle automatic appobject class registration: diff -r bf3453789887 -r 70d47389630c web/formfields.py --- a/web/formfields.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/formfields.py Mon Feb 15 18:44:47 2010 +0100 @@ -816,6 +816,18 @@ def format_single_value(self, req, value): return value + def process_form_value(self, form): + """process posted form and return correctly typed value""" + try: + return form.formvalues[self] + except KeyError: + value = self._process_form_value(form) + # if value is None, there are some remaining pending fields, we'll + # have to recompute this later -> don't cache in formvalues + if value is not None: + form.formvalues[self] = value + return value + def _process_form_value(self, form): """process posted form and return correctly typed value""" widget = self.get_widget(form) @@ -826,7 +838,6 @@ values = (values,) eids = set() for eid in values: - # XXX 'not eid' for AutoCompletionWidget, deal with this in the widget if not eid or eid == INTERNAL_FIELD_VALUE: continue typed_eid = form.actual_eid(eid) diff -r bf3453789887 -r 70d47389630c web/formwidgets.py --- a/web/formwidgets.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/formwidgets.py Mon Feb 15 18:44:47 2010 +0100 @@ -17,6 +17,7 @@ from cubicweb import tags, uilib from cubicweb.web import stdmsgs, INTERNAL_FIELD_VALUE, ProcessFormError + class FieldWidget(object): """abstract widget class""" # javascript / css files required by the widget diff -r bf3453789887 -r 70d47389630c web/test/unittest_views_editforms.py --- a/web/test/unittest_views_editforms.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/test/unittest_views_editforms.py Mon Feb 15 18:44:47 2010 +0100 @@ -16,7 +16,11 @@ AFS = uicfg.autoform_section def rbc(entity, formtype, section): - return [(rschema.type, x) for rschema, tschemas, x in AFS.relations_by_section(entity, formtype, section)] + if section in ('attributes', 'metadata', 'hidden'): + permission = 'update' + else: + permission = 'add' + return [(rschema.type, x) for rschema, tschemas, x in AFS.relations_by_section(entity, formtype, section, permission)] class AutomaticEntityFormTC(CubicWebTC): @@ -69,19 +73,16 @@ [('use_email', 'subject'), ]) # owned_by is defined both as subject and object relations on CWUser - self.assertListEquals(rbc(e, 'main', 'hidden'), - [('in_state', 'subject'), - ('is', 'subject'), - ('is_instance_of', 'subject'), - ('has_text', 'subject'), - ('identity', 'subject'), - ('tags', 'object'), - ('for_user', 'object'), - ('created_by', 'object'), - ('wf_info_for', 'object'), - ('owned_by', 'object'), - ('identity', 'object'), - ]) + self.assertListEquals(sorted(rbc(e, 'main', 'hidden')), + sorted([('has_text', 'subject'), + ('identity', 'subject'), + ('tags', 'object'), + ('for_user', 'object'), + ('created_by', 'object'), + ('wf_info_for', 'object'), + ('owned_by', 'object'), + ('identity', 'object'), + ])) def test_inlined_view(self): self.failUnless('main_inlined' in AFS.etype_get('CWUser', 'use_email', 'subject', 'EmailAddress')) @@ -122,10 +123,8 @@ ('connait', 'object') ]) self.assertListEquals(rbc(e, 'main', 'hidden'), - [('is', 'subject'), - ('has_text', 'subject'), + [('has_text', 'subject'), ('identity', 'subject'), - ('is_instance_of', 'subject'), ('identity', 'object'), ]) diff -r bf3453789887 -r 70d47389630c web/uicfg.py --- a/web/uicfg.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/uicfg.py Mon Feb 15 18:44:47 2010 +0100 @@ -312,6 +312,10 @@ formsections.add('%s_%s' % (formtype, section)) def tag_relation(self, key, formtype, section=None): + if isinstance(formtype, tuple): + for ftype in formtype: + self.tag_relation(key, ftype, section) + return if section is None: tag = formtype for formtype, section in self.bw_tag_map[tag].iteritems(): @@ -343,8 +347,8 @@ # overriden to avoid recomputing done in parent classes return self._tagdefs.get(key, ()) - def relations_by_section(self, entity, formtype, section, - permission=None, strict=False): + def relations_by_section(self, entity, formtype, section, permission, + strict=False): """return a list of (relation schema, target schemas, role) for the given entity matching categories and permission. @@ -359,52 +363,60 @@ else: eid = None strict = False + if permission == 'update': + assert section in ('attributes', 'metadata', 'hidden') + relpermission = 'add' + else: + assert section not in ('attributes', 'metadata', 'hidden') + relpermission = permission cw = entity._cw for rschema, targetschemas, role in eschema.relation_definitions(True): - # check category first, potentially lower cost than checking - # permission which may imply rql queries _targetschemas = [] for tschema in targetschemas: + # check section's tag first, potentially lower cost than + # checking permission which may imply rql queries if not tag in self.etype_get(eschema, rschema, role, tschema): continue rdef = rschema.role_rdef(eschema, tschema, role) - if permission is not None and \ - not ((not strict and rdef.has_local_role(permission)) or - rdef.has_perm(cw, permission, fromeid=eid)): - continue + if rschema.final: + if not rdef.has_perm(cw, permission, eid=eid): + continue + elif strict or not rdef.has_local_role(relpermission): + if role == 'subject': + if not rdef.has_perm(cw, relpermission, fromeid=eid): + continue + elif role == 'object': + if not rdef.has_perm(cw, relpermission, toeid=eid): + continue _targetschemas.append(tschema) if not _targetschemas: continue targetschemas = _targetschemas - if permission is not None: - rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0]) - # tag allowing to hijack the permission machinery when - # permission is not verifiable until the entity is actually - # created... - if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role): - yield (rschema, targetschemas, role) + rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0]) + # XXX tag allowing to hijack the permission machinery when + # permission is not verifiable until the entity is actually + # created... + if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role): + yield (rschema, targetschemas, role) + continue + if not rschema.final and role == 'subject': + # on relation with cardinality 1 or ?, we need delete perm as well + # if the relation is already set + if (relpermission == 'add' + and rdef.role_cardinality(role) in '1?' + and eid and entity.related(rschema.type, role) + and not rdef.has_perm(cw, 'delete', fromeid=eid, + toeid=entity.related(rschema.type, role)[0][0])): continue - if rschema.final: - if not rdef.has_perm(cw, permission, fromeid=eid): - continue - elif role == 'subject': - # on relation with cardinality 1 or ?, we need delete perm as well - # if the relation is already set - if (permission == 'add' - and rdef.role_cardinality(role) in '1?' - and eid and entity.related(rschema.type, role) - and not rdef.has_perm(cw, 'delete', fromeid=eid, - toeid=entity.related(rschema.type, role)[0][0])): - continue - elif role == 'object': - # on relation with cardinality 1 or ?, we need delete perm as well - # if the relation is already set - if (permission == 'add' - and rdef.role_cardinality(role) in '1?' - and eid and entity.related(rschema.type, role) - and not rdef.has_perm(cw, 'delete', toeid=eid, - fromeid=entity.related(rschema.type, role)[0][0])): - continue + elif role == 'object': + # on relation with cardinality 1 or ?, we need delete perm as well + # if the relation is already set + if (relpermission == 'add' + and rdef.role_cardinality(role) in '1?' + and eid and entity.related(rschema.type, role) + and not rdef.has_perm(cw, 'delete', toeid=eid, + fromeid=entity.related(rschema.type, role)[0][0])): + continue yield (rschema, targetschemas, role) autoform_section = AutoformSectionRelationTags('autoform_section') @@ -442,8 +454,8 @@ class AutoformIsInlined(RelationTags): """XXX for < 3.6 bw compat""" def tag_relation(self, key, tag): - warn('autoform_is_inlined rtag is deprecated, use autoform_section ' - 'with inlined formtype and "attributes" or "hidden" section', + warn('autoform_is_inlined is deprecated, use autoform_section ' + 'with formtype="inlined", section="attributes" or section="hidden"', DeprecationWarning, stacklevel=3) section = tag and 'inlined' or 'hidden' autoform_section.tag_relation(key, 'main', section) diff -r bf3453789887 -r 70d47389630c web/views/actions.py --- a/web/views/actions.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/actions.py Mon Feb 15 18:44:47 2010 +0100 @@ -31,7 +31,7 @@ # if user has no update right but it can modify some relation, # display action anyway form = entity._cw.vreg['forms'].select('edition', entity._cw, - entity=entity) + entity=entity) for dummy in form.editable_relations(): return 1 try: diff -r bf3453789887 -r 70d47389630c web/views/autoform.py --- a/web/views/autoform.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/autoform.py Mon Feb 15 18:44:47 2010 +0100 @@ -85,7 +85,10 @@ def __init__(self, *args, **kwargs): for attr in self._select_attrs: - setattr(self, attr, kwargs.pop(attr, None)) + # don't pop attributes from kwargs, so the end-up in + # self.cw_extra_kwargs which is then passed to the edition form (see + # the .form method) + setattr(self, attr, kwargs.get(attr)) super(InlineEntityEditionFormView, self).__init__(*args, **kwargs) def _entity(self): @@ -384,7 +387,11 @@ related = [] if entity.has_eid(): rset = entity.related(rschema, role, limit=form.related_limit) - if rschema.has_perm(form._cw, 'delete'): + if role == 'subject': + haspermkwargs = {'fromeid': entity.eid} + else: + haspermkwargs = {'toeid': entity.eid} + if rschema.has_perm(form._cw, 'delete', **haspermkwargs): toggleable_rel_link_func = toggleable_relation_link else: toggleable_rel_link_func = lambda x, y, z: u'' @@ -650,7 +657,7 @@ return self.display_fields # XXX we should simply put eid in the generated section, no? return [(rtype, role) for rtype, _, role in self._relations_by_section( - 'attributes', strict=strict) if rtype != 'eid'] + 'attributes', 'update', strict) if rtype != 'eid'] def editable_relations(self): """return a sorted list of (relation's label, relation'schema, role) for diff -r bf3453789887 -r 70d47389630c web/views/calendar.py --- a/web/views/calendar.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/calendar.py Mon Feb 15 18:44:47 2010 +0100 @@ -155,7 +155,9 @@ last_day_of_month = date(year + 1, 1, 1) - timedelta(1) else: last_day_of_month = date(year, month + 1, 1) - timedelta(1) - lastday = last_day_of_month + timedelta(6 - last_day_of_month.weekday()) + # date range exclude last day so we should at least add one day, hence + # the 7 + lastday = last_day_of_month + timedelta(7 - last_day_of_month.weekday()) month_dates = list(date_range(firstday, lastday)) dates = {} task_max = 0 @@ -250,7 +252,6 @@ # output header self.w(u'%s%s%s%s%s%s%s' % tuple(self._cw._(day) for day in WEEKDAYS)) - # build calendar for mdate, task_rows in zip(month_dates, days): if mdate.weekday() == 0: diff -r bf3453789887 -r 70d47389630c web/views/formrenderers.py --- a/web/views/formrenderers.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/formrenderers.py Mon Feb 15 18:44:47 2010 +0100 @@ -92,7 +92,7 @@ self.render_fields(w, form, values) self.render_buttons(w, form) w(u'') - w(u'') + w(self.close_form(form, values)) errormsg = self.error_message(form) if errormsg: data.insert(0, errormsg) @@ -171,6 +171,13 @@ tag += ' cubicweb:target="%s"' % xml_escape(form.cwtarget) return tag + '>' + def close_form(self, form, values): + """seem dump but important for consistency w/ close form, and necessary + for form renderers overriding open_form to use something else or more than + and
+ """ + return '
' + def render_fields(self, w, form, values): fields = self._render_hidden_fields(w, form) if fields: diff -r bf3453789887 -r 70d47389630c web/views/forms.py --- a/web/views/forms.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/forms.py Mon Feb 15 18:44:47 2010 +0100 @@ -266,6 +266,9 @@ self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row, col=self.cw_col, entity=self.edited_entity) + def should_display_add_new_relation_link(self, rschema, existant, card): + return False + # controller side method (eg POST reception handling) def actual_eid(self, eid): @@ -284,9 +287,6 @@ def editable_relations(self): return () - def should_display_add_new_relation_link(self, rschema, existant, card): - return False - @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function') def subject_relation_vocabulary(self, rtype, limit=None): """defaut vocabulary method for the given relation, looking for diff -r bf3453789887 -r 70d47389630c web/views/primary.py --- a/web/views/primary.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/primary.py Mon Feb 15 18:44:47 2010 +0100 @@ -46,8 +46,8 @@ self.render_entity(entity) def render_entity(self, entity): + self.render_entity_toolbox(entity) self.render_entity_title(entity) - self.render_entity_toolbox(entity) # entity's attributes and relations, excluding meta data # if the entity isn't meta itself if self.is_primary(): @@ -161,7 +161,7 @@ try: label, rset, vid, dispctrl = box except ValueError: - warn('box views should now be defined as a 4-uple (label, rset, vid, dispctrl), ' + warn('[3.5] box views should now be defined as a 4-uple (label, rset, vid, dispctrl), ' 'please update %s' % self.__class__.__name__, DeprecationWarning) label, rset, vid = box diff -r bf3453789887 -r 70d47389630c web/views/schema.py --- a/web/views/schema.py Thu Feb 11 12:19:08 2010 +0100 +++ b/web/views/schema.py Mon Feb 15 18:44:47 2010 +0100 @@ -380,7 +380,6 @@ def _generate(self, tmpfile): """display global schema information""" - print 'skipedtypes', skip_types(self._cw) visitor = FullSchemaVisitor(self._cw, self._cw.vreg.schema, skiptypes=skip_types(self._cw)) s2d.schema2dot(outputfile=tmpfile, visitor=visitor)