You can turn the default search box in the classic (or other) tracker into a jump/search box.
- If all you enter is a single number (unquoted), it will jump to that issue. If you want to search for a number, surround it with quotes(").
- If you enter a comma separated list of numbers (e.g. by copying a superseder entry box), transform the search to an id search for those issue items.
- If you enter something that looks like a designator (e.g. user22, msg1234), jump to that item. Quote the designator or add additional words to search for the designator.
- Otherwise search for an issue with all those terms just like the search box
All these changes are done on a per tracker basis and requires no changes to the core roundup code. So all these files live under your tracker directory.
Html page change
Change the existing search form in html/page.html. It should look something like:
<form method="GET" action="issue"> <input type="hidden" name="@columns" tal:attributes="value columns_showall" value="id,activity,title,creator,assignedto,status"/> <input type="hidden" name="@sort" value="activity"/> <input type="hidden" name="@group" value="priority"/> <input id="search-text" name="@search_text" size="10" tal:attributes="value request/search_text | default" /> <input type="submit" id="submit" name="submit" value="Search" i18n:attributes="value" /> </form>
Add a hidden @action field with the value JumpSearch to what exists so it looks like:
<form method="GET" action="issue"> <input type="hidden" name="@columns" tal:attributes="value columns_showall" value="id,activity,title,creator,assignedto,status"/> <input type="hidden" name="@sort" value="activity"/> <input type="hidden" name="@group" value="priority"/> <input type="hidden" name="@action" value="JumpSearch"/> <input id="search-text" name="@search_text" size="10" tal:attributes="value request/search_text | default" /> <input type="submit" id="submit" name="submit" value="Search" i18n:attributes="value" /> </form>
Consider replacing the submit button label text with something more descriptive. Perhaps "Goto/Search".
Add the extension
Now we create the action JumpSearch. Create an extensions/jumpsearch.py file in your tracker. Fill it with the following:
1 import roundup.cgi.actions
2 import cgi
3
4 import re
5
6 class JumpSearch(roundup.cgi.actions.SearchAction, roundup.cgi.actions.ShowAction):
7 ''' Used to create a combo Jump to issue and Search issues box.
8
9 If a single unquoted number is entered jump to that issue.
10 If the search item looks like an item designator (user2,
11 issue23) jump to that item.
12 If a list of numbers is entered (e.g. by copying a dependson
13 entry box), transform the search to an id search for those
14 issue items.
15 Otherwise call the standard Search function.
16 '''
17
18 def handle(self):
19 if '@search_text' in self.form:
20 # get the value of the @search_text field
21 # only the first is used if there are multiples.
22 search_text = self.form.getfirst('@search_text')
23 else:
24 # if there is no @search_text pass it off to the default
25 # search action to have it deal with it
26 roundup.cgi.actions.SearchAction.handle(self)
27 return
28
29 if search_text.isdigit():
30 # we have a single number (unquoted) with possible whitespace
31 # so jump to that issue.
32
33 # add the @number field used to do the redirect
34 self.form.list.append(cgi.MiniFieldStorage("@number", search_text))
35 # add the @type field used to do the redirect to an issue with
36 # number @number.
37 self.form.list.append(cgi.MiniFieldStorage("@type", "issue"))
38 # call the existing ShowAction for display
39 return roundup.cgi.actions.ShowAction.handle(self)
40
41 # really wish I could do an elif match=....
42 # match msg22 or user13 or issue10
43 designator=re.compile(r"^(\w+([0-9]+?)$") # split class and node id
44 match=re.match(designator, search_text)
45 if match:
46 # we have something that could be user2 or msg2389
47 # try jumping to it
48 klass = match.group(1)
49 nodeid = match.group(2)
50
51 #verify that class exists
52 try:
53 self.db.getclass(klass)
54 except KeyError:
55 # the class doesn't exist
56 # call the existing SearchAction for display
57 return roundup.cgi.actions.SearchAction.handle(self)
58
59 # class exists show the designator
60 self.form.list.append(cgi.MiniFieldStorage("@number", nodeid))
61 # add the @type field used to do the redirect to an issue with
62 # number @number.
63 self.form.list.append(cgi.MiniFieldStorage("@type", klass))
64 # note that self.form.list still has @search_text in it,
65 # but it's ignored by ShowAction so don't waste cycles
66 # removing it.
67 return roundup.cgi.actions.ShowAction.handle(self)
68
69 # match 2,3,4 5 as list of id's.
70 # NOTE: this has to be done in two steps as my prefered
71 # regexp:
72 # r"^\s*(\d+)(?:[,\s]+(\d+))+\s*$"
73 # only returns the first and last match (e.g. in 1,2,3,4, I
74 # get only 1 and 4). I want this to return 1 and 2 and 3 and 4
75 # but..... Why, who knows. This first match makes sure the
76 # list is numbers with spaces/commas only.
77 idlist=re.compile(r"^\s*\d+(?:[,\s]+\d+)+\s*$") # match id list
78 match=re.match(idlist, search_text)
79 if match:
80 # overall pattern matches.
81 idelement=re.compile(r"(\d+)[,\s]*")
82 nums=re.findall(idelement, search_text)
83 ids=','.join(nums)
84 # remove @search_text entry and add search for id
85 # have to remove @search_text since SearchAction
86 # uses it.
87 self.form.list[:] = [fs for fs in self.form.list
88 if fs.name != '@search_text']
89 self.form.list.append(cgi.MiniFieldStorage("id", ids))
90 return roundup.cgi.actions.SearchAction.handle(self)
91
92 else:
93 # call the existing SearchAction for display
94 return roundup.cgi.actions.SearchAction.handle(self)
95
96
97 def init(instance):
98 # register the new jumpsearch action.
99 instance.registerAction('jumpsearch', JumpSearch)