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.