# HG changeset patch # User Alexandre Fayolle # Date 1267721805 -3600 # Node ID d175ce5c2e8581365a5ee7f31d5702bacaa58dba # Parent e5efbcaa30119c02cfc13f651c674eeff96dcd3b# Parent f531742e85f4a32124774ae7dbed2f3a63460c2b merge diff -r e5efbcaa3011 -r d175ce5c2e85 cwctl.py --- a/cwctl.py Thu Mar 04 17:26:43 2010 +0100 +++ b/cwctl.py Thu Mar 04 17:56:45 2010 +0100 @@ -204,7 +204,7 @@ # simplify constraints if versions: for constraint in versions: - op, ver = constraint.split() + op, ver = constraint if oper is None: oper = op version = ver @@ -238,7 +238,12 @@ for name, constraint in use.items(): self.constraints.setdefault(name,set()) if constraint: - self.constraints[name].add(constraint) + try: + oper, version = constraint.split() + self.constraints[name].add( (oper, version) ) + except: + self.warnings.append('cube %s depends on %s but constraint badly formatted: %s' + % (cube, name, constraint)) self.reverse_constraints.setdefault(name, set()).add(cube) class ListCommand(Command): diff -r e5efbcaa3011 -r d175ce5c2e85 devtools/__init__.py --- a/devtools/__init__.py Thu Mar 04 17:26:43 2010 +0100 +++ b/devtools/__init__.py Thu Mar 04 17:56:45 2010 +0100 @@ -30,8 +30,8 @@ SYSTEM_RELATIONS = schema.META_RTYPES | set(( # workflow related - 'workflow_of', 'state_of', 'transition_of', 'initial_state', 'allowed_transition', - 'destination_state', 'from_state', 'to_state', + 'workflow_of', 'state_of', 'transition_of', 'initial_state', 'default_workflow', + 'allowed_transition', 'destination_state', 'from_state', 'to_state', 'condition', 'subworkflow', 'subworkflow_state', 'subworkflow_exit', 'custom_workflow', 'in_state', 'wf_info_for', # cwproperty diff -r e5efbcaa3011 -r d175ce5c2e85 doc/book/en/admin/additional-tips.rst --- a/doc/book/en/admin/additional-tips.rst Thu Mar 04 17:26:43 2010 +0100 +++ b/doc/book/en/admin/additional-tips.rst Thu Mar 04 17:56:45 2010 +0100 @@ -28,7 +28,7 @@ Simply use the pg_dump in a cron :: - pg_dump -Fc --username=cubicweb --no-owner --file=/var/lib/cubicweb/backup/-$(date '+%Y-%m-%d_%H:%M:%S').dump + su -c "pg_dump -Fc --username=cubicweb --no-owner" postgres > -$(date '+%Y-%m-%d_%H:%M:%S').dump **CubicWeb way** diff -r e5efbcaa3011 -r d175ce5c2e85 entity.py --- a/entity.py Thu Mar 04 17:26:43 2010 +0100 +++ b/entity.py Thu Mar 04 17:56:45 2010 +0100 @@ -797,7 +797,7 @@ del self.__unique except AttributeError: pass - + # raw edition utilities ################################################### def set_attributes(self, _cw_unsafe=False, **kwargs): diff -r e5efbcaa3011 -r d175ce5c2e85 schema.py --- a/schema.py Thu Mar 04 17:26:43 2010 +0100 +++ b/schema.py Thu Mar 04 17:56:45 2010 +0100 @@ -50,8 +50,9 @@ )) WORKFLOW_TYPES = set(('Transition', 'State', 'TrInfo', 'Workflow', - 'WorkflowTransition', 'BaseTransition', - 'SubWorkflowExitPoint')) + 'WorkflowTransition', 'BaseTransition', + 'SubWorkflowExitPoint')) + INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri')) @@ -63,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): @@ -369,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') @@ -491,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 @@ -1054,8 +1083,16 @@ cw = entity._cw elif form is not None: cw = form._cw - if cw is not None and cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT): - return self.regular_formats + tuple(NEED_PERM_FORMATS) + 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 diff -r e5efbcaa3011 -r d175ce5c2e85 schemas/__init__.py --- a/schemas/__init__.py Thu Mar 04 17:26:43 2010 +0100 +++ b/schemas/__init__.py Thu Mar 04 17:56:45 2010 +0100 @@ -1,38 +1,25 @@ """some utilities to define schema permissions :organization: Logilab -:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2008-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" from rql.utils import quote -from cubicweb.schema import ERQLExpression, RRQLExpression +from cubicweb.schema import RO_REL_PERMS, RO_ATTR_PERMS, \ + PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, \ + ERQLExpression, RRQLExpression # permissions for "meta" entity type (readable by anyone, can only be # added/deleted by managers) -META_ETYPE_PERMS = { - 'read': ('managers', 'users', 'guests',), - 'add': ('managers',), - 'delete': ('managers',), - 'update': ('managers', 'owners',), - } - +META_ETYPE_PERMS = PUB_SYSTEM_ENTITY_PERMS # XXX deprecates # permissions for "meta" relation type (readable by anyone, can only be # added/deleted by managers) -META_RTYPE_PERMS = { - 'read': ('managers', 'users', 'guests',), - 'add': ('managers',), - 'delete': ('managers',), - } - +META_RTYPE_PERMS = PUB_SYSTEM_REL_PERMS # XXX deprecates # permissions for relation type that should only set by hooks using unsafe # execute, readable by anyone -HOOKS_RTYPE_PERMS = { - 'read': ('managers', 'users', 'guests',), - 'add': (), - 'delete': (), - } +HOOKS_RTYPE_PERMS = RO_REL_PERMS # XXX deprecates def _perm(names): if isinstance(names, (list, tuple)): diff -r e5efbcaa3011 -r d175ce5c2e85 schemas/base.py --- a/schemas/base.py Thu Mar 04 17:26:43 2010 +0100 +++ b/schemas/base.py Thu Mar 04 17:56:45 2010 +0100 @@ -10,9 +10,9 @@ from yams.buildobjs import (EntityType, RelationType, SubjectRelation, String, Datetime, Password) -from cubicweb.schema import (RQLConstraint, WorkflowableEntityType, - ERQLExpression, RRQLExpression) -from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS +from cubicweb.schema import ( + RQLConstraint, WorkflowableEntityType, ERQLExpression, RRQLExpression, + PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS) class CWUser(WorkflowableEntityType): """define a CubicWeb user""" @@ -85,7 +85,7 @@ class in_group(RelationType): """core relation indicating a user's groups""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS class owned_by(RelationType): """core relation indicating owners of an entity. This relation @@ -118,18 +118,21 @@ class creation_date(RelationType): """creation time of an entity""" + __permissions__ = PUB_SYSTEM_ATTR_PERMS cardinality = '11' subject = '*' object = 'Datetime' class modification_date(RelationType): """latest modification time of an entity""" + __permissions__ = PUB_SYSTEM_ATTR_PERMS cardinality = '11' subject = '*' object = 'Datetime' class cwuri(RelationType): """internal entity uri""" + __permissions__ = PUB_SYSTEM_ATTR_PERMS cardinality = '11' subject = '*' object = 'String' @@ -155,7 +158,7 @@ class CWPermission(EntityType): """entity type that may be used to construct some advanced security configuration """ - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=100, description=_('name or identifier of the permission')) @@ -170,11 +173,11 @@ """link a permission to the entity. This permission should be used in the security definition of the entity's type to be useful. """ - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS class require_group(RelationType): """used to grant a permission to a group""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS class ExternalUri(EntityType): @@ -209,6 +212,8 @@ Also, checkout the AppObject.get_cache() method. """ + # XXX only handle by hooks, shouldn't be readable/editable at all through + # the ui and so no permissions should be granted, no? __permissions__ = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), diff -r e5efbcaa3011 -r d175ce5c2e85 schemas/bootstrap.py --- a/schemas/bootstrap.py Thu Mar 04 17:26:43 2010 +0100 +++ b/schemas/bootstrap.py Thu Mar 04 17:56:45 2010 +0100 @@ -10,14 +10,16 @@ from yams.buildobjs import (EntityType, RelationType, RelationDefinition, SubjectRelation, RichString, String, Boolean, Int) -from cubicweb.schema import RQLConstraint -from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS +from cubicweb.schema import ( + RQLConstraint, + PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS + ) # not restricted since as "is" is handled as other relations, guests need # access to this class CWEType(EntityType): """define an entity type, used to build the instance schema""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) description = RichString(internationalizable=True, @@ -28,7 +30,7 @@ class CWRType(EntityType): """define a relation type, used to build the instance schema""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) description = RichString(internationalizable=True, @@ -48,7 +50,7 @@ used to build the instance schema """ - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final TRUE')], composite='object') @@ -85,7 +87,7 @@ used to build the instance schema """ - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final FALSE')], composite='object') @@ -116,7 +118,7 @@ # not restricted since it has to be read when checking allowed transitions class RQLExpression(EntityType): """define a rql expression used to define permissions""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS exprtype = String(required=True, vocabulary=['ERQLExpression', 'RRQLExpression']) mainvars = String(maxsize=8, description=_('name of the main variables which should be ' @@ -134,14 +136,14 @@ class CWConstraint(EntityType): """define a schema constraint""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS cstrtype = SubjectRelation('CWConstraintType', cardinality='1*') value = String(description=_('depends on the constraint type')) class CWConstraintType(EntityType): """define a schema constraint type""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) @@ -149,7 +151,7 @@ # not restricted since it has to be read when checking allowed transitions class CWGroup(EntityType): """define a CubicWeb users group""" - __permissions__ = META_ETYPE_PERMS + __permissions__ = PUB_SYSTEM_ENTITY_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) @@ -173,32 +175,32 @@ class relation_type(RelationType): """link a relation definition to its relation type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS inlined = True class from_entity(RelationType): """link a relation definition to its subject entity type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS inlined = True class to_entity(RelationType): """link a relation definition to its object entity type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS inlined = True class constrained_by(RelationType): """constraints applying on this relation""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS class cstrtype(RelationType): """constraint factory""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS inlined = True class read_permission_cwgroup(RelationDefinition): """groups allowed to read entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'read_permission' subject = ('CWEType', 'CWAttribute', 'CWRelation') object = 'CWGroup' @@ -206,7 +208,7 @@ class add_permission_cwgroup(RelationDefinition): """groups allowed to add entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'add_permission' subject = ('CWEType', 'CWRelation') object = 'CWGroup' @@ -214,7 +216,7 @@ class delete_permission_cwgroup(RelationDefinition): """groups allowed to delete entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'delete_permission' subject = ('CWEType', 'CWRelation') object = 'CWGroup' @@ -222,7 +224,7 @@ class update_permission_cwgroup(RelationDefinition): """groups allowed to update entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'update_permission' subject = ('CWEType', 'CWAttribute') object = 'CWGroup' @@ -230,7 +232,7 @@ class read_permission_rqlexpr(RelationDefinition): """rql expression allowing to read entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'read_permission' subject = ('CWEType', 'CWAttribute', 'CWRelation') object = 'RQLExpression' @@ -239,7 +241,7 @@ class add_permission_rqlexpr(RelationDefinition): """rql expression allowing to add entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'add_permission' subject = ('CWEType', 'CWRelation') object = 'RQLExpression' @@ -248,7 +250,7 @@ class delete_permission_rqlexpr(RelationDefinition): """rql expression allowing to delete entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'delete_permission' subject = ('CWEType', 'CWRelation') object = 'RQLExpression' @@ -257,7 +259,7 @@ class update_permission_rqlexpr(RelationDefinition): """rql expression allowing to update entities/relations of this type""" - __permissions__ = META_RTYPE_PERMS + __permissions__ = PUB_SYSTEM_REL_PERMS name = 'update_permission' subject = ('CWEType', 'CWAttribute') object = 'RQLExpression' @@ -305,3 +307,13 @@ cardinality = '?*' subject = 'CWEType' object = 'CWEType' + +def post_build_callback(schema): + """set attributes permissions for schema/workflow entities""" + from cubicweb.schema import SCHEMA_TYPES, WORKFLOW_TYPES, META_RTYPES + for eschema in schema.entities(): + if eschema in SCHEMA_TYPES or eschema in WORKFLOW_TYPES: + for rschema in eschema.subject_relations(): + if rschema.final and not rschema in META_RTYPES: + rdef = eschema.rdef(rschema) + rdef.permissions = PUB_SYSTEM_ATTR_PERMS diff -r e5efbcaa3011 -r d175ce5c2e85 server/__init__.py --- a/server/__init__.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/__init__.py Thu Mar 04 17:56:45 2010 +0100 @@ -186,6 +186,7 @@ handler.install_custom_sql_scripts(join(CW_SOFTWARE_ROOT, 'schemas'), driver) for directory in reversed(config.cubes_path()): handler.install_custom_sql_scripts(join(directory, 'schema'), driver) + # serialize the schema initialize_schema(config, schema, handler) # yoo ! cnx.commit() diff -r e5efbcaa3011 -r d175ce5c2e85 server/querier.py --- a/server/querier.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/querier.py Thu Mar 04 17:56:45 2010 +0100 @@ -358,6 +358,7 @@ self.preprocess(rqlst, security=False) return rqlst + class InsertPlan(ExecutionPlan): """an execution model specific to the INSERT rql query """ diff -r e5efbcaa3011 -r d175ce5c2e85 server/repository.py --- a/server/repository.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/repository.py Thu Mar 04 17:56:45 2010 +0100 @@ -105,7 +105,8 @@ # skip that for super session (though we can still skip it for internal # sessions). Also we should imo rely on the orm to first fetch existing # entity if any then delete it. - if session.is_internal_session: + if session.is_internal_session \ + or not session.vreg.config.is_hook_category_activated('integrity'): return card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality') # one may be tented to check for neweids but this may cause more than one diff -r e5efbcaa3011 -r d175ce5c2e85 server/schemaserial.py --- a/server/schemaserial.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/schemaserial.py Thu Mar 04 17:56:45 2010 +0100 @@ -51,9 +51,6 @@ return res # schema / perms deserialization ############################################## -OLD_SCHEMA_TYPES = frozenset(('EFRDef', 'ENFRDef', 'ERType', 'EEType', - 'EConstraintType', 'EConstraint', 'EGroup', - 'EUser', 'ECache', 'EPermission', 'EProperty')) def deserialize_schema(schema, session): """return a schema according to information stored in an rql database @@ -90,12 +87,6 @@ {'x': eid, 'n': netype}) session.system_sql('UPDATE entities SET type=%(n)s WHERE type=%(x)s', {'x': etype, 'n': netype}) - # XXX should be donne as well on sqlite based sources - if not etype in OLD_SCHEMA_TYPES and \ - (getattr(dbhelper, 'case_sensitive', False) - or etype.lower() != netype.lower()): - session.system_sql('ALTER TABLE %s%s RENAME TO %s%s' % ( - sqlutils.SQL_PREFIX, etype, sqlutils.SQL_PREFIX, netype)) session.commit(False) try: session.system_sql('UPDATE deleted_entities SET type=%(n)s WHERE type=%(x)s', @@ -182,6 +173,17 @@ res.setdefault(eid, {}).setdefault(action, []).append( (expr, mainvars, expreid) ) return res +def deserialize_rdef_constraints(session): + """return the list of relation definition's constraints as instances""" + res = {} + for rdefeid, ceid, ct, val in session.execute( + 'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, ' + 'X cstrtype T, T name TN, X value V', build_descr=False): + cstr = CONSTRAINTS[ct].deserialize(val) + cstr.eid = ceid + res.setdefault(rdefeid, []).append(cstr) + return res + def set_perms(erschema, permsdict): """set permissions on the given erschema according to the permission definition dictionary as built by deserialize_ertype_permissions for a @@ -202,21 +204,9 @@ for p in somethings) -def deserialize_rdef_constraints(session): - """return the list of relation definition's constraints as instances""" - res = {} - for rdefeid, ceid, ct, val in session.execute( - 'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, ' - 'X cstrtype T, T name TN, X value V', build_descr=False): - cstr = CONSTRAINTS[ct].deserialize(val) - cstr.eid = ceid - res.setdefault(rdefeid, []).append(cstr) - return res - - # schema / perms serialization ################################################ -def serialize_schema(cursor, schema, verbose=False): +def serialize_schema(cursor, schema): """synchronize schema and permissions in the database according to current schema """ @@ -224,37 +214,43 @@ if not quiet: _title = '-> storing the schema in the database ' print _title, - execute = cursor.execute + execute = cursor.unsafe_execute eschemas = schema.entities() - aller = eschemas + schema.relations() - if not verbose and not quiet: - pb_size = len(aller) + len(CONSTRAINTS) + len([x for x in eschemas if x.specializes()]) + if not quiet: + pb_size = (len(eschemas + schema.relations()) + + len(CONSTRAINTS) + + len([x for x in eschemas if x.specializes()])) pb = ProgressBar(pb_size, title=_title) else: pb = None + groupmap = group_mapping(cursor, interactive=False) + # serialize all entity types, assuring CWEType is serialized first for proper + # is / is_instance_of insertion + eschemas.remove(schema.eschema('CWEType')) + eschemas.insert(0, schema.eschema('CWEType')) + for eschema in eschemas: + for rql, kwargs in eschema2rql(eschema, groupmap): + execute(rql, kwargs, build_descr=False) + if pb is not None: + pb.update() + # serialize constraint types rql = 'INSERT CWConstraintType X: X name %(ct)s' for cstrtype in CONSTRAINTS: - if verbose: - print rql execute(rql, {'ct': unicode(cstrtype)}, build_descr=False) if pb is not None: pb.update() - groupmap = group_mapping(cursor, interactive=False) - for ertype in aller: - # skip eid and has_text relations - if ertype in VIRTUAL_RTYPES: + # serialize relations + for rschema in schema.relations(): + # skip virtual relations such as eid, has_text and identity + if rschema in VIRTUAL_RTYPES: if pb is not None: pb.update() continue - for rql, kwargs in erschema2rql(schema[ertype], groupmap): - if verbose: - print rql % kwargs + for rql, kwargs in rschema2rql(rschema, groupmap=groupmap): execute(rql, kwargs, build_descr=False) if pb is not None: pb.update() for rql, kwargs in specialize2rql(schema): - if verbose: - print rql % kwargs execute(rql, kwargs, build_descr=False) if pb is not None: pb.update() diff -r e5efbcaa3011 -r d175ce5c2e85 server/serverconfig.py --- a/server/serverconfig.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/serverconfig.py Thu Mar 04 17:56:45 2010 +0100 @@ -185,6 +185,8 @@ # check user's state at login time consider_user_state = True + # XXX hooks control stuff should probably be on the session, not on the config + # hooks activation configuration # all hooks should be activated during normal execution disabled_hooks_categories = set() @@ -232,9 +234,13 @@ @classmethod def is_hook_activated(cls, hook): + return cls.is_hook_category_activated(hook.category) + + @classmethod + def is_hook_category_activated(cls, category): if cls.hooks_mode is cls.DENY_ALL: - return hook.category in cls.enabled_hooks_categories - return hook.category not in cls.disabled_hooks_categories + return category in cls.enabled_hooks_categories + return category not in cls.disabled_hooks_categories # should some hooks be deactivated during [pre|post]create script execution free_wheel = False diff -r e5efbcaa3011 -r d175ce5c2e85 server/session.py --- a/server/session.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/session.py Thu Mar 04 17:56:45 2010 +0100 @@ -499,7 +499,7 @@ operation.handle_event('revert%s_event' % trstate) # XXX use slice notation since self.pending_operations is a # read-only property. - self.pending_operations[:] = processed + self.pending_operations + self.pending_operations[:] = processed + self.pending_operations self.rollback(reset_pool) raise self.pool.commit() diff -r e5efbcaa3011 -r d175ce5c2e85 server/sources/rql2sql.py --- a/server/sources/rql2sql.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/sources/rql2sql.py Thu Mar 04 17:56:45 2010 +0100 @@ -41,6 +41,8 @@ from cubicweb.server.sqlutils import SQL_PREFIX from cubicweb.server.utils import cleanup_solutions +ColumnAlias._q_invariant = False # avoid to check for ColumnAlias / Variable + def _new_var(select, varname): newvar = select.get_variable(varname) if not 'relations' in newvar.stinfo: @@ -711,7 +713,7 @@ return '%s=%s' % (lhssql, rhsvar.accept(self)) def _process_relation_term(self, relation, rid, termvar, termconst, relfield): - if termconst or isinstance(termvar, ColumnAlias) or not termvar._q_invariant: + if termconst or not termvar._q_invariant: termsql = termconst and termconst.accept(self) or termvar.accept(self) yield '%s.%s=%s' % (rid, relfield, termsql) elif termvar._q_invariant: diff -r e5efbcaa3011 -r d175ce5c2e85 server/test/unittest_querier.py --- a/server/test/unittest_querier.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/test/unittest_querier.py Thu Mar 04 17:56:45 2010 +0100 @@ -257,7 +257,7 @@ result, descr = rset.rows, rset.description self.assertEquals(descr[0][0], 'String') self.assertEquals(descr[0][1], 'Int') - self.assertEquals(result[0][0], 'RQLExpression') # XXX may change as schema evolve + self.assertEquals(result[0][0], 'CWRelation') # XXX may change as schema evolve def test_select_groupby_orderby(self): rset = self.execute('Any N GROUPBY N ORDERBY N WHERE X is CWGroup, X name N') @@ -862,6 +862,14 @@ self.assert_(rset.rows) self.assertEquals(rset.description, [('Personne', 'Societe',)]) + def test_insert_5bis(self): + peid = self.execute("INSERT Personne X: X nom 'bidule'")[0][0] + self.execute("INSERT Societe Y: Y nom 'toto', X travaille Y WHERE X eid %(x)s", + {'x': peid}, 'x') + rset = self.execute('Any X, Y WHERE X nom "bidule", Y nom "toto", X travaille Y') + self.assert_(rset.rows) + self.assertEquals(rset.description, [('Personne', 'Societe',)]) + def test_insert_6(self): self.execute("INSERT Personne X, Societe Y: X nom 'bidule', Y nom 'toto', X travaille Y") rset = self.execute('Any X, Y WHERE X nom "bidule", Y nom "toto", X travaille Y') diff -r e5efbcaa3011 -r d175ce5c2e85 server/test/unittest_schemaserial.py --- a/server/test/unittest_schemaserial.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/test/unittest_schemaserial.py Thu Mar 04 17:56:45 2010 +0100 @@ -149,7 +149,6 @@ ('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 2}), ('SET X add_permission Y WHERE Y eid %(g)s, ', {'g': 0}), ('SET X update_permission Y WHERE Y eid %(g)s, ', {'g': 0}), - ('SET X update_permission Y WHERE Y eid %(g)s, ', {'g': 3}), ('SET X delete_permission Y WHERE Y eid %(g)s, ', {'g': 0}), ]) @@ -168,11 +167,6 @@ ('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 1}), ('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 2}), ('SET X update_permission Y WHERE Y eid %(g)s, ', {'g': 0}), - ('INSERT RQLExpression E: ' - 'E expression %(e)s, E exprtype %(t)s, E mainvars %(v)s, ' - 'X update_permission E ' - 'WHERE ', # completed by the outer function - {'e': u'U has_update_permission X', 't': u'ERQLExpression', 'v': u'X'}), ]) #def test_perms2rql(self): diff -r e5efbcaa3011 -r d175ce5c2e85 server/test/unittest_security.py --- a/server/test/unittest_security.py Thu Mar 04 17:26:43 2010 +0100 +++ b/server/test/unittest_security.py Thu Mar 04 17:56:45 2010 +0100 @@ -257,6 +257,26 @@ self.assertEquals(rset.rows, [[aff2]]) rset = cu.execute('Affaire X WHERE NOT X eid %(x)s', {'x': aff2}, 'x') self.assertEquals(rset.rows, []) + # test can't update an attribute of an entity that can't be readen + self.assertRaises(Unauthorized, cu.execute, 'SET X sujet "hacked" WHERE X eid %(x)s', {'x': eid}, 'x') + + + def test_entity_created_in_transaction(self): + affschema = self.schema['Affaire'] + origperms = affschema.permissions['read'] + affschema.set_action_permissions('read', affschema.permissions['add']) + try: + cnx = self.login('iaminusersgrouponly') + cu = cnx.cursor() + aff2 = cu.execute("INSERT Affaire X: X sujet 'cool'")[0][0] + # entity created in transaction are readable *by eid* + self.failUnless(cu.execute('Any X WHERE X eid %(x)s', {'x':aff2}, 'x')) + # XXX would be nice if it worked + rset = cu.execute("Affaire X WHERE X sujet 'cool'") + self.assertEquals(len(rset), 0) + finally: + affschema.set_action_permissions('read', origperms) + cnx.close() def test_read_erqlexpr_has_text1(self): aff1 = self.execute("INSERT Affaire X: X sujet 'cool'")[0][0] diff -r e5efbcaa3011 -r d175ce5c2e85 web/data/jquery.corner.js --- a/web/data/jquery.corner.js Thu Mar 04 17:26:43 2010 +0100 +++ b/web/data/jquery.corner.js Thu Mar 04 17:56:45 2010 +0100 @@ -140,7 +140,7 @@ this.style.position = 'relative'; ds.position = 'absolute'; ds.bottom = ds.left = ds.padding = ds.margin = '0'; - if ($.browser.msie) + if (($.browser.msie) && ($.browser.version < 8.0)) ds.setExpression('width', 'this.parentNode.offsetWidth'); else ds.width = '100%'; diff -r e5efbcaa3011 -r d175ce5c2e85 web/form.py --- a/web/form.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/form.py Thu Mar 04 17:56:45 2010 +0100 @@ -186,6 +186,11 @@ # deleting validation errors here breaks form reloading (errors are # no more available), they have to be deleted by application's publish # method on successful commit + if hasattr(self, '_form_previous_values'): + # XXX behaviour changed in 3.6.1, warn + warn('[3.6.1] restore_previous_post already called, remove this call', + DeprecationWarning, stacklevel=2) + return forminfo = self._cw.get_session_data(sessionkey, pop=True) if forminfo: self._form_previous_values = forminfo['values'] diff -r e5efbcaa3011 -r d175ce5c2e85 web/htmlwidgets.py --- a/web/htmlwidgets.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/htmlwidgets.py Thu Mar 04 17:56:45 2010 +0100 @@ -336,88 +336,3 @@ yield column, self.model.sortvalue(rowindex, column.rset_sortcol) -class ProgressBarWidget(HTMLWidget): - """display a progress bar widget""" - precision = 0.1 - red_threshold = 1.1 - orange_threshold = 1.05 - yellow_threshold = 1 - - def __init__(self, done, todo, total): - self.done = done - self.todo = todo - self.budget = total - - @property - def overrun(self): - """overrun = done + todo - """ - if self.done + self.todo > self.budget: - overrun = self.done + self.todo - self.budget - else: - overrun = 0 - if overrun < self.precision: - overrun = 0 - return overrun - - @property - def overrun_percentage(self): - """pourcentage overrun = overrun / budget""" - if self.budget == 0: - return 0 - else: - return self.overrun * 100. / self.budget - - def _render(self): - done = self.done - todo = self.todo - budget = self.budget - if budget == 0: - pourcent = 100 - else: - pourcent = done*100./budget - if pourcent > 100.1: - color = 'red' - elif todo+done > self.red_threshold*budget: - color = 'red' - elif todo+done > self.orange_threshold*budget: - color = 'orange' - elif todo+done > self.yellow_threshold*budget: - color = 'yellow' - else: - color = 'green' - if pourcent < 0: - pourcent = 0 - - if floor(done) == done or done>100: - done_str = '%i' % done - else: - done_str = '%.1f' % done - if floor(budget) == budget or budget>100: - budget_str = '%i' % budget - else: - budget_str = '%.1f' % budget - - title = u'%s/%s = %i%%' % (done_str, budget_str, pourcent) - short_title = title - if self.overrun_percentage: - title += u' overrun +%sj (+%i%%)' % (self.overrun, - self.overrun_percentage) - overrun = self.overrun - if floor(overrun) == overrun or overrun>100: - overrun_str = '%i' % overrun - else: - overrun_str = '%.1f' % overrun - short_title += u' +%s' % overrun_str - # write bars - maxi = max(done+todo, budget) - if maxi == 0: - maxi = 1 - - cid = random.randint(0, 100000) - self.w(u'%s
' - u'' - u'' - % (short_title.replace(' ',' '), cid, cid, - int(100.*done/maxi), int(100.*(done+todo)/maxi), - int(100.*budget/maxi), color)) diff -r e5efbcaa3011 -r d175ce5c2e85 web/test/unittest_views_editforms.py --- a/web/test/unittest_views_editforms.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/test/unittest_views_editforms.py Thu Mar 04 17:56:45 2010 +0100 @@ -43,13 +43,11 @@ ('firstname', 'subject'), ('surname', 'subject'), ('in_group', 'subject'), - ('eid', 'subject'), ]) self.assertListEquals(rbc(e, 'muledit', 'attributes'), [('login', 'subject'), ('upassword', 'subject'), ('in_group', 'subject'), - ('eid', 'subject'), ]) self.assertListEquals(rbc(e, 'main', 'metadata'), [('last_login_time', 'subject'), @@ -76,13 +74,10 @@ # owned_by is defined both as subject and object relations on CWUser self.assertListEquals(sorted(x for x in rbc(e, 'main', 'hidden') if x != ('tags', 'object')), - sorted([('has_text', 'subject'), - ('identity', 'subject'), - ('for_user', 'object'), + sorted([('for_user', 'object'), ('created_by', 'object'), ('wf_info_for', 'object'), ('owned_by', 'object'), - ('identity', 'object'), ])) def test_inlined_view(self): @@ -106,11 +101,9 @@ ('test', 'subject'), ('description', 'subject'), ('salary', 'subject'), - ('eid', 'subject') ]) self.assertListEquals(rbc(e, 'muledit', 'attributes'), [('nom', 'subject'), - ('eid', 'subject') ]) self.assertListEquals(rbc(e, 'main', 'metadata'), [('creation_date', 'subject'), @@ -124,10 +117,7 @@ ('connait', 'object') ]) self.assertListEquals(rbc(e, 'main', 'hidden'), - [('has_text', 'subject'), - ('identity', 'subject'), - ('identity', 'object'), - ]) + []) def test_edition_form(self): rset = self.execute('CWUser X LIMIT 1') diff -r e5efbcaa3011 -r d175ce5c2e85 web/uicfg.py --- a/web/uicfg.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/uicfg.py Thu Mar 04 17:56:45 2010 +0100 @@ -455,7 +455,7 @@ """XXX for < 3.6 bw compat""" def tag_relation(self, key, tag): warn('autoform_is_inlined is deprecated, use autoform_section ' - 'with formtype="inlined", section="attributes" or section="hidden"', + 'with formtype="main", section="inlined"', DeprecationWarning, stacklevel=3) section = tag and 'inlined' or 'hidden' autoform_section.tag_relation(key, 'main', section) diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/autoform.py --- a/web/views/autoform.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/autoform.py Thu Mar 04 17:56:45 2010 +0100 @@ -661,7 +661,7 @@ return [] # XXX we should simply put eid in the generated section, no? return [(rtype, role) for rtype, _, role in self._relations_by_section( - 'attributes', 'update', strict) if rtype != 'eid'] + 'attributes', 'update', strict)] def editable_relations(self): """return a sorted list of (relation's label, relation'schema, role) for diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/basetemplates.py --- a/web/views/basetemplates.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/basetemplates.py Thu Mar 04 17:56:45 2010 +0100 @@ -391,8 +391,9 @@ self.w(u'') self.w(u'\n') self.w(u'\n') - self.wview('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', - title=False, showmessage=False) + if self._cw.cnx.anonymous_connection: + self.wview('logform', rset=self.cw_rset, id='popupLoginBox', + klass='hidden', title=False, showmessage=False) def state_header(self): state = self._cw.search_state diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/formrenderers.py --- a/web/views/formrenderers.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/formrenderers.py Thu Mar 04 17:56:45 2010 +0100 @@ -132,9 +132,9 @@ errors = form.remaining_errors() if errors: if len(errors) > 1: - templstr = '
  • %s
  • \n' + templstr = u'
  • %s
  • \n' else: - templstr = ' %s\n' + templstr = u' %s\n' for field, err in errors: if field is None: errormsg += templstr % err diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/ibreadcrumbs.py --- a/web/views/ibreadcrumbs.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/ibreadcrumbs.py Thu Mar 04 17:56:45 2010 +0100 @@ -104,7 +104,7 @@ class BreadCrumbView(EntityView): __regid__ = 'breadcrumbs' - def cell_call(self, row, col): + def cell_call(self, row, col, **kwargs): entity = self.cw_rset.get_entity(row, col) desc = xml_escape(uilib.cut(entity.dc_description(), 50)) # XXX remember camember : tags.a autoescapes ! @@ -115,7 +115,7 @@ class BreadCrumbTextView(EntityView): __regid__ = 'breadcrumbtext' - def cell_call(self, row, col): + def cell_call(self, row, col, **kwargs): entity = self.cw_rset.get_entity(row, col) textsize = self._cw.property_value('navigation.short-line-size') self.w(uilib.cut(entity.dc_title(), textsize)) diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/iprogress.py --- a/web/views/iprogress.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/iprogress.py Thu Mar 04 17:56:45 2010 +0100 @@ -8,13 +8,15 @@ __docformat__ = "restructuredtext en" _ = unicode +from math import floor + from logilab.mtconverter import xml_escape +from cubicweb.utils import make_uid from cubicweb.selectors import implements from cubicweb.interfaces import IProgress, IMileStone from cubicweb.schema import display_name from cubicweb.view import EntityView -from cubicweb.web.htmlwidgets import ProgressBarWidget class ProgressTableView(EntityView): @@ -182,11 +184,85 @@ title = _('progress bar') __select__ = implements(IProgress) + precision = 0.1 + red_threshold = 1.1 + orange_threshold = 1.05 + yellow_threshold = 1 + + @classmethod + def overrun(cls, entity): + """overrun = done + todo - """ + if entity.done + entity.todo > entity.revised_cost: + overrun = entity.done + entity.todo - entity.revised_cost + else: + overrun = 0 + if overrun < cls.precision: + overrun = 0 + return overrun + + @classmethod + def overrun_percentage(cls, entity): + """pourcentage overrun = overrun / budget""" + if entity.revised_cost == 0: + return 0 + else: + return cls.overrun(entity) * 100. / entity.revised_cost + def cell_call(self, row, col): self._cw.add_css('cubicweb.iprogress.css') self._cw.add_js('cubicweb.iprogress.js') entity = self.cw_rset.get_entity(row, col) - widget = ProgressBarWidget(entity.done, entity.todo, - entity.revised_cost) - self.w(widget.render()) + done = entity.done + todo = entity.todo + budget = entity.revised_cost + if budget == 0: + pourcent = 100 + else: + pourcent = done*100./budget + if pourcent > 100.1: + color = 'red' + elif todo+done > self.red_threshold*budget: + color = 'red' + elif todo+done > self.orange_threshold*budget: + color = 'orange' + elif todo+done > self.yellow_threshold*budget: + color = 'yellow' + else: + color = 'green' + if pourcent < 0: + pourcent = 0 + if floor(done) == done or done>100: + done_str = '%i' % done + else: + done_str = '%.1f' % done + if floor(budget) == budget or budget>100: + budget_str = '%i' % budget + else: + budget_str = '%.1f' % budget + + title = u'%s/%s = %i%%' % (done_str, budget_str, pourcent) + short_title = title + if self.overrun_percentage(entity): + title += u' overrun +%sj (+%i%%)' % (self.overrun(entity), + self.overrun_percentage(entity)) + overrun = self.overrun(entity) + if floor(overrun) == overrun or overrun>100: + overrun_str = '%i' % overrun + else: + overrun_str = '%.1f' % overrun + short_title += u' +%s' % overrun_str + # write bars + maxi = max(done+todo, budget) + if maxi == 0: + maxi = 1 + + cid = make_uid('progress_bar') + self._cw.html_headers.add_onload('draw_progressbar("canvas%s", %i, %i, %i, "%s");' % + (cid, + int(100.*done/maxi), int(100.*(done+todo)/maxi), + int(100.*budget/maxi), color), + jsoncall=self._cw.json_request) + self.w(u'%s
    ' + u'' + % (short_title.replace(' ',' '), cid)) diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/workflow.py --- a/web/views/workflow.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/workflow.py Thu Mar 04 17:56:45 2010 +0100 @@ -67,7 +67,7 @@ form = self.get_form(entity, transition) self.w(u'

    %s %s

    \n' % (self._cw._(transition.name), entity.view('oneline'))) - msg = self.req._('status will change from %(st1)s to %(st2)s') % { + msg = self._cw._('status will change from %(st1)s to %(st2)s') % { 'st1': entity.printable_state, 'st2': self._cw._(transition.destination(entity).name)} self.w(u'

    %s

    \n' % msg) diff -r e5efbcaa3011 -r d175ce5c2e85 web/views/xmlrss.py --- a/web/views/xmlrss.py Thu Mar 04 17:26:43 2010 +0100 +++ b/web/views/xmlrss.py Thu Mar 04 17:56:45 2010 +0100 @@ -188,11 +188,14 @@ self.w(u'%s\n' % xml_escape(entity.absolute_url())) self.render_title_link(entity) - self._marker('description', entity.dc_description(format='text/html')) + self.render_description(entity) self._marker('dc:date', entity.dc_date(self.date_format)) self.render_entity_creator(entity) self.w(u'\n') + def render_description(self, entity): + self._marker('description', entity.dc_description(format='text/html')) + def render_title_link(self, entity): self._marker('title', entity.dc_long_title()) self._marker('link', entity.absolute_url())