Beispiel #1
0
    def createWorkItems(self, activity_ids, source, content_object=None):
        """Creates a new workitem for the activity with the given name.

           Raises UnknownActivityError if one or more of the given activities
           do not exist.
        """
        if not activity_ids:
            return ()

        utils.logger.log(
            logging.DEBUG,
            'Creating workitems %r in %r' % (activity_ids, source))

        self._creating += 1
        try:
            passed_activities = self._veto_workitem_creation(
                activity_ids, source)
            new_ids, new = self._create_workitems_helper(
                passed_activities, source, content_object)
            source.generated_workitems += tuple(new_ids)
            for wi in new:
                controller = ILifeCycleController(wi)
                controller.start(
                    "Work item was created and automatically started.")
            self.notifyWorkItemStateChange()
        finally:
            self._creating -= 1

        # We need to explicitly check for completeness here again, because we
        # blocked the checks during creation.
        self._check_complete(self.getWorkItems('active'))
        return new_ids
Beispiel #2
0
 def _rememberTrigger(self, triggering_workitem, route):
     mode = self.getActivity().mode
     all_routes = self._getAllRoutes()
     closed_routes = self.closed_routes
     if route in closed_routes:
         return
     controller = ILifeCycleController(self)
     if mode == MULTI_MERGE:
         closed_routes.append(route)
         self._doTrigger(triggering_workitem, route)
         if len(closed_routes) == len(all_routes):
             self.completing_work_item = triggering_workitem
             # XXX no test for this
             controller.complete("Gate '%s' found by route '%s'" %
                                 (self.activity_id, route))
     elif mode == DISCRIMINATE:
         if len(closed_routes) == 0:
             self._doTrigger(triggering_workitem, route)
             self.completing_work_item = triggering_workitem
             controller.complete("Gate '%s' found by route '%s'" %
                                 (self.activity_id, route))
         closed_routes.append(route)
     elif mode == DELAYED_DISCRIMINATE:
         self.delayed_discriminator = True
     elif mode == SYNCHRONIZING_MERGE:
         closed_routes.append(route)
         if len(closed_routes) == len(all_routes):
             self._doTrigger(triggering_workitem, route)
             self.completing_work_item = triggering_workitem
             controller.complete("Gate '%s' completed via route '%s'"
                                 % (self.activity_id, route))
     else:
         # XXX no tests for this
         controller.fail("Unknown gate mode: '%s'" % mode)
     self.closed_routes = closed_routes
Beispiel #3
0
 def onTermination(self):
     """Trigger that gets called before the object is terminated."""
     for inst in self.objectValues():
         controller = ILifeCycleController(inst)
         if controller.state != 'ended':
             controller.terminate('Terminated due to termination of %s.' %
                                  self.log_name)
Beispiel #4
0
 def onReset(self):
     """Trigger that gets called after the object is reset."""
     for inst in self.objectValues():
         controller = ILifeCycleController(inst)
         if controller.state != 'ended':
             controller.terminate('Terminated due to reset of %s.' %
                                  self.log_name)
Beispiel #5
0
    def test_recursive(self):
        doc = self._init_object('workflows/termination_recursive.alf')
        instance = doc.getInstance()
        controller = ILifeCycleController(instance)
        controller.start("testing")

        task = instance.getWorkItems()[0]
        task.complete(exit="complete")
        self.assertEquals('ended', controller.state)
        self.assertEquals(True, controller.completed)
Beispiel #6
0
    def test_object_deletion(self):
        doc = self._init_object('workflows/termination_delete.alf')
        instance = doc.getInstance()
        container = doc.getParentNode()
        id = doc.getId()
        assert id in container.objectIds()

        controller = ILifeCycleController(instance)
        controller.start("testing")

        self.assertEquals('ended', controller.state)
        self.assertEquals(True, controller.completed)

        self.assert_(id not in container.objectIds())
Beispiel #7
0
    def test_fallout_startActivity(self):
        # test for fix of bug #2666
        self.loginAsPortalOwner()
        self._create_test_users()
        self._import_wf('workflows/fallout_startActivity.alf')

        doc1 = self.create(self.portal, 'DummyContent', 'doc1')
        doc1.assignProcess(self.test_process)
        instance = doc1.getInstance()
        controller = ILifeCycleController(instance)
        controller.start('test')
        self.assertEqual('failed', controller.state)
        fallout_workitems = instance.getWorkItems('failed')
        self.assertEqual(1, len(fallout_workitems))
        self.assertEqual('become_fallout', fallout_workitems[0].activity_id)
Beispiel #8
0
    def test_restart(self):
        self.loginAsPortalOwner()
        self._import_wf('workflows/task.alf')
        portal = self.portal

        doc = self.create(portal, 'DummyContent', 'doc1')
        doc.assignProcess(self.test_process)
        instance = doc.getInstance()
        ILifeCycleController(instance).start('testing')

        # This was a bug that is explicitly tested for now
        self.failUnless(instance.aq_base is doc.getInstance().aq_base)
        ILifeCycleController(instance).reset('more testing')
        ILifeCycleController(instance).start('more testing')
        self.failUnless(instance.aq_base is doc.getInstance().aq_base)
Beispiel #9
0
class SwitchWorkItem(BaseWorkItem):

    zope.interface.implements(ISwitchWorkItem)
    zope.interface.classProvides(IWorkItemClass)

    security = ClassSecurityInfo()

    activity_type = "switch"

    security.declarePrivate('onStart')

    def onStart(self):
        "Trigger that gets called after the workitem has been started."
        super(SwitchWorkItem, self).onStart()
        activity = self.getActivity()
        first = activity.mode == "first"

        for case in activity.getExits():
            try:
                case_result = utils.evaluateTales(case.condition,
                                                  workitem=self)
            except Exception, m:
                ILifeCycleController(self).fail(
                    "Evaluating condition on case %s of activity %s raised an "
                    " exception." % (case.id, activity.title_or_id()), m)
                return
            if case_result:
                self.passCheckpoint(case.id)
                if first:
                    break

        ILifeCycleController(self).complete(activity.title_or_id())
Beispiel #10
0
 def manage_afterAdd(self, item, container):
     DummyContent.inheritedAttribute('manage_afterAdd')(self, item,
                                                        container)
     if len(self.getAllInstances()) > 0:
         return
     self.assignProcess(test_process)
     ILifeCycleController(self.getInstance()).start("testing")
Beispiel #11
0
    def test_before_start(self):
        self.loginAsPortalOwner()
        self._import_wf('workflows/before_start.alf')
        portal = self.portal
        wftool = getToolByName(portal, 'workflow_manager')

        doc = self.create(portal, 'DummyContent', 'doc1')
        doc.before = 0
        doc.after = 0
        doc.expr_works = 0
        doc.assignProcess(self.test_process)
        instance = doc.getInstance()
        ILifeCycleController(instance).start('testing')

        self.assertEquals(1, doc.expr_works)
        self.assertEquals(1, doc.before)
        self.assertEquals(0, doc.after)
        self.assertEquals([],
                          wftool.processes['test'].current()['some-task'].graphGetStartActivities())
        self.assertEquals(1, len(instance.getWorkItems()))
        task = instance.getWorkItems()[0]
        self.assertEquals(1, len(task.objectValues()))
        task_start = task.objectValues()[0]
        self.assertEquals(1, len(task_start.objectValues()))
        task_start_expression = task_start.objectValues()[0]
        self.assert_(isinstance(
            task_start_expression,
            Products.AlphaFlow.aspects.expression.ExpressionAspect))
Beispiel #12
0
    def test_contentretrieve(self):
        self.loginAsPortalOwner()
        self._create_test_users()
        self._import_wf()
        portal = self.portal

        fold = self.create(portal, 'DummyFolder', 'f1')
        doc = self.create(fold, 'DummyContent', 'doc1')
        doc.setTitle('gaaack')
        doc.reindexObject()
        doc.assignProcess(self.test_process)
        instance = doc.getInstance()
        ILifeCycleController(instance).start('testing')

        wi = instance.getWorkItems()[0]

        def check(ob):
            self.assertEquals(doc.absolute_url(), ob.getUrl())
            self.assertEquals(doc.getPhysicalPath(),
                              ob.getContentObjectPath())
            self.assertEquals(doc, ob.getContentObject())
            brain = ob.getContentObjectPortalCatalogBrain()
            self.assertEquals(doc.Title(), brain.Title)
            self.assertEquals(doc.absolute_url(), brain.getURL())

        check(instance)
        check(wi)
Beispiel #13
0
    def test_notifyAssigneesChange_content(self):
        self.loginAsPortalOwner()
        self._import_wf('workflows/cachetest_multi.alf')
        self._create_test_users()
        portal = self.portal

        alf = getToolByName(portal, 'workflow_manager')

        portal.invokeFactory('DummyContent', 'doc')
        doc = portal.doc
        doc.test_assignees = ['hans']
        doc.test_assignees2 = ['hubert']
        doc.assignProcess(self.test_process)
        instance = doc.getInstance()
        ILifeCycleController(instance).start("Los gehts")

        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForContent(doc, 'hans'))
        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForContent(doc, 'hubert'))

        doc.test_assignees2 = ['karlheinz']
        alf.updateCacheByContent(doc)

        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForContent(doc, 'hans'))
        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForContent(doc, 'karlheinz'))
        self.assertRaises(KeyError,
                          alf.getDynamicRolesForContent, doc, 'hubert')
Beispiel #14
0
 def onStart(self):
     """Runs the automatic procedure, handles exceptions and moves on."""
     try:
         BaseWorkItem.onStart(self)
         self.run()
     except Exception, m:
         ILifeCycleController(self).fail("Automatic activity failed.", m)
Beispiel #15
0
 def _is_accepted(self):
     acc = getattr(self,
                   '_is_accepted_' + self.getActivity().decision_modus)()
     if not acc:
         return
     self.passCheckpoint("accept")
     ILifeCycleController(self).complete("All assignees accepted.")
Beispiel #16
0
 def __repr__(self):
     try:
         state = ILifeCycleController(self).state
     except TypeError:
         state = 'n/a'
     return '<%s for %r (%s)>' % (self.__class__.__name__,
                                  self.activity_id, state)
Beispiel #17
0
    def _update_cache(self, workitem):
        """update internal state/workitem cache"""
        state_to_id = getattr(aq_base(self), '_cache_state_to_id', None)
        if state_to_id is None:
            state_to_id = self._cache_state_to_id = {}

        id_to_state = getattr(aq_base(self), '_cache_id_to_state', None)
        if id_to_state is None:
            id_to_state = self._cache_id_to_state = {}

        to_index = getattr(aq_base(self), '_v_workitems_to_index', None)
        if to_index is None:
            to_index = self._v_workitems_to_index = {}

        id = workitem.getId()
        new_state = ILifeCycleController(workitem).state
        old_state = id_to_state.get(id)

        if new_state != old_state:
            # remove old state from state_to_id,
            # NOTE: id_to_state will be overwritten implicitly
            ids_in_old_state = state_to_id.setdefault(old_state, {})
            try:
                del ids_in_old_state[id]
            except KeyError:
                pass

            # set new state
            state_to_id.setdefault(new_state, {})[id] = True
            id_to_state[id] = new_state
            to_index[id] = 1

            self._p_changed = 1
Beispiel #18
0
 def onRecovery(self):
     """Trigger that gets called after the object is recovered."""
     for inst in self.objectValues():
         controller = ILifeCycleController(inst)
         if controller.state == 'failed':
             raise ValueError("Can't recover while failed %s are left." %
                              self.log_children_name)
Beispiel #19
0
    def beforeCreationItems(self, items, parent):
        vote_no = []
        my_parent = self.getParent()

        # Do the new items belong to a route?
        route = self._findRouteForWI(parent)
        if route is None:
            return []

        all_routes = self._getAllRoutes()

        for wi in items:
            if wi == self.activity_id:
                vote_no.append(wi)
                self._rememberTrigger(parent, route)

            # XXX This is some special stuff to make the delayed trigger work
            if self.getActivity().mode == DELAYED_DISCRIMINATE:
                process = self.getProcess()
                new_activity = process[wi]
                if isinstance(new_activity, GateActivity):
                    closed_routes = self.closed_routes
                    if not route in self.closed_routes:
                        closed_routes.append(route)
                    if len(closed_routes) == len(all_routes) and self.delayed_discriminator:
                        self._doTrigger(parent, route)
                        self.completing_work_item = parent
                        ILifeCycleController(self).complete(
                            "Gate '%s' triggered and completed after route "
                            "'%s'" % (self.activity_id, route))
                        self.delayed_discriminator = False
                    self.closed_routes = closed_routes

        return vote_no
Beispiel #20
0
 def accept(self, REQUEST=None):
     """Accept"""
     if self.state != "active":
         raise ValueError, "Can't accept when not active."
     self.passCheckpoint("accept")
     ILifeCycleController(self).complete("Accepted.")
     self.notifyAssigneesChange()
     self._update_ui_after_action("Review registered.", REQUEST)
Beispiel #21
0
 def reject(self, REQUEST=None):
     """Reject"""
     if self.state != "active":
         raise ValueError, "Can't reject when not active."
     self.passCheckpoint("reject")
     ILifeCycleController(self).complete("Rejected.")
     self.notifyAssigneesChange()
     self._update_ui_after_action('Rejected.', REQUEST)
Beispiel #22
0
 def _veto_workitem_creation(self, activity_ids, source):
     vetoed = []
     for wi in self.getWorkItems():
         try:
             vetoed.extend(wi.beforeCreationItems(activity_ids, source))
         except Exception, m:
             ILifeCycleController(wi).fail("Vetoing failed.", m)
             return []
Beispiel #23
0
 def run(self):
     """Performs the actual automatic activity"""
     instance = self.getInstance()
     workitems = instance.getWorkItems()
     for wi in workitems:
         if wi is self:
             continue
         ILifeCycleController(wi).terminate(
             "Terminated by %s." % self.getActivity().title_or_id())
Beispiel #24
0
    def _check_failed(self):
        """Perform a check whether this instance is failed.

        This method returns True in two cases:

          1. The state of the process instance is `failed`
          2. At least one of the work items is failed.
        """
        items_failed = len(self.getWorkItemIds(state="failed"))
        controller = ILifeCycleController(self)
        if controller.state == 'failed':
            return True
        if items_failed > 0:
            controller.fail(
                "Automatically failed because %s work items failed." %
                items_failed)
            return True
        return False
Beispiel #25
0
 def _create_instance(self):
     portal = self.portal
     portal.invokeFactory('DummyContent', 'doc')
     doc = portal.doc
     doc.assignProcess(self.test_process)
     instance = doc.getInstance()
     f = instance.getField('write_document_assignees')
     f.set(instance, 'author')
     ILifeCycleController(instance).start('test')
     return instance
Beispiel #26
0
    def test_notifyAssigneesChange(self):
        self.loginAsPortalOwner()
        self._import_wf('workflows/cachetest.alf')
        self._create_test_users()
        portal = self.portal

        alf = getToolByName(portal, 'workflow_manager')

        portal.invokeFactory('DummyContent', 'doc')
        doc = portal.doc
        doc.test_assignees = ['hans']
        doc.assignProcess(self.test_process)
        instance = doc.getInstance()
        ILifeCycleController(instance).start("Los gehts")
        wi = instance.getWorkItems()[0]

        self.assertEquals(['Assignee'],
                          alf.getDynamicRolesForWorkItem(wi, 'hans'))
        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForInstance(instance, 'hans'))

        doc.test_assignees = ['heinrich']
        wi.notifyAssigneesChange()

        self.assertRaises(KeyError,
                          alf.getDynamicRolesForWorkItem, wi, 'hans')
        self.assertEquals(['Assignee'],
                          alf.getDynamicRolesForWorkItem(wi, 'heinrich'))
        self.assertEquals(['ProcessUser'],
                          alf.getDynamicRolesForInstance(instance, 'heinrich'))

        wi_old = wi
        assignees = ['Ernie', 'Bert']
        doc.test_assignees = assignees
        wi.complete('complete')
        wi = instance.getWorkItems()[0]

        self.assertRaises(KeyError,
                          alf.getDynamicRolesForWorkItem, wi_old, 'heinrich')

        for assignee in assignees:
            self.assertEquals(['Assignee'],
                              alf.getDynamicRolesForWorkItem(wi, assignee))
            self.assertEquals(['ProcessUser'],
                              alf.getDynamicRolesForInstance(instance, assignee))

        self.assertRaises(KeyError,
                          alf.getDynamicRolesForInstance, instance, 'hans')
        self.assertRaises(KeyError,
                          alf.getDynamicRolesForInstance, instance, 'heinrich')

        doc.test_assignees = []
        wi.complete('complete')
        wi = instance.getWorkItems()[0]
        self.assertEquals({}, dict(alf._workitem_role_cache))
Beispiel #27
0
    def _check_complete(self, workitems):
        """End instance if only daemons are left.

        workitems -- list of active workitems (passed for performance reasons)

        This method is a bit weird, but works. There are two cases:

            1. If no active work items are left, we complete the instance.

            2. If only daemons are left, we complete those. As a side effect,
            when the last daemon is completed, we get another trigger for
            _check_complete and the instance will be completed by case 1.

        """
        if self._creating:
            # Do not check for completeness while we are still creating work
            # items.
            return

        controller = ILifeCycleController(self)
        daemons = [
            wi for wi in workitems
            if IDaemonActivity.providedBy(wi.getActivity())
        ]
        items_active = len(workitems)
        daemons_active = len(daemons)

        if controller.state != "active":
            # XXX Missing test
            # We don't have to check further as we are not active.
            return

        if not items_active:
            # Case 1: No active work items
            controller.complete(
                "Automatically completed instance as all work items ended.")

        if (controller.state == "active" and items_active == daemons_active):
            # Case 2: Only daemons are active
            for daemon in daemons:
                ILifeCycleController(daemon).complete(
                    "Automatically completed daemon.")
Beispiel #28
0
 def test_grouped_schema(self):
     self._create_test_users()
     self._import_wf('workflows/multi_review.alf')
     portal = self.portal
     alf = getToolByName(portal, 'workflow_manager')
     portal.invokeFactory("DummyContent", "doc")
     doc = portal.doc
     doc.assignProcess(self.test_process)
     instance = doc.getInstance()
     controller = ILifeCycleController(instance)
     controller.start('test')
     wis = instance.getWorkItems()
     self.assertEqual(1, len(wis))
     gs = wis[0].getGroupedSchema()
     act_ids_expected = ['write_document']
     act_ids_expected.sort()
     act_ids_got = [ g.activity_id for g in gs ]
     act_ids_got.sort()
     self.assertEquals(act_ids_expected, act_ids_got)
     self.assertEquals(gs[0].Title(), 'Dokument schreiben')
Beispiel #29
0
    def notifyWorkItemStateChange(self, workitem):
        """Check if routes have to be closed."""
        # check if workitem is one of our gates
        if workitem.getId() not in self.generated_workitems:
            return
        if workitem.activity_type != "gate":
            return

        # check if gate is completed
        if not ILifeCycleController(workitem).completed:
            return

        finished_gate = workitem.activity_id

        workitems = self.getGeneratedWorkItems()
        # kill other gates
        for wi in workitems:
            if wi.activity_type != "gate":
                continue
            if wi.state != "active":
                continue
            ILifeCycleController(wi).terminate(
                "Competing gate '%s' successfully completed." % finished_gate)

        # kill open routes
        for wi in workitems:
            if wi.activity_type == "gate":
                continue
            # XXX Do not kill the work item that triggered all this ...
            # Our assumption is, that the work item is successfull and
            # either will try to complete in a moment, or may still be
            # relevant as it was 'successfull'. That's the ignore= flag.
            killWorkItemRecursively(
                wi,
                "Competing route successfully completed at gate '%s'." %
                finished_gate,
                ignore=[workitem.completing_work_item])

        # complete routing
        ILifeCycleController(self).complete("Gate %s completed" %
                                            workitem.activity_id)
Beispiel #30
0
    def test_cache(self):
        zope.component.provideAdapter(LifeCycleControllerFactory,
                                      adapts=(WorkitemFake,),
                                      provides=ILifeCycleController)
        portal = self.portal
        portal.invokeFactory('DummyContent', 'doc1')
        doc1 = portal.doc1
        definition = ProcessDefinitionFake()
        i = Instance(definition, doc1, 'test')

        wi = WorkitemFake()
        ILifeCycleController(wi).state = 'active'
        i._setObject(wi.id, wi)
        wi = i._getOb(wi.id)
        i._update_cache(wi)
        self.assertEquals([wi], i.getWorkItems())

        ILifeCycleController(wi).state = 'complete'
        i._update_cache(wi)
        self.assertEquals([], i.getWorkItems())
        self.assertEquals([wi], i.getWorkItems('complete'))