Logo Search packages:      
Sourcecode: zope-cps version File versions  Download package

def CPSWorkflow::workflow::WorkflowDefinition::_executeTransition (   self,
  ob,
  tdef = None,
  kwargs = None 
) [private]

Put the object in a new state, following transition tdef.

Can be called at creation, when there is no current workflow
state.

Definition at line 356 of file workflow.py.

00356                                                             :
        """Put the object in a new state, following transition tdef.

        Can be called at creation, when there is no current workflow
        state.
        """

        sci = None
        econtext = None
        moved_exc = None
        utool = getToolByName(self, 'portal_url') # CPS
        wftool = aq_parent(aq_inner(self))
        evtool = getEventService(self)

        # Get the stack ds defined for this object in this current state
        # If it's the creation time no stacks are defined
        stacks = wftool.getStacks(ob)

        # Figure out the old and new states.
        old_sdef = self._getWorkflowStateOf(ob)
        ### CPS: Allow initial transitions to have no old state.
        #
        if old_sdef is not None:
            old_state = old_sdef.getId()
        else:
            old_state = None
        #
        ###
        if tdef is None:
            # CPS: tdef is never None for CPSWorkflow
            raise WorkflowException('No transition!')
            #new_state = self.initial_state
            #former_status = {}
        else:
            new_state = tdef.new_state_id
            if not new_state:
                # Stay in same state.
                new_state = old_state
            former_status = self._getStatusOf(ob)
        new_sdef = self.states.get(new_state, None)
        if new_sdef is None:
            raise WorkflowException, (
                "Destination state '%s' undefined for "
                "transition '%s' in workflow '%s'" %(
                new_state, tdef.getId(), self.getId()))


        ### CPS: Behavior sanity checks.
        #
        behavior = tdef.transition_behavior
        LOG('CPSWorkflow', TRACE, 'Behavior in wf %s, trans %s: %s'
            % (self.getId(), tdef.getId(), behavior))
        kwargs = kwargs.copy() # Because we'll modify it.

        if TRANSITION_BEHAVIOR_MOVE in behavior:
            raise NotImplementedError
            ## Check allowed from source container.
            #src_container = aq_parent(aq_inner(ob))
            #ok, why = wftool.isBehaviorAllowedFor(src_container,
            #                                     TRANSITION_ALLOWSUB_MOVE,
            #                                     get_details=1)
            #if not ok:
            #    raise WorkflowException("src_container=%s does not allow "
            #                            "subobject move (%s)" %
            #                            (src_container.getId(), why))
            ## Now check dest container.
            #dest_container = kwargs.get('dest_container')
            #if dest_container is None:
            #    raise WorkflowException('Missing dest_container for move '
            #                            'transition=%s' % tdef.getId())
            #dest_container = self._objectMaybeFromRpath(dest_container)
            #ok, why = wftool.isBehaviorAllowedFor(dest_container, #XXXincorrect
            #                                     TRANSITION_INITIAL_MOVE,
            #                                  transition=self.dest_transition,
            #                                     get_details=1)
            #if not ok:
            #    raise WorkflowException("dst_container=%s does not allow "
            #                            "object initial move (%s)" %
            #                            (dst_container.getId(), why))
            #XXX now do move (recreate into dest container)
            #XXX raise ObjectDeleted ??? ObjectMoved ???
        if TRANSITION_BEHAVIOR_COPY in behavior:
            raise NotImplementedError
            #XXX
        if TRANSITION_BEHAVIOR_PUBLISHING in behavior:
            dest_container = kwargs.get('dest_container')
            if dest_container is None:
                raise WorkflowException("Missing dest_container for publishing"
                                        " transition=%s for ob=%s" %
                                        (tdef.getId(), ob))
            dest_container = self._objectMaybeFromRpath(dest_container)
            # Put it back so that it's useable from variables.
            kwargs['dest_container'] = utool.getRelativeUrl(dest_container)
            initial_transition = kwargs.get('initial_transition')
            if initial_transition is None:
                raise WorkflowException("Missing initial_transition for "
                                        "publishing transition=%s" %
                                        tdef.getId())
            if initial_transition not in tdef.clone_allowed_transitions:
                raise WorkflowException("Incorrect initial_transition %s, "
                                        "allowed=%s"
                                        % (initial_transition,
                                           tdef.clone_allowed_transitions))
        if TRANSITION_BEHAVIOR_CHECKOUT in behavior:
            dest_container = kwargs.get('dest_container')
            if dest_container is None:
                raise WorkflowException("Missing dest_container for checkout"
                                        " transition=%s" % tdef.getId())
            dest_container = self._objectMaybeFromRpath(dest_container)
            kwargs['dest_container'] = utool.getRelativeUrl(dest_container)
            initial_transition = kwargs.get('initial_transition')
            if initial_transition is None:
                raise WorkflowException("Missing initial_transition for "
                                        "checkout transition=%s" %
                                        tdef.getId())
            if initial_transition not in \
                    tdef.checkout_allowed_initial_transitions:
                raise WorkflowException("Incorrect initial_transition %s, "
                                        "allowed=%s"
                                        % (initial_transition,
                                    tdef.checkout_allowed_initial_transitions))
            language_map = kwargs.get('language_map')
        if TRANSITION_BEHAVIOR_CHECKIN in behavior:
            dest_objects = kwargs.get('dest_objects')
            if dest_objects is None:
                raise WorkflowException("Missing dest_objects for checkin"
                                        " transition=%s" % tdef.getId())
            dest_objects = [self._objectMaybeFromRpath(d)
                            for d in dest_objects]
            kwargs['dest_objects'] = [utool.getRelativeUrl(d)
                                      for d in dest_objects]
            checkin_transition = kwargs.get('checkin_transition')
            if checkin_transition is None:
                raise WorkflowException("Missing checkin_transition for "
                                        "checkin transition=%s" % tdef.getId())
            if checkin_transition not in tdef.checkin_allowed_transitions:
                raise WorkflowException("Incorrect checkin_transition %s, "
                                        "allowed=%s"
                                        % (checkin_transition,
                                           tdef.checkin_allowed_transitions))
            for dest_object in dest_objects:
                # Check that they have the same docid.
                if ob.getDocid() != dest_object.getDocid():
                    raise WorkflowException("Cannot checkin into different "
                                            "docid")
                # Check that the default language is still the same than
                # when we did checkout. # XXX We want to be more flexible.
                lang = ob.getDefaultLanguage()
                if (ob._getFromLanguageRevisions().get(lang, 1) !=
                    dest_object._getLanguageRevisions().get(lang, 2)):
                    raise WorkflowException("Cannot checkin into changed "
                                            "document %s" %
                                       '/'.join(dest_object.getPhysicalPath()))
        if TRANSITION_BEHAVIOR_DELETE in behavior:
            pass
            ## XXX Check that container allows delete.
            #container = aq_parent(aq_inner(ob))
            #ok, why = wftool.isBehaviorAllowedFor(container,
            #                                     TRANSITION_ALLOWSUB_DELETE,
            #                                     get_details=1)
            #if not ok:
            #    raise WorkflowException("Container=%s does not allow "
            #                            "subobject deletion (%s)" %
            #                            (container.getId(), why))
        #
        ###

        # Execute the "before" script.
        if tdef is not None and tdef.script_name:
            script = self.scripts[tdef.script_name]
            # Pass lots of info to the script in a single parameter.
            sci = StateChangeInfo(
                ob, self, former_status, tdef, old_sdef, new_sdef, stacks, kwargs, )
            try:
                script(sci)  # May throw an exception.
            except ObjectMoved, moved_exc:
                ob = moved_exc.getNewObject()
                # Re-raise after transition

        ### CPS: Behavior.
        #

        delete_ob = None

        if TRANSITION_BEHAVIOR_MOVE in behavior:
            raise NotImplementedError
            #XXX now do move (recreate into dest container)
            #XXX raise ObjectDeleted ??? ObjectMoved ???

        if TRANSITION_BEHAVIOR_COPY in behavior:
            raise NotImplementedError
            #XXX

        if TRANSITION_BEHAVIOR_PUBLISHING in behavior:
            wftool.cloneObject(ob, dest_container, initial_transition, kwargs)

        if TRANSITION_BEHAVIOR_CHECKOUT in behavior:
            wftool.checkoutObject(ob, dest_container, initial_transition,
                                  language_map, kwargs)

        if TRANSITION_BEHAVIOR_CHECKIN in behavior:
            for dest_object in dest_objects:
                wftool.checkinObject(ob, dest_object, checkin_transition)
            # Now delete the original object.
            delete_ob = ob

        if TRANSITION_BEHAVIOR_FREEZE in behavior:
            # Freeze the object.
            if isinstance(ob, ProxyBase):
                # XXX use an event?

                ob.freezeProxy()
                ob.proxyChanged()

        if TRANSITION_BEHAVIOR_DELETE in behavior:
            delete_ob = ob

        if TRANSITION_BEHAVIOR_MERGE in behavior:
            container = aq_parent(aq_inner(ob))
            dest_ob = wftool.mergeObject(ob, container,
                                         self.state_var, new_state)
            if dest_ob is not None:
                delete_ob = ob
                # Provide info to the UI to get a correct redirect.
                res = ('ObjectMoved', utool.getRelativeUrl(dest_ob))
                moved_exc = ObjectMoved(dest_ob, res)
                ob = dest_ob

        #################################################
        #              Stack workflows cases
        ##################################################

        if TRANSITION_BEHAVIOR_PUSH_DELEGATEES in behavior:

            LOG("::CPSWorkflow._executeTansition() :: "
                "FLAG : TRANSITION_BEHAVIOR_PUSH_DELEGATEES",
                TRACE,
                str(kwargs))

            #
            # Get the stack definitions from the transition definition
            # The workflow variable id is the stackdef id.
            #

            sdef_behaviors = new_sdef.state_behaviors
            push_allowed_on_variables = new_sdef.push_on_workflow_variable

            #
            # First check if the state allows the behavior
            # Raise an exception if not allowed
            #

            if STATE_BEHAVIOR_PUSH_DELEGATEES not in sdef_behaviors:
                raise WorkflowException(
                    "State behavior not allowed for '%s' on '%s'" %(
                    STATE_BEHAVIOR_PUSH_DELEGATEES,
                    new_sdef.getId(),
                    ))

            wf_vars = tdef.push_on_workflow_variable

            #
            # Raise an exception if not variables are associated to the
            # transition flag. Mostly for debuging right now. It's anyway a
            # problem of configuration
            #

            if not wf_vars:
                raise WorkflowException(
                    "Transition %s needs associated variables to execute on" %(
                    TRANSITION_BEHAVIOR_PUSH_DELEGATEES,
                    ))

            for wf_var in wf_vars:

                #
                # Filter on the working workflow variable It's necessarly since
                # the transition can be allowed on several wf variable id
                #

                current_wf_var_id = kwargs.get('current_wf_var_id', '')
                if current_wf_var_id != wf_var:
                    continue

                #
                # Check here if the behavior is allowed by the state for this
                # given worfklow variable id
                #

                if wf_var not in push_allowed_on_variables:
                    raise WorkflowException(
                        "State %s dosen't allow '%s' on var '%s'" %(
                        new_sdef.getId(),
                        STATE_BEHAVIOR_PUSH_DELEGATEES,
                        wf_var,
                        ))

                stackdef = new_sdef.getStackDefinitionFor(wf_var)
                if stackdef is not None:
                    ds = stacks.get(wf_var)
                    stacks[wf_var] = stackdef._push(ds, **kwargs)

        if TRANSITION_BEHAVIOR_POP_DELEGATEES in behavior:

            LOG("::CPSWorkflow._executeTansition() :: "
                "FLAG : TRANSITION_BEHAVIOR_POP_DELEGATEES",
                TRACE,
                str(kwargs))

            #
            # Get the stack definitions from the transition definition
            # The workflow variable id is the stackdef id.
            #


            sdef_behaviors = new_sdef.state_behaviors
            pop_allowed_on_variables = new_sdef.pop_on_workflow_variable

            #
            # First check if the state allows the behavior
            # Raise an exception if not allowed
            #

            if STATE_BEHAVIOR_POP_DELEGATEES not in sdef_behaviors:
                raise WorkflowException(
                    "State behavior not allowed for '%s' on '%s'" %(
                    STATE_BEHAVIOR_POP_DELEGATEES,
                    new_sdef.getId(),
                    ))

            wf_vars = tdef.pop_on_workflow_variable

            #
            # Raise an exception if not variables are associated to the
            # transition flag. Mostly for debuging right now. It's anyway a
            # problem of configuration
            #

            if not wf_vars:
                raise WorkflowException(
                    "Transition %s needs associated variables to execute on" %(
                    TRANSITION_BEHAVIOR_POP_DELEGATEES,
                    ))

            for wf_var in wf_vars:

                #
                # Filter on the working workflow variable It's necessarly since
                # the transition can be allowed on several wf variable id
                #
                current_wf_var_id = kwargs.get('current_wf_var_id', '')
                if current_wf_var_id != wf_var:
                    continue

                #
                # Check here if the behavior is allowed by the state for this
                # given worfklow variable id
                #

                if wf_var not in pop_allowed_on_variables:
                    raise WorkflowException(
                        "State %s dosen't allow '%s' on var '%s'" %(
                        new_sdef.getId(),
                        STATE_BEHAVIOR_POP_DELEGATEES,
                        wf_var,
                        ))

                stackdef = new_sdef.getStackDefinitionFor(wf_var)
                if stackdef is not None:
                    ds = stacks.get(wf_var)
                    stacks[wf_var] = stackdef._pop(ds, **kwargs)

        if TRANSITION_BEHAVIOR_WORKFLOW_UP in behavior:

            LOG("::CPSWorkflow._executeTansition() :: "
                "FLAG : TRANSITION_BEHAVIOR_WORKFLOW_UP",
                TRACE,
                str(kwargs))

            #
            # Get the stack definitions from the transition definition
            # The workflow variable id is the stackdef id.
            #

            sdef_behaviors = new_sdef.state_behaviors
            up_vars = new_sdef.workflow_up_on_workflow_variable

            #
            # First check if the state allows the behavior
            # Raise an exception if not allowed
            #

            if STATE_BEHAVIOR_WORKFLOW_UP not in sdef_behaviors:
                raise WorkflowException(
                    "State behavior not allowed for '%s' on '%s'" %(
                    STATE_BEHAVIOR_WORKFLOW_UP,
                    new_sdef.getId(),
                    ))

            wf_vars = tdef.workflow_up_on_workflow_variable

            #
            # Raise an exception if not variables are associated to the
            # transition flag. Mostly for debuging right now. It's anyway a
            # problem of configuration
            #

            if not wf_vars:
                raise WorkflowException(
                    "Transition %s needs associated variables to execute on" %(
                    TRANSITION_BEHAVIOR_WORKFLOW_UP,
                    ))

            for wf_var in wf_vars:

                #
                # Filter on the working workflow variable It's necessarly since
                # the transition can be allowed on several wf variable id
                #

                current_wf_var_id = kwargs.get('current_wf_var_id', '')
                if current_wf_var_id != wf_var:
                    continue

                #
                # Check here if the behavior is allowed by the state for this
                # given worfklow variable id
                #

                if wf_var not in up_vars:
                    raise WorkflowException(
                        "State %s dosen't allow '%s' on var '%s'" %(
                        new_sdef.getId(),
                        STATE_BEHAVIOR_WORKFLOW_UP,
                        wf_var,
                        ))

                stackdef = new_sdef.getStackDefinitionFor(wf_var)
                if stackdef is not None:
                    ds = stacks.get(wf_var)
                    stacks[wf_var] = stackdef._doIncLevel(ds)

        if TRANSITION_BEHAVIOR_WORKFLOW_DOWN in behavior:

            LOG("::CPSWorkflow._executeTansition() :: "
                "FLAG : TRANSITION_BEHAVIOR_WORKFLOW_DOWN",
                TRACE,
                str(kwargs))

            #
            # Get the stack definitions from the transition definition
            # The workflow variable id is the stackdef id.
            #

            sdef_behaviors = new_sdef.state_behaviors
            down_vars = new_sdef.workflow_down_on_workflow_variable

            #
            # First check if the state allows the behavior
            # Raise an exception if not allowed
            #

            if STATE_BEHAVIOR_WORKFLOW_DOWN not in sdef_behaviors:
                raise WorkflowException(
                    "State behavior not allowed for '%s' on '%s'" %(
                    STATE_BEHAVIOR_WORKFLOW_DOWN,
                    new_sdef.getId(),
                    ))

            wf_vars = tdef.workflow_down_on_workflow_variable

            #
            # Raise an exception if not variables are associated to the
            # transition flag. Mostly for debuging right now. It's anyway a
            # problem of configuration
            #

            if not wf_vars:
                raise WorkflowException(
                    "Transition %s needs associated variables to execute on" %(
                    TRANSITION_BEHAVIOR_WORKFLOW_DOWN,
                    ))

            for wf_var in wf_vars:

                #
                # Filter on the working workflow variable It's necessarly since
                # the transition can be allowed on several wf variable id
                #

                current_wf_var_id = kwargs.get('current_wf_var_id', '')
                if current_wf_var_id != wf_var:
                    continue

                #
                # Check here if the behavior is allowed by the state for this
                # given worfklow variable id
                #

                if wf_var not in down_vars:
                    raise WorkflowException(
                        "State %s dosen't allow '%s' on var '%s'" %(
                        new_sdef.getId(),
                        STATE_BEHAVIOR_WORKFLOW_DOWN,
                        wf_var,
                        ))

                stackdef = new_sdef.getStackDefinitionFor(wf_var)
                if stackdef is not None:
                    ds = stacks.get(wf_var)
                    stacks[wf_var] = stackdef._doDecLevel(ds)


        if TRANSITION_BEHAVIOR_WORKFLOW_RESET in behavior:

            LOG("::CPSWorkflow._executeTansition() :: "
                "FLAG : TRANSITION_BEHAVIOR_WORKFLOW_RESET",
                TRACE,
                str(kwargs))

            #
            # Get the stack definitions from the transition definition
            # The workflow variable id is the stackdef id.
            #

            sdef_behaviors = new_sdef.state_behaviors
            reset_vars = new_sdef.workflow_reset_on_workflow_variable

            #
            # First check if the state allows the behavior
            # Raise an exception if not allowed
            #

            if STATE_BEHAVIOR_WORKFLOW_RESET not in sdef_behaviors:
                raise WorkflowException(
                    "State behavior not allowed for '%s' on '%s'" %(
                    STATE_BEHAVIOR_WORKFLOW_RESET,
                    new_sdef.getId(),
                    ))

            wf_vars = tdef.workflow_reset_on_workflow_variable

            #
            # Raise an exception if not variables are associated to the
            # transition flag. Mostly for debuging right now. It's anyway a
            # problem of configuration
            #

            if not wf_vars:
                raise WorkflowException(
                    "Transition %s needs associated variables to execute on" %(
                    TRANSITION_BEHAVIOR_WORKFLOW_RESET,
                    ))

            for wf_var in wf_vars:

                #
                # Filter on the working workflow variable It's necessarly since
                # the transition can be allowed on several wf variable id
                #

                current_wf_var_id = kwargs.get('current_wf_var_id', '')

                if current_wf_var_id != wf_var:
                    continue

                #
                # Check here if the behavior is allowed by the state for this
                # given worfklow variable id
                #

                if wf_var not in reset_vars:
                    raise WorkflowException(
                        "State %s dosen't allow '%s' on var '%s'" %(
                        new_sdef.getId(),
                        STATE_BEHAVIOR_WORKFLOW_RESET,
                        wf_var,
                        ))

                stackdef = new_sdef.getStackDefinitionFor(wf_var)
                if stackdef is not None:
                    ds = stacks.get(wf_var)
                    stacks[wf_var] = stackdef._reset(ds, **kwargs)

        #
        # Update variables.
        #

        state_values = new_sdef.var_values
        if state_values is None:
            state_values = {}
        tdef_exprs = None
        if tdef is not None:
            tdef_exprs = tdef.var_exprs
        if tdef_exprs is None:
            tdef_exprs = {}
        status = {}
        for id, vdef in self.variables.items():
            if not vdef.for_status:
                continue
            expr = None
            if state_values.has_key(id):
                value = state_values[id]
            elif tdef_exprs.has_key(id):
                expr = tdef_exprs[id]
            else:
                if vdef.default_expr is not None:
                    expr = vdef.default_expr
                else:
                    value = vdef.default_value
            if expr is not None:
                # Evaluate an expression.
                if econtext is None:
                    # Lazily create the expression context.
                    if sci is None:
                        sci = StateChangeInfo(
                            ob, self, former_status, tdef,
                            old_sdef, new_sdef, stacks, kwargs)
                    econtext = createExprContext(sci)
                value = expr(econtext)

            status[id] = value

        # update stack variables, initializing stacks defined in given state
        stacks = {}
        stackdefs = new_sdef.getStackDefinitions()
        for k in stackdefs.keys():
            # stack is a variable, it has just been updated in the status
            if status[k] is None:
                # Call the registry and construct stack instance
                stype = stackdefs[k].getStackDataStructureType()
                new_stack = StackReg.makeWorkflowStackTypeInstance(
                    stype)
                status[k] = new_stack

        # Update state.
        status[self.state_var] = new_state
        tool = aq_parent(aq_inner(self))
        tool.setStatusOf(self.id, ob, status)

        # Update role to permission assignments.
        kw = {}
        kw['current_wf_var_id'] = kwargs.get('current_wf_var_id', '')
        kw['tdef'] = tdef
        self.updateRoleMappingsFor(ob, **kw)

        # Execute the "after" script.
        if tdef is not None and tdef.after_script_name:
            script = self.scripts[tdef.after_script_name]
            # Taking care of the case of an "after" script for a deleted object.
            # The condition delete_ob == ob is here to make sure we don't reset
            # ob in case of a TRANSITION_BEHAVIOR_MERGE.
            if delete_ob is not None and delete_ob == ob:
                ob = None
            # Pass lots of info to the script in a single parameter.
            sci = StateChangeInfo(
                ob, self, status, tdef, old_sdef, new_sdef, stacks, kwargs)
            script(sci)  # May throw an exception.

        ### CPS: Delete. Done after setting status, to keep history.
        #
        if delete_ob is not None:
            # XXX refactoring.
            evtool.notify('workflow_%s' % tdef.getId(),
                          delete_ob, {'kwargs': kwargs})
            container = aq_parent(aq_inner(delete_ob))
            container._delObject(delete_ob.getId())
            if moved_exc is not None:
                raise moved_exc
            else:
                raise ObjectDeleted

        ### CPS: Event notification. This has to be done after all the
        # potential transition scripts.
        #
        # XXX pass a whole sci ?
        evtool.notify('workflow_%s' % tdef.getId(), ob, {'kwargs': kwargs})
        #
        ###

        # Return the new state object.
        if moved_exc is not None:
            # Propagate the notification that the object has moved.
            raise moved_exc
        else:
            return new_sdef

    #
    # API


Generated by  Doxygen 1.6.0   Back to index