merge stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 06 Oct 2009 17:18:23 +0200
branchstable
changeset 3573 cdb41ceeffc7
parent 3572 606ce53dc557 (current diff)
parent 3563 f1dbb9bf4da3 (diff)
child 3574 f179ccbd13e6
merge
--- a/cwctl.py	Tue Oct 06 17:18:10 2009 +0200
+++ b/cwctl.py	Tue Oct 06 17:18:23 2009 +0200
@@ -414,9 +414,6 @@
 the --force option."
             raise ExecutionError(msg % (appid, pidf))
         helper.start_server(config, debug)
-        if not debug:
-            # in debug mode, we reach this point once the instance is stopped...
-            print 'instance %s %s' % (appid, self.actionverb)
 
 
 class StopInstanceCommand(InstanceCommand):
--- a/doc/book/en/B0015-define-permissions.en.txt	Tue Oct 06 17:18:10 2009 +0200
+++ b/doc/book/en/B0015-define-permissions.en.txt	Tue Oct 06 17:18:23 2009 +0200
@@ -3,7 +3,7 @@
 The security model
 ------------------
 
-The security model of `cubicWeb` is based on `Access Control List`. 
+The security model of `cubicWeb` is based on `Access Control List`.
 The main principles are:
 
 * users and groups of users
@@ -29,7 +29,7 @@
   actions if all the other groups the user belongs does not provide
   those permissions
 
-  
+
 Permissions definition
 ``````````````````````
 
@@ -59,14 +59,14 @@
   This can only be used for the actions `update` and `delete` of an entity
   type.
 
-It is also possible to use specific groups if they are defined in the precreate 
+It is also possible to use specific groups if they are defined in the precreate
 of the cube (``migration/precreate.py``).
 
 
 Use of RQL expression for writing rights
 ````````````````````````````````````````
 
-It is possible to define RQL expression to provide update permission 
+It is possible to define RQL expression to provide update permission
 (`add`, `delete` and `update`) on relation and entity types.
 
 RQL expression for entity type permission :
@@ -79,47 +79,66 @@
   respectively on the current entity (on which the action is verified) and
   on the user who send the request
 
-* it is possible to use, in this expression, a special relation 
-  "has_<ACTION>_permission" where the subject is the user and the 
+* it is possible to use, in this expression, a special relation
+  "has_<ACTION>_permission" where the subject is the user and the
   object is a any variable, meaning that the user needs to have
   permission to execute the action <ACTION> on the entities related
-  to this variable 
+  to this variable
 
-For RQL expressions on a relation type, the principles are the same except 
+For RQL expressions on a relation type, the principles are the same except
 for the following :
 
-* you have to use the class `RQLExpression` in the case of a non-final relation
+* you have to use the class `RRQLExpression` in the case of a non-final relation
 
 * in the expression, the variables S, O and U are pre-defined references
   to respectively the subject and the object of the current relation (on
   which the action is being verified) and the user who executed the query
 
-* we can also defined rights on attributes of an entity (non-final relation),
-  knowing that : 
+* we can also define rights on attributes of an entity (non-final
+  relation), knowing that :
 
-  - to defines RQL expression, we have to use the class `ERQLExpression`
-    in which X represents the entity the attribute belongs to
+  - to define RQL expression, we have to use the class
+    `ERQLExpression` in which X represents the entity the attribute
+    belongs to
 
   - the permissions `add` and `delete` are equivalent. Only `add`/`read`
     are actually taken in consideration.
 
 In addition to that the entity type `EPermission` from the standard library
-allow to build very complex and dynamic security architecture. The schema of
+allows to build very complex and dynamic security architecture. The schema of
 this entity type is as follow : ::
 
-    class EPermission(MetaEntityType):
+    class CWPermission(EntityType):
 	"""entity type that may be used to construct some advanced security configuration
 	"""
-	name = String(required=True, indexed=True, internationalizable=True, maxsize=100)
-	require_group = SubjectRelation('EGroup', cardinality='+*',
-					description=_('groups to which the permission is granted'))
-	require_state = SubjectRelation('State',
-				    description=_("entity'state in which the permission is applicable"))
-	# can be used on any entity
-	require_permission = ObjectRelation('**', cardinality='*1', composite='subject',
-					    description=_("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_ETYPE_PERMS
+
+        name = String(required=True, indexed=True, internationalizable=True, maxsize=100,
+                      description=_('name or identifier of the permission'))
+        label = String(required=True, internationalizable=True, maxsize=100,
+                       description=_('distinct label to distinguate between other permission entity of the same name'))
+        require_group = SubjectRelation('CWGroup',
+                                        description=_('groups to which the permission is granted'))
+
+    # explicitly add X require_permission CWPermission for each entity that should have
+    # configurable security
+    class require_permission(RelationType):
+        """link a permission to the entity. This permission should be used in the
+        security definition of the entity's type to be useful.
+        """
+        permissions = {
+            'read':   ('managers', 'users', 'guests'),
+            'add':    ('managers',),
+            'delete': ('managers',),
+            }
+
+    class require_group(RelationType):
+        """used to grant a permission to a group"""
+        permissions = {
+            'read':   ('managers', 'users', 'guests'),
+            'add':    ('managers',),
+            'delete': ('managers',),
+            }
 
 
 Example of configuration ::
@@ -151,14 +170,14 @@
 		       }
 	inlined = True
 
-This configuration indicates that an entity `EPermission` named
+This configuration indicates that an entity `CWPermission` named
 "add_version" can be associated to a project and provides rights to create
 new versions on this project to specific groups. It is important to notice that :
 
 * in such case, we have to protect both the entity type "Version" and the relation
   associating a version to a project ("version_of")
 
-* because of the genricity of the entity type `EPermission`, we have to execute
+* because of the genericity of the entity type `CWPermission`, we have to execute
   a unification with the groups and/or the states if necessary in the expression
   ("U in_group G, P require_group G" in the above example)
 
@@ -176,12 +195,13 @@
 ``````````````````````````````````````````````````````
 Potentially, the use of an RQL expression to add an entity or a relation
 can cause problems for the user interface, because if the expression uses
-the entity or the relation to create, then we are not able to verify the 
+the entity or the relation to create, then we are not able to verify the
 permissions before we actually add the entity (please note that this is
 not a problem for the RQL server at all, because the permissions checks are
-done after the creation). In such case, the permission check methods 
-(check_perm, has_perm) can indicate that the user is not allowed to create 
-this entity but can obtain the permission. 
+done after the creation). In such case, the permission check methods
+(check_perm, has_perm) can indicate that the user is not allowed to create
+this entity but can obtain the permission.
+
 To compensate this problem, it is usually necessary, for such case,
 to use an action that reflects the schema permissions but which enables
 to check properly the permissions so that it would show up if necessary.
--- a/doc/book/en/development/datamodel/define-workflows.rst	Tue Oct 06 17:18:10 2009 +0200
+++ b/doc/book/en/development/datamodel/define-workflows.rst	Tue Oct 06 17:18:23 2009 +0200
@@ -8,17 +8,17 @@
 
 A workflow describes how certain entities have to evolve between
 different states. Hence we have a set of states, and a "transition graph",
-i.e. a list of possible transitions from one state to another state.
+i.e. a set of possible transitions from one state to another state.
 
 We will define a simple workflow for a blog, with only the following
 two states: `submitted` and `published`. So first, we create a simple
-*CubicWeb* in ten minutes (see :ref:`BlogFiveMinutes`).
+*CubicWeb* instance in ten minutes (see :ref:`BlogFiveMinutes`).
 
 Set-up a workflow
 -----------------
 
 We want to create a workflow to control the quality of the BlogEntry
-submitted on your instance. When a BlogEntry is created by a user
+submitted on the instance. When a BlogEntry is created by a user
 its state should be `submitted`. To be visible to all, it has to
 be in the state `published`. To move it from `submitted` to `published`,
 we need a transition that we can call `approve_blogentry`.
@@ -27,16 +27,16 @@
 So we have to define a group of users, `moderators`, and
 this group will have appropriate permissions to publish a BlogEntry.
 
-There are two ways to create a workflow: from the user interface,
-or by defining it in ``migration/postcreate.py``.
-This script is executed each time a new ``cubicweb-ctl db-init`` is done.
-We strongly recommend to create the workflow in ``migration/postcreate.py``
-and we will now show you how. Read `Under the hood`_ to understand why.
+There are two ways to create a workflow: from the user interface, or
+by defining it in ``migration/postcreate.py``. This script is executed
+each time a new ``cubicweb-ctl db-init`` is done.  We strongly
+recommend to create the workflow in ``migration/postcreate.py`` and we
+will now show you how. Read `Two bits of warning`_ to understand why.
 
-The state of a entity is managed by the `in_state` attribute which can be added to you entity schema by two ways:
+The state of an entity is managed by the `in_state` attribute which
+can be added to your entity schema by inheriting from
+`cubicweb.schema.WorkflowableEntityType`.
 
-* direct inheritance by subclassing your class from `cubicweb.schema.WorkflowableEntityType`
-* by delegation using `cubicweb.schema.make_worflowable` (usable as a decorator)
 
 About our example of BlogEntry, we must have:
 
@@ -44,7 +44,7 @@
 
   from cubicweb.schema import WorkflowableEntityType
 
-  class BlogEntry(EntityType, WorkflowableEntityType):
+  class BlogEntry(WorkflowableEntityType):
       ...
 
 
@@ -53,10 +53,14 @@
 
 The ``postcreate.py`` script is executed in a special environment, adding
 several *CubicWeb* primitives that can be used.
+
 They are all defined in the ``class ServerMigrationHelper``.
 We will only discuss the methods we use to create a workflow in this example.
 
-To define our workflow for BlogDemo, please add the following lines
+A workflow is a collection of entities of type ``State`` and of type
+``Transition`` which are standard *CubicWeb* entity types.
+
+To define a workflow for BlogDemo, please add the following lines
 to ``migration/postcreate.py``:
 
 .. sourcecode:: python
@@ -69,25 +73,35 @@
 
 .. sourcecode:: python
 
-  wf = add_workflow(u'your workflow description', 'BlogEntry')
+  wf = add_workflow(u'blog publication workflow', 'BlogEntry')
 
-At first, instanciate a new workflow object with a gentle description and the concerned entity types (this one can be a tuple for multiple value).
+At first, instanciate a new workflow object with a gentle description
+and the concerned entity types (this one can be a tuple for multiple
+value).
 
 .. sourcecode:: python
 
   submitted = wf.add_state(_('submitted'), initial=True)
   published = wf.add_state(_('published'))
 
-``add_state`` expects as first argument the name of the state you want to create and an optional argument to say if it is supposed to be the initial state of the entity type.
+This will create two entities of type ``State``, one with name
+'submitted', and the other with name 'published'.
+
+``add_state`` expects as first argument the name of the state you want
+to create and an optional argument to say if it is supposed to be the
+initial state of the entity type.
 
 .. sourcecode:: python
 
   wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),)
 
+This will create an entity of type ``Transition`` with name
+`approve_blogentry` which will be linked to the ``State`` entities
+created before.
 
 ``add_transition`` expects
 
-  * as the first argument the name of the transition
+  * as the first argument: the name of the transition
   * then the list of states on which the transition can be triggered,
   * the target state of the transition,
   * and the permissions
@@ -102,10 +116,11 @@
   Do not forget to add the `_()` in front of all states and transitions names while creating
   a workflow so that they will be identified by the i18n catalog scripts.
 
-In addition to the user group conditions which the user needs to belong to one of those, we could have added a RQL condition.
-In this case, the user can only perform the action if the two conditions are satisfied.
+In addition to the user groups (one of which the user needs to belong
+to), we could have added a RQL condition.  In this case, the user can
+only perform the action if the two conditions are satisfied.
 
-If we use a RQL condition on a transition, we can use the following variables:
+If we use an RQL condition on a transition, we can use the following variables:
 
 * `%(eid)s`, object's eid
 * `%(ueid)s`, user executing the query eid
@@ -114,38 +129,30 @@
 
 .. image:: ../../images/03-transitions-view.en.png
 
-You can notice that in the action box of a BlogEntry, the state
-is now listed as well as the possible transitions for the current state defined by the workflow.
+You can notice that in the action box of a BlogEntry, the state is now
+listed as well as the possible transitions for the current state
+defined by the workflow.
+
 The transitions will only be displayed for users having the right permissions.
 In our example, the transition `approve_blogentry` will only be displayed
 for the users belonging to the group `moderators` or `managers`.
 
 
-Under the hood
-~~~~~~~~~~~~~~
-
-A workflow is a collection of entities of type ``State`` and of type ``Transition``
-which are standard *CubicWeb* entity types.
-
-For instance, the preceding lines:
-
-.. sourcecode:: python
-
-  submitted = wf.add_state(_('submitted'), initial=True)
-  published = wf.add_state(_('published'))
+Two bits of warning
+~~~~~~~~~~~~~~~~~~~
 
-will create two entities of type ``State``, one with name 'submitted', and the other
-with name 'published'. Whereas:
-
-.. sourcecode:: python
-
-  wf.add_transition(_('approve_blogentry'), (submitted,), published, ('moderators', 'managers'),)
+We could perfectly use the administration interface to do these
+operations. It is a convenient thing to do at times (when doing
+development, to quick-check things). But it is not recommended beyond
+that because it is a bit complicated to do it right and it will be
+only local to your instance (or, said a bit differently, such a
+workflow only exists in an instance database). Furthermore, you cannot
+write unit tests against deployed instances, and experience shows it
+is mandatory to have tests for any mildly complicated workflow
+setup.
 
-will create an entity of type ``Transition`` with name `approve_blogentry` which will
-be linked to the ``State`` entities created before.
-As a consequence, we could use the administration interface to do these operations. But it is not recommended because it will be uselessly complicated and will be only local to your instance.
-
-Indeed, if you create the states and transitions through the user interface, next time you initialize the database you will have to re-create all the entities.
-The user interface should only be a reference for you to view the states and transitions, but is not the appropriate interface to define your application workflow.
-
-
+Indeed, if you create the states and transitions through the user
+interface, next time you initialize the database you will have to
+re-create all the workflow entities. The user interface should only be
+a reference for you to view the states and transitions, but is not the
+appropriate interface to define your application workflow.
--- a/doc/book/en/development/devrepo/operations.rst	Tue Oct 06 17:18:10 2009 +0200
+++ b/doc/book/en/development/devrepo/operations.rst	Tue Oct 06 17:18:23 2009 +0200
@@ -3,7 +3,35 @@
 Repository operations
 ======================
 
-[WRITE ME]
+When one needs to perform operations (real world operations like mail
+notifications, file operations, real-world side-effects) at
+transaction commit time, Operations are the way to go.
+
+Possible events are:
+
+* precommit: the pool is preparing to commit. You shouldn't do
+  anything things which has to be reverted if the commit fail at this
+  point, but you can freely do any heavy computation or raise an
+  exception if the commit can't go.  You can add some new operation
+  during this phase but their precommit event won't be triggered
+
+* commit: the pool is preparing to commit. You should avoid to do to
+  expensive stuff or something that may cause an exception in this
+  event
 
-* repository operations
+* revertcommit: if an operation failed while commited, this event is
+  triggered for all operations which had their commit event already to
+  let them revert things (including the operation which made fail the
+  commit)
 
+* rollback: the transaction has been either rollbacked either
+  * intentionaly
+  * a precommit event failed, all operations are rollbacked
+  * a commit event failed, all operations which are not been triggered
+    for commit are rollbacked
+
+Exceptions signaled from within a rollback are logged and swallowed.
+
+The order of operations may be important, and is controlled according
+to operation's class (see : Operation, LateOperation, SingleOperation,
+SingleLastOperation).
Binary file doc/book/en/images/03-transitions-view.en.png has changed
--- a/doc/book/en/makefile	Tue Oct 06 17:18:10 2009 +0200
+++ b/doc/book/en/makefile	Tue Oct 06 17:18:23 2009 +0200
@@ -10,7 +10,7 @@
 SPHINXBUILD   = sphinx-build
 PAPER         =
 #BUILDDIR      = build
-BUILDDIR      = /tmp/cwdoc
+BUILDDIR      = ~/tmp/cwdoc
 
 # Internal variables for sphinx
 PAPEROPT_a4     = -D latex_paper_size=a4
--- a/etwist/server.py	Tue Oct 06 17:18:10 2009 +0200
+++ b/etwist/server.py	Tue Oct 06 17:18:23 2009 +0200
@@ -35,7 +35,7 @@
     # (start-repository command)
     # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
     if os.fork():   # launch child and...
-        return -1
+        os._exit(0)
     os.setsid()
     if os.fork():   # launch child and...
         os._exit(0) # kill off parent again.
@@ -379,12 +379,11 @@
     logger = getLogger('cubicweb.twisted')
     logger.info('instance started on %s', baseurl)
     if not debug:
-        if daemonize():
-            # child process
-            return
+        print 'instance starting in the background'
+        daemonize()
         if config['pid-file']:
             # ensure the directory where the pid-file should be set exists (for
-            # instance /var/run/cubicweb may be deleted on computer restart)
+            # instance /var/run/cubicweb may be deleted on computer restart) 
             piddir = os.path.dirname(config['pid-file'])
             if not os.path.exists(piddir):
                 os.makedirs(piddir)
--- a/web/formfields.py	Tue Oct 06 17:18:10 2009 +0200
+++ b/web/formfields.py	Tue Oct 06 17:18:23 2009 +0200
@@ -576,6 +576,8 @@
                                                                 skip_meta_attr=False)
         return fieldclass(**kwargs)
     kwargs['role'] = role
+    if role == 'object': # tag the type with '_object' instead of the type
+        kwargs['label'] = (eschema.type, rschema.type + '_object')
     return RelationField.fromcardinality(card, **kwargs)
 
 
--- a/web/views/schema.py	Tue Oct 06 17:18:10 2009 +0200
+++ b/web/views/schema.py	Tue Oct 06 17:18:23 2009 +0200
@@ -24,7 +24,9 @@
 
 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
 SKIP_TYPES = ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES
-SKIP_TYPES.update(set(('Transition', 'State', 'TrInfo',
+SKIP_TYPES.update(set(('Transition', 'State', 'TrInfo', 'Workflow',
+                       'WorkflowTransition', 'BaseTransition',
+                       'SubWorkflowExitPoint',
                        'CWUser', 'CWGroup',
                        'CWCache', 'CWProperty', 'CWPermission',
                        'ExternalUri')))