From e2d8ea996bbf6404148e25510de154e178ad7941 Mon Sep 17 00:00:00 2001
From: Emmanuel Blot
Date: Fri, 13 May 2011 19:38:32 +0200
Subject: [PATCH] Add attachment notification
---
trac/ticket/notification.py | 290 ++++++++++++++++++++++++++++---------------
1 files changed, 187 insertions(+), 103 deletions(-)
diff --git a/trac/ticket/notification.py b/trac/ticket/notification.py
index ed25467..931b234 100644
--- a/trac/ticket/notification.py
+++ b/trac/ticket/notification.py
@@ -18,16 +18,19 @@
from __future__ import with_statement
+from datetime import datetime
from hashlib import md5
from unicodedata import east_asian_width
from genshi.template.text import NewTextTemplate
+from trac.attachment import IAttachmentChangeListener
from trac.core import *
from trac.config import *
from trac.notification import NotifyEmail
from trac.ticket.api import TicketSystem
-from trac.util.datefmt import to_utimestamp
+from trac.ticket.model import Ticket
+from trac.util.datefmt import to_utimestamp, utc
from trac.util.text import CRLF, wrap, obfuscate_email_address, to_unicode, \
text_width
from trac.util.translation import deactivate, reactivate
@@ -92,121 +95,165 @@ class TicketNotifyEmail(NotifyEmail):
translated_fields = ticket.fields
try:
ticket.fields = TicketSystem(self.env).get_ticket_fields()
- self._notify(ticket, newticket, modtime)
+ self.ticket = ticket
+ self.modtime = modtime
+ self.newticket = newticket
+ self.reporter = ''
+ self.owner = ''
+ link = self.env.abs_href.ticket(ticket.id)
+ summary = self.ticket['summary']
+ author = None
+
+ if not newticket and modtime: # Ticket change
+ (link, summary) = \
+ self._build_notification_changes(ticket, modtime)
+ else:
+ link = self.env.abs_href.ticket(ticket.id)
+ summary = self.ticket['summary']
+ if newticket:
+ author = ticket['reporter']
+
+ ticket_values = ticket.values.copy()
+ ticket_values['id'] = ticket.id
+ ticket_values['description'] = wrap(
+ ticket_values.get('description', ''), self.COLS,
+ initial_indent=' ', subsequent_indent=' ', linesep=CRLF,
+ ambiwidth=self.ambiwidth)
+ ticket_values['new'] = self.newticket
+ ticket_values['link'] = link
+
+ subject = self.format_subj(summary)
+ if not self.newticket:
+ subject = 'Re: ' + subject
+ self.data.update({
+ 'ticket_props': self.format_props(),
+ 'ticket_body_hdr': self.format_hdr(),
+ 'subject': subject,
+ 'ticket': ticket_values,
+ })
+ NotifyEmail.notify(self, ticket.id, subject, author)
finally:
ticket.fields = translated_fields
reactivate(t)
- def _notify(self, ticket, newticket=True, modtime=None):
- self.ticket = ticket
- self.modtime = modtime
- self.newticket = newticket
+ def notify_attachment(self, ticket, author, filename, modtime, add):
+ """Send ticket attachment notification (untranslated)"""
+ t = deactivate()
+ translated_fields = ticket.fields
+ try:
+ ticket.fields = TicketSystem(self.env).get_ticket_fields()
+ self.ticket = ticket
+ self.modtime = modtime
+ self.newticket = False
+ self.reporter = ''
+ self.owner = ''
+ link = self.env.abs_href.ticket(ticket.id)
+ summary = self.ticket['summary']
+ ticket_values = ticket.values.copy()
+ ticket_values['id'] = ticket.id
+ ticket_values['description'] = wrap(
+ ticket_values.get('description', ''), self.COLS,
+ initial_indent=' ', subsequent_indent=' ', linesep=CRLF,
+ ambiwidth=self.ambiwidth)
+ ticket_values['new'] = self.newticket
+ ticket_values['link'] = link
+ subject = 'Re: ' + self.format_subj(summary)
+ body = '%s attachment "%s"' % (add and 'Add' or 'Delete', filename)
+ change = { 'author': author }
+ self.data.update({
+ 'ticket_props': self.format_props(),
+ 'ticket_body_hdr': self.format_hdr(),
+ 'subject': subject,
+ 'ticket': ticket_values,
+ 'change': change,
+ 'changes_body': body,
+ })
+ NotifyEmail.notify(self, ticket.id, subject, author)
+ finally:
+ ticket.fields = translated_fields
+ reactivate(t)
- changes_body = ''
- self.reporter = ''
- self.owner = ''
- changes_descr = ''
+ def _build_notification_changes(self, ticket, modtime):
+ from trac.ticket.web_ui import TicketModule
change_data = {}
+ changes_descr = ''
+ changes_body = ''
link = self.env.abs_href.ticket(ticket.id)
summary = self.ticket['summary']
- author = None
-
- if not self.newticket and modtime: # Ticket change
- from trac.ticket.web_ui import TicketModule
- for change in TicketModule(self.env).grouped_changelog_entries(
- ticket, when=modtime):
- if not change['permanent']: # attachment with same time...
- continue
- author = change['author']
- change_data.update({
- 'author': obfuscate_email_address(author),
- 'comment': wrap(change['comment'], self.COLS, ' ', ' ',
- CRLF, self.ambiwidth)
- })
- link += '#comment:%s' % str(change.get('cnum', ''))
- for field, values in change['fields'].iteritems():
- old = values['old']
- new = values['new']
- newv = ''
- if field == 'description':
- new_descr = wrap(new, self.COLS, ' ', ' ', CRLF,
- self.ambiwidth)
- old_descr = wrap(old, self.COLS, '> ', '> ', CRLF,
- self.ambiwidth)
- old_descr = old_descr.replace(2 * CRLF, CRLF + '>' + \
- CRLF)
- cdescr = CRLF
- cdescr += 'Old description:' + 2 * CRLF + old_descr + \
- 2 * CRLF
- cdescr += 'New description:' + 2 * CRLF + new_descr + \
- CRLF
- changes_descr = cdescr
- elif field == 'summary':
- summary = "%s (was: %s)" % (new, old)
- elif field == 'cc':
- (addcc, delcc) = self.diff_cc(old, new)
- chgcc = ''
- if delcc:
- chgcc += wrap(" * cc: %s (removed)" %
- ', '.join(delcc),
- self.COLS, ' ', ' ', CRLF,
- self.ambiwidth) + CRLF
- if addcc:
- chgcc += wrap(" * cc: %s (added)" %
- ', '.join(addcc),
- self.COLS, ' ', ' ', CRLF,
- self.ambiwidth) + CRLF
- if chgcc:
- changes_body += chgcc
- self.prev_cc += self.parse_cc(old) if old else []
- else:
- if field in ['owner', 'reporter']:
- old = obfuscate_email_address(old)
- new = obfuscate_email_address(new)
- newv = new
- length = 7 + len(field)
- spacer_old, spacer_new = ' ', ' '
- if len(old + new) + length > self.COLS:
- length = 5
- if len(old) + length > self.COLS:
- spacer_old = CRLF
- if len(new) + length > self.COLS:
- spacer_new = CRLF
- chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old,
- spacer_old, spacer_new,
- new)
- chg = chg.replace(CRLF, CRLF + length * ' ')
- chg = wrap(chg, self.COLS, '', length * ' ', CRLF,
- self.ambiwidth)
- changes_body += ' %s%s' % (chg, CRLF)
- if newv:
- change_data[field] = {'oldvalue': old, 'newvalue': new}
-
- if newticket:
- author = ticket['reporter']
-
- ticket_values = ticket.values.copy()
- ticket_values['id'] = ticket.id
- ticket_values['description'] = wrap(
- ticket_values.get('description', ''), self.COLS,
- initial_indent=' ', subsequent_indent=' ', linesep=CRLF,
- ambiwidth=self.ambiwidth)
- ticket_values['new'] = self.newticket
- ticket_values['link'] = link
-
- subject = self.format_subj(summary)
- if not self.newticket:
- subject = 'Re: ' + subject
+ for change in TicketModule(self.env).grouped_changelog_entries(
+ ticket, when=modtime):
+ if not change['permanent']: # attachment with same time...
+ continue
+ author = change['author']
+ change_data.update({
+ 'author': obfuscate_email_address(author),
+ 'comment': wrap(change['comment'], self.COLS, ' ', ' ',
+ CRLF, self.ambiwidth)
+ })
+ link += '#comment:%s' % str(change.get('cnum', ''))
+ for field, values in change['fields'].iteritems():
+ old = values['old']
+ new = values['new']
+ newv = ''
+ if field == 'description':
+ new_descr = wrap(new, self.COLS, ' ', ' ', CRLF,
+ self.ambiwidth)
+ old_descr = wrap(old, self.COLS, '> ', '> ', CRLF,
+ self.ambiwidth)
+ old_descr = old_descr.replace(2 * CRLF, CRLF + '>' + \
+ CRLF)
+ cdescr = CRLF
+ cdescr += 'Old description:' + 2 * CRLF + old_descr + \
+ 2 * CRLF
+ cdescr += 'New description:' + 2 * CRLF + new_descr + \
+ CRLF
+ changes_descr = cdescr
+ elif field == 'summary':
+ summary = "%s (was: %s)" % (new, old)
+ elif field == 'cc':
+ (addcc, delcc) = self.diff_cc(old, new)
+ chgcc = ''
+ if delcc:
+ chgcc += wrap(" * cc: %s (removed)" %
+ ', '.join(delcc),
+ self.COLS, ' ', ' ', CRLF,
+ self.ambiwidth) + CRLF
+ if addcc:
+ chgcc += wrap(" * cc: %s (added)" %
+ ', '.join(addcc),
+ self.COLS, ' ', ' ', CRLF,
+ self.ambiwidth) + CRLF
+ if chgcc:
+ changes_body += chgcc
+ self.prev_cc += self.parse_cc(old) if old else []
+ else:
+ if field in ['owner', 'reporter']:
+ old = obfuscate_email_address(old)
+ new = obfuscate_email_address(new)
+ newv = new
+ length = 7 + len(field)
+ spacer_old, spacer_new = ' ', ' '
+ if len(old + new) + length > self.COLS:
+ length = 5
+ if len(old) + length > self.COLS:
+ spacer_old = CRLF
+ if len(new) + length > self.COLS:
+ spacer_new = CRLF
+ chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old,
+ spacer_old, spacer_new,
+ new)
+ chg = chg.replace(CRLF, CRLF + length * ' ')
+ chg = wrap(chg, self.COLS, '', length * ' ', CRLF,
+ self.ambiwidth)
+ changes_body += ' %s%s' % (chg, CRLF)
+ if newv:
+ change_data[field] = {'oldvalue': old, 'newvalue': new}
self.data.update({
- 'ticket_props': self.format_props(),
- 'ticket_body_hdr': self.format_hdr(),
- 'subject': subject,
- 'ticket': ticket_values,
'changes_body': changes_body,
'changes_descr': changes_descr,
'change': change_data
})
- NotifyEmail.notify(self, ticket.id, subject, author)
+ return (link, summary)
def format_props(self):
tkt = self.ticket
@@ -404,3 +451,40 @@ class TicketNotifyEmail(NotifyEmail):
def get_text_width(self, text):
return text_width(text, ambiwidth=self.ambiwidth)
+
+class TicketAttachmentNotifier(Component):
+ """Sends notification on attachment change"""
+
+ implements(IAttachmentChangeListener)
+
+ ticket_notify_attachment = BoolOption('notification',
+ 'ticket_notify_attachment',
+ 'true',
+ """Always send notifications on ticket attachment events.""")
+
+ # IAttachmentChangeListener
+
+ def attachment_added(self, attachment):
+ if self.ticket_notify_attachment:
+ self._notify_attachment(attachment, True)
+
+ def attachment_deleted(self, attachment):
+ if self.ticket_notify_attachment:
+ self._notify_attachment(attachment, False)
+
+ def attachment_reparented(self, attachment, old_parent_realm,
+ old_parent_id):
+ pass
+
+ # Implementation
+
+ def _notify_attachment(self, attachment, add):
+ author = attachment.author
+ resource = attachment.resource.parent
+ if resource.realm != 'ticket':
+ return
+ ticket = Ticket(self.env, resource.id)
+ tn = TicketNotifyEmail(self.env)
+ filename = attachment.filename
+ date = attachment.date or datetime.now(utc)
+ tn.notify_attachment(ticket, author, filename, date, add)
--
1.7.4.1+GitX