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