[CMF-checkins] CVS: CMF/CMFCollector - CollectorIssue.py:1.2

Ken Manheimer klm@zope.com
Thu, 11 Oct 2001 19:51:26 -0400


Update of /cvs-repository/CMF/CMFCollector
In directory cvs.zope.org:/tmp/cvs-serv12869

Modified Files:
	CollectorIssue.py 
Log Message:
 ***  I learned a valuable workflow lesson today.  Any state by
      which workflow actions are conditioned, *and* which are
      changed directly by workflow actions, must be workflow
      variables. Otherwise you run into the non-workflow application
      machinery having to know too much about the nuances of the
      workflow logic that you defeat compartmentalization.

 ***  This was some hard-earned wisdom.-)

.comment_number - starting at 1, to allow for initial request entry.

ACTIONS_ORDER - so we have a nice way to sequence the presentation of
                the workflow actions we know about.

.assigned_to removed as an instance variable every where, and put in:

.assigned_to(): Now a routine that obtains the info from the issue
                workflow.

.do_action(): Incorporated workfow.

.status(): Consult the workflow for the current state.

._valid_actions() and ._valid_action_pairs(): Tidily package valid
                                              actions for presentation.

Revised dublin core to consult workflow-status interfaces that replace
some instance vars.

.__init__(): Removed 'assigned_to' from args since it's now a workflow var


=== CMF/CMFCollector/CollectorIssue.py 1.1 => 1.2 ===
     comment_delimiter = "<hr solid id=comment_delim>"
 
-    comment_number = 0
+    comment_number = 1
+
+    ACTIONS_ORDER = ['Accept', 'Resign', 'Assign',
+                     'Resolve', 'Reject', 'Defer'] 
 
     def __init__(self, 
                  id, container,
@@ -94,7 +97,6 @@
                  topic=None, classification=None,
                  security_related=0,
                  importance=None, severity=None,
-                 assigned_to=None, current_status='pending',
                  resolution=None,
                  reported_version=None, other_version_info=None,
                  creation_date=None, modification_date=None,
@@ -138,8 +140,6 @@
         self.security_related = security_related
         self.importance = importance
         self.severity = severity
-        self.assigned_to = assigned_to
-        self.current_status = current_status
         self.resolution = resolution
         self.reported_version = reported_version
         self.other_version_info = other_version_info
@@ -194,18 +194,44 @@
     security.declareProtected(CMFCorePermissions.View, 'get_transcript')
     def get_transcript(self):
         return self._getOb(TRANSCRIPT_NAME)
-
+    
     security.declareProtected(AddCollectorIssueComment, 'do_action')
-    def do_action(self, action, comment, attachments=None):
+    def do_action(self, action, comment, attachments=None, assignees=None):
         """Execute an action, adding comment to the transcript."""
+
+        username = str(getSecurityManager().getUser())
+
+        if string.lower(action) != 'comment':
+            # Confirm against portal actions tool:
+            if action not in self._valid_actions():
+                raise 'Unauthorized', "Invalid action '%s'" % action
+
+            self.portal_workflow.doActionFor(self,
+                                             action,
+                                             comment=comment,
+                                             username=username,
+                                             assignees=assignees)
+
         transcript = self.get_transcript()
         self.comment_number = self.comment_number + 1
-        entry_leader = "\n\n" + self._entry_header(action) + "\n\n"
+        entry_leader = "\n\n" + self._entry_header(action, username) + "\n\n"
         transcript._edit('stx',
                          transcript.EditableBody()
                          + entry_leader
                          + util.process_comment(comment))
 
+    security.declareProtected(CMFCorePermissions.View, 'assigned_to')
+    def assigned_to(self):
+        """Return the current supporters list, according to workflow."""
+        wftool = getToolByName(self, 'portal_workflow')
+        return wftool.getInfoFor(self, 'assigned_to', [])
+
+    security.declareProtected(CMFCorePermissions.View, 'status')
+    def status(self):
+        """Return the current status according to workflow."""
+        wftool = getToolByName(self, 'portal_workflow')
+        return wftool.getInfoFor(self, 'state', '??')
+
     security.declareProtected(AddCollectorIssueArtifact, 'add_artifact')
     def add_artifact(self, id, type, description, file):
         """Add new artifact, and note in transcript."""
@@ -214,8 +240,9 @@
         it.description = description
         it.manage_upload(file)
         transcript = self.get_transcript()
+        user = getSecurityManager().getUser()
         entry_leader = ("\n\n"
-                        + self._entry_header("New Artifact '%s'" % id)
+                        + self._entry_header("New Artifact '%s'" % id, user)
                         + "\n\n")
         transcript._edit('stx',
                          transcript.EditableBody()
@@ -226,15 +253,17 @@
                            text_format=DEFAULT_TRANSCRIPT_FORMAT):
         """Create events and comments transcript, with initial entry."""
 
+        user = getSecurityManager().getUser()
         addDocument(self, TRANSCRIPT_NAME, description=description)
         it = self.get_transcript()
         it._setPortalTypeName('Collector Issue Transcript')
-        text = "%s\n\n %s " % (self._entry_header('Request', prefix="== "),
-                               description)
+        text = ("%s\n\n %s " %
+                (self._entry_header('Request', user, prefix="== "),
+                 description))
         it._edit(text_format=text_format, text=text)
         it.title = self.title
 
-    def _entry_header(self, type, prefix="<hr> == ", suffix=" =="):
+    def _entry_header(self, type, user, prefix="<hr> == ", suffix=" =="):
         """Return text for the header of a new transcript entry."""
         # Ideally this would be a skin method (probly python script), but i
         # don't know how to call it from the product, sigh.
@@ -244,7 +273,6 @@
         else:
             lead = t
 
-        user = getSecurityManager().getUser()
         return ("%s%s by %s on %s%s" %
                 (prefix, lead, str(user), DateTime().aCommon(), suffix))
 
@@ -253,6 +281,32 @@
         """Quote text for use in literal citations."""
         return util.cited_text(self.get_transcript().text)
 
+    def _valid_actions(self):
+        """Return actions valid according to workflow and application logic."""
+        pa = getToolByName(self, 'portal_actions', None)
+        return [entry['name']
+                for entry in pa.listFilteredActionsFor(self)['issue_workflow']]
+
+    security.declareProtected(CMFCorePermissions.View, 'valid_actions_pairs')
+    def valid_actions_pairs(self):
+        """Return ordered (prettyname, rawname) valid workflow action names."""
+        # XXX I would do this with a python script method, but i'm hitting
+        #     inability to assign to indexes (eg, 'list[x] = 1' or 
+        #     'dict[x] = 1'), so having to resort to python code, sigh.
+
+        order=self.ACTIONS_ORDER
+        got = [()] * len(order)
+        remainder = []
+
+        for raw in self._valid_actions():
+            pretty = raw.split('_')[0].capitalize()
+            if pretty in order:
+                got[order.index(pretty)] = (raw, pretty)
+            else:
+                remainder.append((raw, pretty))
+        return filter(None, got) + remainder
+
+
     #################################################
     # Dublin Core and search provisions
 
@@ -309,7 +363,7 @@
                 + self.classification + ' '
                 + self.importance + ' '
                 + self.severity + ' '
-                + self.current_status + ' '
+                + self.status() + ' '
                 + self.resolution + ' '
                 + self.reported_version + ' '
                 + self.other_version_info + ' '
@@ -317,15 +371,15 @@
 
     def Subject(self):
         """The structured attrs, combined w/field names for targeted search."""
+        assigned_to = tuple(['assigned_to:' + i for i in self.assigned_to()])
         return ('topic:' + self.topic,
                 'classification:' + self.classification,
                 'security_related:' + ((self.security_related and '1') or '0'),
                 'importance:' + self.importance,
                 'severity:' + self.severity,
-                'assigned_to:' + (self.assigned_to or ''),
-                'current_status:' + (self.current_status or ''),
+                'status:' + (self.status() or ''),
                 'resolution:' + (self.resolution or ''),
-                'reported_version:' + self.reported_version)
+                'reported_version:' + self.reported_version) + assigned_to
 
     def __repr__(self):
         return ("<%s %s \"%s\" at 0x%s>"
@@ -349,7 +403,6 @@
                       security_related=0,
                       importance=None,
                       severity=None,
-                      assigned_to=None,
                       reported_version=None,
                       other_version_info=None,
                       REQUEST=None):
@@ -370,7 +423,6 @@
                         security_related=security_related,
                         importance=importance,
                         severity=severity,
-                        assigned_to=assigned_to,
                         reported_version=reported_version,
                         other_version_info=other_version_info)
     it._setPortalTypeName('Collector Issue')