Advertisement
Guest User

trac-0.12.3 HTML notification patch

a guest
Feb 28th, 2012
446
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 15.47 KB | None | 0 0
  1. --- Trac-0.12.3/trac/ticket/web_ui.py   Mon Feb  6 21:50:02 2012
  2. +++ Trac-0.12.3-fizze/trac/ticket/web_ui.py Mon Feb 27 14:24:15 2012
  3. @@ -1194,7 +1194,7 @@
  4.  
  5.          # Notify
  6.          try:
  7. -            tn = TicketNotifyEmail(self.env)
  8. +            tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
  9.              tn.notify(ticket, newticket=True)
  10.          except Exception, e:
  11.              self.log.error("Failure sending notification on creation of "
  12. @@ -1238,7 +1238,7 @@
  13.                                       cnum=internal_cnum):
  14.              fragment = cnum and '#comment:' + cnum or ''
  15.              try:
  16. -                tn = TicketNotifyEmail(self.env)
  17. +                tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
  18.                  tn.notify(ticket, newticket=False, modtime=now)
  19.              except Exception, e:
  20.                  self.log.error("Failure sending notification on change to "
  21.  
  22.  
  23. --- Trac-0.12.3/trac/notification.py    Mon Feb  6 21:50:22 2012
  24. +++ Trac-0.12.3-fizze/trac/notification.py  Mon Feb 27 14:22:30 2012
  25. @@ -277,6 +277,7 @@
  26.          self.longaddr_re = re.compile(r'^\s*(.*)\s+<\s*(%s)\s*>\s*$' % addrfmt)
  27.          self._init_pref_encoding()
  28.          domains = self.env.config.get('notification', 'ignore_domains', '')
  29. +        self.from_email = self.env.config.get('notification', 'smtp_from')
  30.          self._ignore_domains = [x.strip() for x in domains.lower().split(',')]
  31.          # Get the email addresses of all known users
  32.          self.email_map = {}
  33. @@ -454,7 +455,7 @@
  34.          if pcc:
  35.              headers['Cc'] = ', '.join(pcc)
  36.          headers['Date'] = formatdate()
  37. -        msg = MIMEText(body, 'plain')
  38. +        msg = MIMEText(body, 'html')
  39.          # Message class computes the wrong type from MIMEText constructor,
  40.          # which does not take a Charset object as initializer. Reset the
  41.          # encoding type to force a new, valid evaluation
  42.  
  43.  
  44. --- Trac-0.12.3/trac/ticket/notification.py Mon Feb  6 21:50:04 2012
  45. +++ Trac-0.12.3-fizze/trac/ticket/notification.py   Tue Feb 28 10:06:34 2012
  46. @@ -18,13 +18,14 @@
  47.  
  48.  from genshi.template.text import NewTextTemplate
  49.  
  50. +from trac.wiki.formatter import *
  51.  from trac.core import *
  52.  from trac.config import *
  53.  from trac.notification import NotifyEmail
  54.  from trac.ticket.api import TicketSystem
  55.  from trac.util import md5
  56.  from trac.util.datefmt import to_utimestamp
  57. -from trac.util.text import obfuscate_email_address, text_width, wrap
  58. +from trac.util.text import obfuscate_email_address, text_width, wrap, CRLF
  59.  from trac.util.translation import deactivate, reactivate
  60.  
  61.  class TicketNotificationSystem(Component):
  62. @@ -73,9 +74,10 @@
  63.      from_email = 'trac+ticket@localhost'
  64.      COLS = 75
  65.  
  66. -    def __init__(self, env):
  67. +    def __init__(self, env, req):
  68.          NotifyEmail.__init__(self, env)
  69.          self.prev_cc = []
  70. +        self.req = req
  71.          ambiguous_char_width = env.config.get('notification',
  72.                                                'ambiguous_char_width',
  73.                                                'single')
  74. @@ -102,6 +104,7 @@
  75.          self.owner = ''
  76.          changes_descr = ''
  77.          change_data = {}
  78. +        BRCRLF = '<br />' + CRLF
  79.          link = self.env.abs_href.ticket(ticket.id)
  80.          summary = self.ticket['summary']
  81.          
  82. @@ -112,9 +115,9 @@
  83.                  if not change['permanent']: # attachment with same time...
  84.                      continue
  85.                  change_data.update({
  86. -                    'author': self.obfuscate_email(change['author']),
  87. -                    'comment': wrap(change['comment'], self.COLS, ' ', ' ',
  88. -                                    '\n', self.ambiwidth)
  89. +                    'author': change['author'],
  90. +                    'comment': wiki_to_html(change['comment'], env=self.env, req=self.req, absurls=True)
  91. +
  92.                      })
  93.                  link += '#comment:%s' % str(change.get('cnum', ''))
  94.                  for field, values in change['fields'].iteritems():
  95. @@ -122,17 +125,14 @@
  96.                      new = values['new']
  97.                      newv = ''
  98.                      if field == 'description':
  99. -                        new_descr = wrap(new, self.COLS, ' ', ' ', '\n',
  100. -                                         self.ambiwidth)
  101. -                        old_descr = wrap(old, self.COLS, '> ', '> ', '\n',
  102. -                                         self.ambiwidth)
  103. -                        old_descr = old_descr.replace(2 * '\n', '\n' + '>' + \
  104. -                                                      '\n')
  105. -                        cdescr = '\n'
  106. -                        cdescr += 'Old description:' + 2 * '\n' + old_descr + \
  107. -                                  2 * '\n'
  108. -                        cdescr += 'New description:' + 2 * '\n' + new_descr + \
  109. -                                  '\n'
  110. +                        new_descr = wrap(new, self.COLS, ' ', ' ', BRCRLF)
  111. +                        old_descr = wrap(old, self.COLS, '&gt;', '&gt;', BRCRLF)
  112. +                        old_descr = old_descr.replace(2*CRLF, CRLF + '&gt;' + BRCRLF)
  113. +
  114. +                        cdescr = CRLF
  115. +                        cdescr += 'Old description:' + 2*CRLF + old_descr + CRLF + BRCRLF
  116. +                        cdescr += 'New description:' + 2*CRLF + new_descr + CRLF + BRCRLF
  117. +
  118.                          changes_descr = cdescr
  119.                      elif field == 'summary':
  120.                          summary = "%s (was: %s)" % (new, old)
  121. @@ -140,15 +140,14 @@
  122.                          (addcc, delcc) = self.diff_cc(old, new)
  123.                          chgcc = ''
  124.                          if delcc:
  125. -                            chgcc += wrap(" * cc: %s (removed)" %
  126. -                                          ', '.join(delcc),
  127. -                                          self.COLS, ' ', ' ', '\n',
  128. -                                          self.ambiwidth) + '\n'
  129. +                            chgcc += '<li>' + wrap("cc: %s (removed)" % ', '.join(delcc),
  130. +                                           self.COLS, ' ', ' ', BRCRLF) + '</li>'
  131. +                            chgcc += CRLF
  132.                          if addcc:
  133. -                            chgcc += wrap(" * cc: %s (added)" %
  134. -                                          ', '.join(addcc),
  135. -                                          self.COLS, ' ', ' ', '\n',
  136. -                                          self.ambiwidth) + '\n'
  137. +                            chgcc += '<li>' + wrap("cc: %s (added)" % ', '.join(addcc),
  138. +                                           self.COLS, ' ', ' ', BRCRLF) + '</li>'
  139. +                            chgcc += CRLF
  140. +
  141.                          if chgcc:
  142.                              changes_body += chgcc
  143.                          self.prev_cc += old and self.parse_cc(old) or []
  144. @@ -162,25 +161,23 @@
  145.                          if len(old + new) + length > self.COLS:
  146.                              length = 5
  147.                              if len(old) + length > self.COLS:
  148. -                                spacer_old = '\n'
  149. +                                spacer_old = CRLF
  150.                              if len(new) + length > self.COLS:
  151. -                                spacer_new = '\n'
  152. -                        chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old,
  153. -                                                      spacer_old, spacer_new,
  154. -                                                      new)
  155. -                        chg = chg.replace('\n', '\n' + length * ' ')
  156. -                        chg = wrap(chg, self.COLS, '', length * ' ', '\n',
  157. -                                   self.ambiwidth)
  158. -                        changes_body += ' %s%s' % (chg, '\n')
  159. +                                spacer_new = CRLF
  160. +                        chg = wrap('%s &rarr; %s' % (old, new), self.COLS , '',
  161. +                                    ' ', CRLF)
  162. +                        changes_body += '<li>' + '%s:  %s%s' % (field, chg, CRLF) + '</li>'
  163. +
  164.                      if newv:
  165.                          change_data[field] = {'oldvalue': old, 'newvalue': new}
  166. +        if changes_body:
  167. +            changes_body = '<ul>' + changes_body + '</ul>'
  168.          
  169.          ticket_values = ticket.values.copy()
  170.          ticket_values['id'] = ticket.id
  171. -        ticket_values['description'] = wrap(
  172. -            ticket_values.get('description', ''), self.COLS,
  173. -            initial_indent=' ', subsequent_indent=' ', linesep='\n',
  174. -            ambiwidth=self.ambiwidth)
  175. +        # convert wiki syntax to html
  176. +        ticket_values['description'] = wiki_to_html(ticket_values.get('description', ''), env=self.env, req=self.req, absurls=True)
  177. +
  178.          ticket_values['new'] = self.newticket
  179.          ticket_values['link'] = link
  180.          
  181. @@ -200,6 +197,7 @@
  182.  
  183.      def format_props(self):
  184.          tkt = self.ticket
  185. +        BRCRLF = '<br />' + CRLF
  186.          fields = [f for f in tkt.fields
  187.                    if f['name'] not in ('summary', 'cc', 'time', 'changetime')]
  188.          width = [0, 0, 0, 0]
  189. @@ -233,8 +231,11 @@
  190.              else:
  191.                  width_r = min((self.COLS - 1) * 2 / 3, width_r)        
  192.                  width_l = self.COLS - width_r - 1
  193. -        sep = width_l * '-' + '+' + width_r * '-'
  194. -        txt = sep + '\n'
  195. +        format = ('<tr valign="top"><td align="right"><b>%s:</b></td><td align="left">%s</td>','<td align="right"><b>%s:</b></td><td align="left">%s</td></tr>'+CRLF)
  196. +        #sep = width_l * '-' + '+' + width_r * '-'
  197. +        sep = CRLF
  198. +        txt = sep + CRLF
  199. +        txt = '<table border="0" cellpadding="2" cellspacing="0">' + CRLF
  200.          cell_tmp = [u'', u'']
  201.          big = []
  202.          i = 0
  203. @@ -247,10 +248,11 @@
  204.              if fname in ['owner', 'reporter']:
  205.                  fval = self.obfuscate_email(fval)
  206.              if f['type'] == 'textarea' or '\n' in unicode(fval):
  207. -                big.append((f['label'], '\n'.join(fval.splitlines())))
  208. +                big.append((f['label'], CRLF.join(fval.splitlines())))
  209.              else:
  210.                  # Note: f['label'] is a Babel's LazyObject, make sure its
  211.                  # __str__ method won't be called.
  212. +                txt += format[i % 2] % (f['label'], unicode(fval))
  213.                  str_tmp = u'%s:  %s' % (f['label'], unicode(fval))
  214.                  idx = i % 2
  215.                  cell_tmp[idx] += wrap(str_tmp, width_lr[idx] - 2 + 2 * idx,
  216. @@ -269,12 +271,19 @@
  217.                  cell_r.append('')
  218.              fmt_width = width_l - self.get_text_width(cell_l[i]) \
  219.                          + len(cell_l[i])
  220. -            txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], '\n')
  221. +            #txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], '\n')
  222. +
  223. +        if i % 2:
  224. +            txt += '<td colspan="2">&nbsp;</td></tr>' + CRLF
  225. +            txt += CRLF
  226. +
  227.          if big:
  228.              txt += sep
  229.              for name, value in big:
  230. -                txt += '\n'.join(['', name + ':', value, '', ''])
  231. +                txt += CRLF.join(['<tr align="left"><td colspan="2"><b>' + name + ':' + '</b></td></tr>', '<tr align="left"><td colspan="2">' + value + '</td></tr>'])
  232. +                """txt += CRLF.join(['', name + ':', value, '', ''])"""
  233.          txt += sep
  234. +        txt += '</table>' + CRLF
  235.          return txt
  236.  
  237.      def parse_cc(self, txt):
  238. @@ -283,15 +292,13 @@
  239.      def diff_cc(self, old, new):
  240.          oldcc = NotifyEmail.addrsep_re.split(old)
  241.          newcc = NotifyEmail.addrsep_re.split(new)
  242. -        added = [self.obfuscate_email(x) \
  243. -                                for x in newcc if x and x not in oldcc]
  244. -        removed = [self.obfuscate_email(x) \
  245. -                                for x in oldcc if x and x not in newcc]
  246. +        added = [x for x in newcc if x and x not in oldcc]
  247. +        removed = [x for x in oldcc if x and x not in newcc]
  248.          return (added, removed)
  249.  
  250.      def format_hdr(self):
  251.          return '#%s: %s' % (self.ticket.id, wrap(self.ticket['summary'],
  252. -                                                 self.COLS, linesep='\n',
  253. +                                                 self.COLS, linesep=CRLF,
  254.                                                   ambiwidth=self.ambiwidth))
  255.  
  256.      def format_subj(self, summary):
  257. @@ -383,6 +390,7 @@
  258.          hdrs = {}
  259.          hdrs['Message-ID'] = self.get_message_id(dest, self.modtime)
  260.          hdrs['X-Trac-Ticket-ID'] = str(self.ticket.id)
  261. +        hdrs['Content-Type'] = 'text/html; charset=utf-8'
  262.          hdrs['X-Trac-Ticket-URL'] = self.data['ticket']['link']
  263.          if not self.newticket:
  264.              msgid = self.get_message_id(dest)
  265.  
  266.              
  267. --- Trac-0.12.3/trac/ticket/templates/ticket_notify_email.txt   Mon Feb  6 21:50:02 2012
  268. +++ Trac-0.12.3-fizze/trac/ticket/templates/ticket_notify_email.txt Thu Dec 29 08:58:44 2011
  269. @@ -1,32 +1,51 @@
  270. -$ticket_body_hdr
  271. -$ticket_props
  272. -{% choose ticket.new %}\
  273. -{%   when True %}\
  274. -$ticket.description
  275. -{%   end %}\
  276. -{%   otherwise %}\
  277. -{%     if changes_body %}\
  278. -${_('Changes (by %(author)s):', author=change.author)}
  279. -
  280. -$changes_body
  281. +<div>  
  282. +  <div style="font-family: Verdana, Arial, Helvetica, sans-serif; background-color:#f8f8f8">
  283. +    <hr>
  284. +    <a style="text-decoration:none;color:#069; font-size: 19px" href="${project.url or abs_href()}"><strong>$project.name</strong></a>
  285. +    <hr>
  286. +    <a style="text-decoration:none;color:#666666; font-size: 17px" href="$ticket.link">$ticket_body_hdr</a>
  287. +    <hr>
  288. +  </div>    
  289. + {% choose ticket.new %}\
  290. + {%   when True %}\
  291. +    <div style="color:#069; font-size: 15px"><em>New ticket</em> (by <strong>$ticket.reporter</strong>)</div>
  292. +    <br/>
  293. +     <div style="padding:1.5em;">$ticket.description</div>
  294. +    <br/>
  295. + {%   end %}\
  296. + {%   otherwise %}\
  297. + {%     if changes_body %}\
  298. +      <div style="color:#069; font-size: 15px"><em>Changes</em> (by <strong>$change.author</strong>)</div>
  299. +      $changes_body
  300.  {%     end %}\
  301.  {%     if changes_descr %}\
  302.  {%       if not changes_body and not change.comment and change.author %}\
  303. -${_('Description changed by %(author)s:', author=change.author)}
  304. +        <div style="color:#069; font-size: 15px"><em>Description changed by</em> <strong>$change.author</strong></div>
  305.  {%       end %}\
  306. -$changes_descr
  307. ---
  308. +{%      if changes_body or change.comment or not change.author %}\
  309. +        <div style="color:#069; font-size: 15px"><em>Description</em></div>
  310.  {%     end %}\
  311. -{%     if change.comment %}\
  312. +      <div style="padding:1.5em; font-size: 14px">$changes_descr</div>
  313. +      <br/>
  314. +{%    end %}\
  315. +{%    if change.comment %}\
  316. +      <div style="color:#069;"><em>Comment</em>  ${not changes_body and '(by <strong>%s</strong>)' % change.author or ''}</div>
  317. +      <div style="padding:1.5em;">$change.comment</div>
  318. +      <br/>
  319. +{%    end %}\
  320. +{%  end %}\
  321. +  <hr/>
  322. +{% end %}\
  323.  
  324. -${changes_body and _('Comment:') or _('Comment (by %(author)s):', author=change.author)}
  325. +<style type="text/css">
  326. +  th { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
  327. +  td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
  328. +</style>
  329. +  
  330. +    $ticket_props
  331. +    
  332. +    <hr/>
  333. +    <div style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px;background-color:#f0f0f0;color:#999">$project.descr</div>
  334. +    <cite style="display:block;padding:4px;background-color:#f0f0f0;color:#999;font-size:95%;border-top:1px dotted #ccc;">$project.descr</cite>   
  335. +</div>
  336.  
  337. -$change.comment
  338. -{%     end %}\
  339. -{%   end %}\
  340. -{% end %}\
  341. -
  342. ---
  343. -${_('Ticket URL: <%(link)s>', link=ticket.link)}
  344. -$project.name <${project.url or abs_href()}>
  345. -$project.descr
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement