Roundup Tracker

When you create new issue, you may want to make other issues depend on this issue. Normally you would have to load each of the other issues and add the new issue to the multilink.

This change overrides the default edit action to handle a modified syntax for the multilink edit box that changes the multilink property for the specified issue to point to the issue being edited.

So for example if you edit the superseder property of issue5 and submitted::

issue5 would have its superseder property set to: 1, 2. Issue3 and issue4 would have "5" (issue5) added to their superseder properties, and issue 7 would have "5" (issue5) removed from its superseder property. Note that (+3) is the same as (3). This will work for any multilink that links to items of the same type as the item you are editing. If you are editing an issue, it works for multilinks in the issue that link to other issues. If you are editing a user it works for multilinks of the user that link to other users.

Note that the current code only works for editing an issue (object) and not when creating an issue (object).

Put the following in a file in your extensions directory::

  from roundup.cgi.actions import EditItemAction, NewItemAction

  import roundup.hyperdb

  import cgi

  class Edit2Action(EditItemAction):
      '''An alternate edit action that allows reverse links in multilinks

         If a property is a multilink to the class being edited, this
         action allows the user to specify links in the reverse
         direction.

         Normally editing a property changes the property on the issue
         (class) being edited, but it is often useful to edit other
         issue (class) properties and link them the the issue (class)
         you are editing.

         E.G. You are editing issue27's dependencies and you realise
         that you need to have issue33's dependson property point to
         issue27. While you could pull up issue33 and edit it that's an
         extra step. With this action you can add '(33)' (without the
         quotes) to the dependson property of issue27 and get 27 added
         to issue33's dependson property.

         FIXME: This action assumes that the stuff between the
         parenthesis is a number so using (issue33) doesn't quite work
         as this code needs to do the name->number mapping if it's not a
         pure number. Need to see how the original EditAction or
         subsequent parsefromform code handles it.

      '''

      def handle(self):
          ''' Target for an alternate edit for multilinks allowing reverse links.
          '''

          print "Edit2Action: enter"

          _remapForm(self)

          if __debug__:
              print "self.form returned\n"
              print self.form

          # call the core EditItemAction to process the edit.
          EditItemAction.handle(self)


  def _remapForm(self):
      classes = self.db.issue.properties.keys()

      if __debug__:
          print "_remapForm: self.nodeid: '%s'"%self.nodeid

      nodeid=self.nodeid

      if nodeid is None:
          nodeid="issue-1"

      for mfi in self.form.list:
          if mfi.name in classes:
              if __debug__:
                  print "_remapForm: handling field: %s=%s"%(mfi.name,mfi.value)

                  property = self.db.issue.properties[mfi.name]
                  if isinstance(property, roundup.hyperdb.Multilink):
                      if __debug__:
                          print "_remapForm: Found multilink to class '%s'"%\
                              property.classname

                      if property.classname != self.classname:
                          if __debug__:
                              print "_remapForm: Skipping %s due to classname mismatch class %s != property %s"%(mfi.name, self.classname, property.classname)
                          continue

                      multilink = mfi.value
                      multilink = multilink.replace(',',' ') # replace commas with spaces
                      multilinks = multilink.split() # split on runs of whitespace
                      if __debug__:
                          print "_remapForm: multilink value list: ", multilinks

                      newlinks=[] # list of links that will be pssed through after
                                  # decorated links are removed
                      for item in multilinks:
                          item_prefix=item[0:1]
                          item_suffix=item[-1]
                          item_value=item[1:-1]

                          if __debug__:
                              print "_remapForm: item prefix: %s, suffix: %s, item: %s"%(item_prefix,item_suffix,item_value)

                          if item_prefix == '(' and item_suffix == ')': # revlink item
                              if item_value[0:1] == '-':
                                  # remove the revlink
                                  if __debug__:
                                      print "_remapForm: item is negative, remove link"
                                  # strip the - sign leaving a bare number
                                  item_value=item_value[1:]
                                  # verify that the item to be removed
                                  # is in the multilink otherwise an error
                                  # is thrown.
                                  if nodeid not in self.db.issue.get(item_value, mfi.name):
                                      # not there no need to remove
                                      print "skipping unlink of %s from issue%s@%s"%(nodeid, item_value, mfi.name)
                                      continue
                                  self.form.list.append(
                                      cgi.MiniFieldStorage(
                                          "issue%s:remove:%s"% \
                                          (item_value, mfi.name), nodeid)
                                  )
                              else:
                                  if item_value[0:1] == '+':
                                      # strip the + sign leaving a bare number
                                      item_value=item_value[1:]
                                      if __debug__:
                                          print "_remapForm: + removed, new item_value is %s"%item_value
                                  # it's number at this point
                                  # add the appropriate form item to make the change
                                  self.form.list.append(
                                      cgi.MiniFieldStorage(
                                          "issue%s:add:%s"% \
                                          (item_value, mfi.name), nodeid)
                                  )
                          else:
                              # save the undecorated item for later submission
                              newlinks.append(item)

                      # Turn the list of undecorated links (in newlinks)
                      # into a string and overwrite the original value
                      # in the MiniFieldStorage
                      mfi.value = ",".join(newlinks)

      if __debug__:
          print "self.form\n"
          print self.form
          print "returning from remapForm"

  def init(instance):
      '''Override the default edit action with this new version'''
      instance.registerAction('edit', Edit2Action)

The registerAction replaces roundup's default edit operation with this new function (and the new function ends up calling the original edit function once things are remapped).

It's a little rough but it works. Feel free to improve.

In particular if the user making the change doesn't have edit rights to the other object, the whole edit fails.


CategoryActions