95 | | self._notify(ticket, newticket, modtime) |
| 98 | self.ticket = ticket |
| 99 | self.modtime = modtime |
| 100 | self.newticket = newticket |
| 101 | self.reporter = '' |
| 102 | self.owner = '' |
| 103 | link = self.env.abs_href.ticket(ticket.id) |
| 104 | summary = self.ticket['summary'] |
| 105 | author = None |
| 106 | |
| 107 | if not newticket and modtime: # Ticket change |
| 108 | (link, summary) = \ |
| 109 | self._build_notification_changes(ticket, modtime) |
| 110 | else: |
| 111 | link = self.env.abs_href.ticket(ticket.id) |
| 112 | summary = self.ticket['summary'] |
| 113 | if newticket: |
| 114 | author = ticket['reporter'] |
| 115 | |
| 116 | ticket_values = ticket.values.copy() |
| 117 | ticket_values['id'] = ticket.id |
| 118 | ticket_values['description'] = wrap( |
| 119 | ticket_values.get('description', ''), self.COLS, |
| 120 | initial_indent=' ', subsequent_indent=' ', linesep=CRLF, |
| 121 | ambiwidth=self.ambiwidth) |
| 122 | ticket_values['new'] = self.newticket |
| 123 | ticket_values['link'] = link |
| 124 | |
| 125 | subject = self.format_subj(summary) |
| 126 | if not self.newticket: |
| 127 | subject = 'Re: ' + subject |
| 128 | self.data.update({ |
| 129 | 'ticket_props': self.format_props(), |
| 130 | 'ticket_body_hdr': self.format_hdr(), |
| 131 | 'subject': subject, |
| 132 | 'ticket': ticket_values, |
| 133 | }) |
| 134 | NotifyEmail.notify(self, ticket.id, subject, author) |
100 | | def _notify(self, ticket, newticket=True, modtime=None): |
101 | | self.ticket = ticket |
102 | | self.modtime = modtime |
103 | | self.newticket = newticket |
| 139 | def notify_attachment(self, ticket, author, filename, modtime, add): |
| 140 | """Send ticket attachment notification (untranslated)""" |
| 141 | t = deactivate() |
| 142 | translated_fields = ticket.fields |
| 143 | try: |
| 144 | ticket.fields = TicketSystem(self.env).get_ticket_fields() |
| 145 | self.ticket = ticket |
| 146 | self.modtime = modtime |
| 147 | self.newticket = False |
| 148 | self.reporter = '' |
| 149 | self.owner = '' |
| 150 | link = self.env.abs_href.ticket(ticket.id) |
| 151 | summary = self.ticket['summary'] |
| 152 | ticket_values = ticket.values.copy() |
| 153 | ticket_values['id'] = ticket.id |
| 154 | ticket_values['description'] = wrap( |
| 155 | ticket_values.get('description', ''), self.COLS, |
| 156 | initial_indent=' ', subsequent_indent=' ', linesep=CRLF, |
| 157 | ambiwidth=self.ambiwidth) |
| 158 | ticket_values['new'] = self.newticket |
| 159 | ticket_values['link'] = link |
| 160 | subject = 'Re: ' + self.format_subj(summary) |
| 161 | body = '%s attachment "%s"' % (add and 'Add' or 'Delete', filename) |
| 162 | change = { 'author': author } |
| 163 | self.data.update({ |
| 164 | 'ticket_props': self.format_props(), |
| 165 | 'ticket_body_hdr': self.format_hdr(), |
| 166 | 'subject': subject, |
| 167 | 'ticket': ticket_values, |
| 168 | 'change': change, |
| 169 | 'changes_body': body, |
| 170 | }) |
| 171 | NotifyEmail.notify(self, ticket.id, subject, author) |
| 172 | finally: |
| 173 | ticket.fields = translated_fields |
| 174 | reactivate(t) |
112 | | author = None |
113 | | |
114 | | if not self.newticket and modtime: # Ticket change |
115 | | from trac.ticket.web_ui import TicketModule |
116 | | for change in TicketModule(self.env).grouped_changelog_entries( |
117 | | ticket, when=modtime): |
118 | | if not change['permanent']: # attachment with same time... |
119 | | continue |
120 | | author = change['author'] |
121 | | change_data.update({ |
122 | | 'author': obfuscate_email_address(author), |
123 | | 'comment': wrap(change['comment'], self.COLS, ' ', ' ', |
124 | | CRLF, self.ambiwidth) |
125 | | }) |
126 | | link += '#comment:%s' % str(change.get('cnum', '')) |
127 | | for field, values in change['fields'].iteritems(): |
128 | | old = values['old'] |
129 | | new = values['new'] |
130 | | newv = '' |
131 | | if field == 'description': |
132 | | new_descr = wrap(new, self.COLS, ' ', ' ', CRLF, |
133 | | self.ambiwidth) |
134 | | old_descr = wrap(old, self.COLS, '> ', '> ', CRLF, |
135 | | self.ambiwidth) |
136 | | old_descr = old_descr.replace(2 * CRLF, CRLF + '>' + \ |
137 | | CRLF) |
138 | | cdescr = CRLF |
139 | | cdescr += 'Old description:' + 2 * CRLF + old_descr + \ |
140 | | 2 * CRLF |
141 | | cdescr += 'New description:' + 2 * CRLF + new_descr + \ |
142 | | CRLF |
143 | | changes_descr = cdescr |
144 | | elif field == 'summary': |
145 | | summary = "%s (was: %s)" % (new, old) |
146 | | elif field == 'cc': |
147 | | (addcc, delcc) = self.diff_cc(old, new) |
148 | | chgcc = '' |
149 | | if delcc: |
150 | | chgcc += wrap(" * cc: %s (removed)" % |
151 | | ', '.join(delcc), |
152 | | self.COLS, ' ', ' ', CRLF, |
153 | | self.ambiwidth) + CRLF |
154 | | if addcc: |
155 | | chgcc += wrap(" * cc: %s (added)" % |
156 | | ', '.join(addcc), |
157 | | self.COLS, ' ', ' ', CRLF, |
158 | | self.ambiwidth) + CRLF |
159 | | if chgcc: |
160 | | changes_body += chgcc |
161 | | self.prev_cc += self.parse_cc(old) if old else [] |
162 | | else: |
163 | | if field in ['owner', 'reporter']: |
164 | | old = obfuscate_email_address(old) |
165 | | new = obfuscate_email_address(new) |
166 | | newv = new |
167 | | length = 7 + len(field) |
168 | | spacer_old, spacer_new = ' ', ' ' |
169 | | if len(old + new) + length > self.COLS: |
170 | | length = 5 |
171 | | if len(old) + length > self.COLS: |
172 | | spacer_old = CRLF |
173 | | if len(new) + length > self.COLS: |
174 | | spacer_new = CRLF |
175 | | chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old, |
176 | | spacer_old, spacer_new, |
177 | | new) |
178 | | chg = chg.replace(CRLF, CRLF + length * ' ') |
179 | | chg = wrap(chg, self.COLS, '', length * ' ', CRLF, |
180 | | self.ambiwidth) |
181 | | changes_body += ' %s%s' % (chg, CRLF) |
182 | | if newv: |
183 | | change_data[field] = {'oldvalue': old, 'newvalue': new} |
184 | | |
185 | | if newticket: |
186 | | author = ticket['reporter'] |
187 | | |
188 | | ticket_values = ticket.values.copy() |
189 | | ticket_values['id'] = ticket.id |
190 | | ticket_values['description'] = wrap( |
191 | | ticket_values.get('description', ''), self.COLS, |
192 | | initial_indent=' ', subsequent_indent=' ', linesep=CRLF, |
193 | | ambiwidth=self.ambiwidth) |
194 | | ticket_values['new'] = self.newticket |
195 | | ticket_values['link'] = link |
196 | | |
197 | | subject = self.format_subj(summary) |
198 | | if not self.newticket: |
199 | | subject = 'Re: ' + subject |
| 183 | for change in TicketModule(self.env).grouped_changelog_entries( |
| 184 | ticket, when=modtime): |
| 185 | if not change['permanent']: # attachment with same time... |
| 186 | continue |
| 187 | author = change['author'] |
| 188 | change_data.update({ |
| 189 | 'author': obfuscate_email_address(author), |
| 190 | 'comment': wrap(change['comment'], self.COLS, ' ', ' ', |
| 191 | CRLF, self.ambiwidth) |
| 192 | }) |
| 193 | link += '#comment:%s' % str(change.get('cnum', '')) |
| 194 | for field, values in change['fields'].iteritems(): |
| 195 | old = values['old'] |
| 196 | new = values['new'] |
| 197 | newv = '' |
| 198 | if field == 'description': |
| 199 | new_descr = wrap(new, self.COLS, ' ', ' ', CRLF, |
| 200 | self.ambiwidth) |
| 201 | old_descr = wrap(old, self.COLS, '> ', '> ', CRLF, |
| 202 | self.ambiwidth) |
| 203 | old_descr = old_descr.replace(2 * CRLF, CRLF + '>' + \ |
| 204 | CRLF) |
| 205 | cdescr = CRLF |
| 206 | cdescr += 'Old description:' + 2 * CRLF + old_descr + \ |
| 207 | 2 * CRLF |
| 208 | cdescr += 'New description:' + 2 * CRLF + new_descr + \ |
| 209 | CRLF |
| 210 | changes_descr = cdescr |
| 211 | elif field == 'summary': |
| 212 | summary = "%s (was: %s)" % (new, old) |
| 213 | elif field == 'cc': |
| 214 | (addcc, delcc) = self.diff_cc(old, new) |
| 215 | chgcc = '' |
| 216 | if delcc: |
| 217 | chgcc += wrap(" * cc: %s (removed)" % |
| 218 | ', '.join(delcc), |
| 219 | self.COLS, ' ', ' ', CRLF, |
| 220 | self.ambiwidth) + CRLF |
| 221 | if addcc: |
| 222 | chgcc += wrap(" * cc: %s (added)" % |
| 223 | ', '.join(addcc), |
| 224 | self.COLS, ' ', ' ', CRLF, |
| 225 | self.ambiwidth) + CRLF |
| 226 | if chgcc: |
| 227 | changes_body += chgcc |
| 228 | self.prev_cc += self.parse_cc(old) if old else [] |
| 229 | else: |
| 230 | if field in ['owner', 'reporter']: |
| 231 | old = obfuscate_email_address(old) |
| 232 | new = obfuscate_email_address(new) |
| 233 | newv = new |
| 234 | length = 7 + len(field) |
| 235 | spacer_old, spacer_new = ' ', ' ' |
| 236 | if len(old + new) + length > self.COLS: |
| 237 | length = 5 |
| 238 | if len(old) + length > self.COLS: |
| 239 | spacer_old = CRLF |
| 240 | if len(new) + length > self.COLS: |
| 241 | spacer_new = CRLF |
| 242 | chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old, |
| 243 | spacer_old, spacer_new, |
| 244 | new) |
| 245 | chg = chg.replace(CRLF, CRLF + length * ' ') |
| 246 | chg = wrap(chg, self.COLS, '', length * ' ', CRLF, |
| 247 | self.ambiwidth) |
| 248 | changes_body += ' %s%s' % (chg, CRLF) |
| 249 | if newv: |
| 250 | change_data[field] = {'oldvalue': old, 'newvalue': new} |
| 454 | |
| 455 | class TicketAttachmentNotifier(Component): |
| 456 | """Sends notification on attachment change""" |
| 457 | |
| 458 | implements(IAttachmentChangeListener) |
| 459 | |
| 460 | ticket_notify_attachment = BoolOption('notification', |
| 461 | 'ticket_notify_attachment', |
| 462 | 'true', |
| 463 | """Always send notifications on ticket attachment events.""") |
| 464 | |
| 465 | # IAttachmentChangeListener |
| 466 | |
| 467 | def attachment_added(self, attachment): |
| 468 | if self.ticket_notify_attachment: |
| 469 | self._notify_attachment(attachment, True) |
| 470 | |
| 471 | def attachment_deleted(self, attachment): |
| 472 | if self.ticket_notify_attachment: |
| 473 | self._notify_attachment(attachment, False) |
| 474 | |
| 475 | def attachment_reparented(self, attachment, old_parent_realm, |
| 476 | old_parent_id): |
| 477 | pass |
| 478 | |
| 479 | # Implementation |
| 480 | |
| 481 | def _notify_attachment(self, attachment, add): |
| 482 | author = attachment.author |
| 483 | resource = attachment.resource.parent |
| 484 | if resource.realm != 'ticket': |
| 485 | return |
| 486 | ticket = Ticket(self.env, resource.id) |
| 487 | tn = TicketNotifyEmail(self.env) |
| 488 | filename = attachment.filename |
| 489 | date = attachment.date or datetime.now(utc) |
| 490 | tn.notify_attachment(ticket, author, filename, date, add) |