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::
1 2 (3) (4) (-7)
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.