Trac is being migrated to new services! Issues can be found in our new YouTrack instance and WIKI pages can be found on our website.

LocalTracChanges: CCMe.py

File CCMe.py, 7.6 KB (added by datallah, 11 years ago)

Updated CCMe.py for Trac 1.0

Line 
1from genshi.builder import tag
2from genshi.filters import Transformer
3
4from trac.core import implements, Component
5from trac.ticket.api import ITicketActionController
6from trac.ticket.default_workflow import ConfigurableTicketWorkflow
7from trac.util import get_reporter_id
8from trac.web.chrome import add_warning, Chrome
9from trac.web.api import ITemplateStreamFilter
10
11class CCHandlerActionController(Component):
12    """ Automatically CC the User based on his/her selection of a field on the screen
13
14    Don't forget to add `CCHandlerActionController` to the workflow
15    option in [ticket].
16    If there is no workflow option, the line will look like this:
17
18    workflow = ConfigurableTicketWorkflow, CCHandlerActionController
19    """
20    implements(ITemplateStreamFilter, ITicketActionController)
21
22    def __init__(self):
23        self.workflow = ConfigurableTicketWorkflow(self.env)
24
25    # ITemplateStreamFilter methods
26
27    def filter_stream(self, req, method, filename, stream, data):
28        if filename == 'ticket.html' and req.authname != 'anonymous':
29            ticket = data.get('ticket')
30            if ticket and ticket.exists:
31                #filter = Transformer("div[@class='field']/fieldset[@class='iefix']")
32                #filter = Transformer("div[@id='content' and @class='ticket']/div[@id='ticket']")
33                filter = Transformer("//form[@id='propertyform']/div[@class='field']")
34                #Hide the CC checkbox if it is present
35                if data.has_key('fields'):
36                  for dict in data['fields']:
37                    if dict['name'] == 'cc':
38                      if dict.has_key('cc_entry'):
39                        dict['skip'] = True
40                        dict['rendered'] = 'no'
41                      break
42
43
44                return stream | filter.before(self._append_CC_radio(req, ticket)) 
45        return stream
46
47    # ITicketActionController methods
48
49    def get_ticket_actions(self, req, ticket):
50        #We need to handle all statuses so that we can perform the action regardless of which action was selected
51        #TODO: What about other actions not in ConfigurableTicketWorkflow? (We'll worry about it when it happens)
52        return self.workflow.get_ticket_actions(req, ticket)
53
54    def get_all_status(self):
55        return []
56
57    def render_ticket_action_control(self, req, ticket, action):
58        return (None, None, None)
59
60    def get_ticket_changes(self, req, ticket, action):
61        return {}
62
63    def apply_action_side_effects(self, req, ticket, action):
64        if (action != 'delete' and 'cc_update_manual' in req.args):
65            cc_action, cc_entry, cc_list = self._tweak_cc(req, ticket['cc'], req.args['cc_update_manual'] == 'yes')
66
67            if cc_action == 'remove':
68                self.log.info("Removing CC %s on ticket %s" % (cc_entry, ticket.id))
69                #add_warning(req, "You have been removed from the Cc list.")
70                cc_list.remove(cc_entry)
71            elif cc_action == 'add':
72                self.log.info("Adding CC %s on ticket %s" % (cc_entry, ticket.id))
73                #add_warning(req, "You have been added to the Cc list.")
74                cc_list.append(cc_entry)
75            else:
76                return
77
78            new_cc_val = ', '.join(cc_list)
79
80            with self.env.db_transaction as db:
81                cursor = db.cursor()
82                cursor.execute("UPDATE ticket SET cc=%s WHERE id=%s",
83                           (new_cc_val, ticket.id))
84
85    #Internal Functions
86
87    def _append_CC_radio(self, req, ticket):
88        notify_reporter = self.env.config.getbool('notification',
89                                                  'always_notify_reporter')
90        notify_owner = self.env.config.getbool('notification',
91                                               'always_notify_owner')
92        notify_updater = self.env.config.getbool('notification',
93                                                 'always_notify_updater')
94
95        self.log.info("Appending CC Radio Button to ticket %s for user %s" % (ticket.id, req.authname))
96
97        add_checked = False
98        remove_checked = False
99        disabled = False
100        notemsg = None
101
102        if notify_reporter and ticket.values.get('reporter') == req.authname:
103            notemsg = " (You are permanently added to the cc list because you are the reporter.)"
104            add_checked = True
105            disabled = True
106        elif notify_owner and ticket.values.get('owner') == req.authname:
107            notemsg = " (You are automatically added to the cc list because you are the owner.)"
108            add_checked = True
109            disabled = True
110        elif notify_updater:
111            if self._already_changed_ticket(req, ticket):
112                add_checked = True
113                disabled = True
114                notemsg = " (You are permanently added to the cc list because you commented on the ticket.)"
115            else:
116                notemsg = " Note: Updating the ticket will permanently add you to the cc list."
117
118        if not add_checked:
119            add_checked = 'cc_update_manual' in req.args and req.args['cc_update_manual'] == 'yes'
120            remove_checked = 'cc_update_manual' in req.args and req.args['cc_update_manual'] == 'no'
121            #If this isn't a preview with a value already selected, select the current
122            if (not add_checked and not remove_checked):
123                cc_action, cc_entry, cc_list = self._tweak_cc(req, ticket['cc'], True)
124                if cc_action != 'add':
125                    add_checked = True
126                else:
127                    remove_checked = True
128
129        return tag.p("Cc me to this ticket: ",
130            tag.input(type="radio", id="field-cc-manual1", name="cc_update_manual",
131                      value="yes", checked=(add_checked or None), disabled=(disabled or None)),
132            tag.label("Yes", for_="field-cc-manual1"), " ",
133            tag.input(type="radio", id="field-cc-manual0", name="cc_update_manual",
134                      value="no", checked=(remove_checked or None), disabled=(disabled or None)),
135            tag.label("No", for_="field-cc-manual0"),
136            notemsg and tag.em(notemsg) or "")
137
138    def _already_changed_ticket(self, req, ticket):
139        with self.env.db_query as db: 
140            cursor = db.cursor()
141            cursor.execute("SELECT DISTINCT author,ticket FROM ticket_change "
142                       "WHERE ticket=%s AND author=%s", (ticket.id, req.authname))
143            for author,ticket in cursor:
144                return True
145
146        return False
147
148    def _tweak_cc(self, req, cc, cc_me):
149        """Return an (action, recipient, cc_list) tuple corresponding to a change
150        of CC status for this user relative to the current `cc_list`."""
151        entries = []
152        email = req.session.get('email', '').strip()
153        if req.authname != 'anonymous':
154            entries.append(req.authname)
155        elif email:
156            entries.append(email)
157        else:
158            author = get_reporter_id(req, 'author').strip()
159            if author and author != 'anonymous':
160                email = author.split()[-1]
161                if (email[0], email[-1]) == ('<', '>'):
162                    email = email[1:-1]
163                entries.append(email)
164
165        add = []
166        remove = []
167        cc_list = Chrome(self.env).cc_list(cc)
168        for entry in entries:
169            if entry in cc_list:
170                if not cc_me:
171                    remove.append(entry)
172            else:
173                if cc_me:
174                    add.append(entry)
175
176        action = entry = ''
177        if remove:
178            action, entry = ('remove', remove[0])
179        elif add:
180            action, entry = ('add', add[0])
181        return (action, entry, cc_list)
182
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!