1 | from trac.core import * |
---|
2 | from trac.web.api import ITemplateStreamFilter |
---|
3 | |
---|
4 | from genshi.builder import tag |
---|
5 | from genshi.filters import Transformer |
---|
6 | |
---|
7 | revision = "$Rev$" |
---|
8 | url = "$URL$" |
---|
9 | |
---|
10 | class TicketCreateButtons(Component): |
---|
11 | """Add a buttons to the ticket box to create related tickets which |
---|
12 | inherit some values from the current ticket. |
---|
13 | |
---|
14 | The [tickets-create-buttons] section of trac.ini can be used to |
---|
15 | add buttons which create a new ticket based on the current |
---|
16 | ticket. The plugin handles all *.tag values in that section. The |
---|
17 | values are as follows: |
---|
18 | |
---|
19 | tag : An argument to genshi.filters.Transfomer used to find the |
---|
20 | ticket form element to which the button will be prepended. |
---|
21 | Typically this is a custom field name. For example, |
---|
22 | .//td[@headers="h_blockedby"] for the MasterTickets blockedBy |
---|
23 | field. |
---|
24 | label : The label for the button (e.g., "Create") |
---|
25 | title : The HTML title element used as a tool tip for the button |
---|
26 | |
---|
27 | inherit : A comma-separated list of fields whose values should be |
---|
28 | inherited from the current ticket. If not present, all fields |
---|
29 | are inherited. if present but blank, no fields are inherited. |
---|
30 | Otherwise, the listed fields are inherited. |
---|
31 | |
---|
32 | link : A comma-separated list of pairs of fields used to link the |
---|
33 | two tickets. Each element is in the form newfield:currentfield. |
---|
34 | For example, blockedby:id sets the new ticket's blockedby field |
---|
35 | to the current tickets id. link fields override inherited |
---|
36 | fields. |
---|
37 | |
---|
38 | set : A comma-separated list of field:value pairs for setting |
---|
39 | values in the new ticket. For example, keywords:Foo sets the |
---|
40 | new ticket's keywords field to Foo. set fields override |
---|
41 | inherited and link field. |
---|
42 | |
---|
43 | Example: |
---|
44 | |
---|
45 | [ticket-create-buttons] |
---|
46 | blocking.tag = .//td[@headers="h_blocking"] |
---|
47 | blocking.label = Create |
---|
48 | blocking.title = Create a new successor |
---|
49 | blocking.inherit = type, owner, reporter, milestone, component |
---|
50 | blocking.link = blockedby:id |
---|
51 | blocking.set = keywords:Foo |
---|
52 | """ |
---|
53 | |
---|
54 | implements(ITemplateStreamFilter) |
---|
55 | |
---|
56 | # ITemplateStreamFilter methods |
---|
57 | |
---|
58 | def filter_stream(self, req, method, filename, stream, data): |
---|
59 | if filename == 'ticket.html': |
---|
60 | ticket = data.get('ticket') |
---|
61 | if ticket and ticket.exists and \ |
---|
62 | 'TICKET_CREATE' in req.perm(ticket.resource): |
---|
63 | # Find the configured buttons (anything in |
---|
64 | # [ticket-create-buttons] that has a name like "*.tag") |
---|
65 | options=self.config.options('ticket-create-buttons') |
---|
66 | buttons=[] |
---|
67 | for n, v in options: |
---|
68 | p=n.split('.') |
---|
69 | if len(p)==2 and p[1] == 'tag': |
---|
70 | buttons.append(p[0]) |
---|
71 | |
---|
72 | # Create the configured buttons |
---|
73 | for b in buttons: |
---|
74 | tag=self.config.get('ticket-create-buttons','%s.tag' % b) |
---|
75 | filter = Transformer(tag) |
---|
76 | stream = stream | filter.prepend(self._create_button(b, req, ticket, data)) |
---|
77 | return stream |
---|
78 | |
---|
79 | def _create_button(self, b, req, ticket, data): |
---|
80 | # Text for button |
---|
81 | label=self.config.get('ticket-create-buttons','%s.label' % b) |
---|
82 | title=self.config.get('ticket-create-buttons','%s.title' % b) |
---|
83 | |
---|
84 | # Field values for new ticket |
---|
85 | fields = {} |
---|
86 | |
---|
87 | # Values inherited from the current ticket |
---|
88 | # No setting: all, blank setting: none, Otherwise: the fields listed |
---|
89 | inherit=self.config.getlist('ticket-create-buttons', |
---|
90 | '%s.inherit' % b, |
---|
91 | default=data.keys()) |
---|
92 | for f in inherit: |
---|
93 | fields[f]=ticket[f] |
---|
94 | |
---|
95 | # Fields that link the new ticket to the current ticket |
---|
96 | # Missing or empty, no links. |
---|
97 | link=self.config.getlist('ticket-create-buttons','%s.link' % b, default=[]) |
---|
98 | for l in link: |
---|
99 | to, fr = l.split(':') |
---|
100 | if fr == 'id': |
---|
101 | fields[to] = ticket.id |
---|
102 | else: |
---|
103 | fields[to]=ticket[fr] |
---|
104 | |
---|
105 | # Specific value assignments. E.g., a button could always create a test |
---|
106 | set=self.config.getlist('ticket-create-buttons','%s.set' % b, default=[]) |
---|
107 | for s in set: |
---|
108 | n, v = s.split(':') |
---|
109 | fields[n]=v |
---|
110 | |
---|
111 | # Build the form with the values set up above. |
---|
112 | return tag.form( |
---|
113 | tag.div( |
---|
114 | tag.input(type="submit", name="create_"+b, value=label, |
---|
115 | title=title), |
---|
116 | # With name='field_'+n here the field prefilled for post but not for get |
---|
117 | [tag.input(type="hidden", name=n, value=v) for n, v in |
---|
118 | fields.items()], |
---|
119 | class_="inlinebuttons"), |
---|
120 | # With "post" here instead of "get" the ticket is previewed and |
---|
121 | # we get a warning about the missing summary. |
---|
122 | method="get", action=req.href.newticket()) |
---|