Strange Error Dell

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Tuesday, February 28, 2012

trac-0.12.3 and HTML notification

Posted on 2:04 AM by Unknown
Trac still does not do HTML mails. As I've written before, it can be hacked to send good looking HTML notifications, though. I happily used trac-0.12.2 until I discovered some issues with subversion-1.7.
So I upgraded to trac-0.12.3, the upgrade went seamlessly. All it took as a repository resync for changesets with removed files to show up again. Yay!
Of course trac still doesn't send HTML notifications, so I had to apply my previous patches from trac-0.12.2. Except for trac/ticket/notification.py this was rather easy. See my diff here or grab it from pastebin.



--- Trac-0.12.3/trac/ticket/web_ui.py    Mon Feb  6 21:50:02 2012
+++ Trac-0.12.3-fizze/trac/ticket/web_ui.py    Mon Feb 27 14:24:15 2012
@@ -1194,7 +1194,7 @@

         # Notify
         try:
-            tn = TicketNotifyEmail(self.env)
+            tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
             tn.notify(ticket, newticket=True)
         except Exception, e:
             self.log.error("Failure sending notification on creation of "
@@ -1238,7 +1238,7 @@
                                      cnum=internal_cnum):
             fragment = cnum and '#comment:' + cnum or ''
             try:
-                tn = TicketNotifyEmail(self.env)
+                tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
                 tn.notify(ticket, newticket=False, modtime=now)
             except Exception, e:
                 self.log.error("Failure sending notification on change to "


--- Trac-0.12.3/trac/notification.py    Mon Feb  6 21:50:22 2012
+++ Trac-0.12.3-fizze/trac/notification.py    Mon Feb 27 14:22:30 2012
@@ -277,6 +277,7 @@
         self.longaddr_re = re.compile(r'^\s*(.*)\s+<\s*(%s)\s*>\s*$' % addrfmt)
         self._init_pref_encoding()
         domains = self.env.config.get('notification', 'ignore_domains', '')
+        self.from_email = self.env.config.get('notification', 'smtp_from')
         self._ignore_domains = [x.strip() for x in domains.lower().split(',')]
         # Get the email addresses of all known users
         self.email_map = {}
@@ -454,7 +455,7 @@
         if pcc:
             headers['Cc'] = ', '.join(pcc)
         headers['Date'] = formatdate()
-        msg = MIMEText(body, 'plain')
+        msg = MIMEText(body, 'html')
         # Message class computes the wrong type from MIMEText constructor,
         # which does not take a Charset object as initializer. Reset the
         # encoding type to force a new, valid evaluation


--- Trac-0.12.3/trac/ticket/notification.py    Mon Feb  6 21:50:04 2012
+++ Trac-0.12.3-fizze/trac/ticket/notification.py    Tue Feb 28 10:06:34 2012
@@ -18,13 +18,14 @@

 from genshi.template.text import NewTextTemplate

+from trac.wiki.formatter import *
 from trac.core import *
 from trac.config import *
 from trac.notification import NotifyEmail
 from trac.ticket.api import TicketSystem
 from trac.util import md5
 from trac.util.datefmt import to_utimestamp
-from trac.util.text import obfuscate_email_address, text_width, wrap
+from trac.util.text import obfuscate_email_address, text_width, wrap, CRLF
 from trac.util.translation import deactivate, reactivate

 class TicketNotificationSystem(Component):
@@ -73,9 +74,10 @@
     from_email = 'trac+ticket@localhost'
     COLS = 75

-    def __init__(self, env):
+    def __init__(self, env, req):
         NotifyEmail.__init__(self, env)
         self.prev_cc = []
+        self.req = req
         ambiguous_char_width = env.config.get('notification',
                                               'ambiguous_char_width',
                                               'single')
@@ -102,6 +104,7 @@
         self.owner = ''
         changes_descr = ''
         change_data = {}
+        BRCRLF = '<br />' + CRLF
         link = self.env.abs_href.ticket(ticket.id)
         summary = self.ticket['summary']
         
@@ -112,9 +115,9 @@
                 if not change['permanent']: # attachment with same time...
                     continue
                 change_data.update({
-                    'author': self.obfuscate_email(change['author']),
-                    'comment': wrap(change['comment'], self.COLS, ' ', ' ',
-                                    '\n', self.ambiwidth)
+                    'author': change['author'],
+                    'comment': wiki_to_html(change['comment'], env=self.env, req=self.req, absurls=True)
+
                     })
                 link += '#comment:%s' % str(change.get('cnum', ''))
                 for field, values in change['fields'].iteritems():
@@ -122,17 +125,14 @@
                     new = values['new']
                     newv = ''
                     if field == 'description':
-                        new_descr = wrap(new, self.COLS, ' ', ' ', '\n',
-                                         self.ambiwidth)
-                        old_descr = wrap(old, self.COLS, '> ', '> ', '\n',
-                                         self.ambiwidth)
-                        old_descr = old_descr.replace(2 * '\n', '\n' + '>' + \
-                                                      '\n')
-                        cdescr = '\n'
-                        cdescr += 'Old description:' + 2 * '\n' + old_descr + \
-                                  2 * '\n'
-                        cdescr += 'New description:' + 2 * '\n' + new_descr + \
-                                  '\n'
+                        new_descr = wrap(new, self.COLS, ' ', ' ', BRCRLF)
+                        old_descr = wrap(old, self.COLS, '&gt;', '&gt;', BRCRLF)
+                        old_descr = old_descr.replace(2*CRLF, CRLF + '&gt;' + BRCRLF)
+
+                        cdescr = CRLF
+                        cdescr += 'Old description:' + 2*CRLF + old_descr + CRLF + BRCRLF
+                        cdescr += 'New description:' + 2*CRLF + new_descr + CRLF + BRCRLF
+
                         changes_descr = cdescr
                     elif field == 'summary':
                         summary = "%s (was: %s)" % (new, old)
@@ -140,15 +140,14 @@
                         (addcc, delcc) = self.diff_cc(old, new)
                         chgcc = ''
                         if delcc:
-                            chgcc += wrap(" * cc: %s (removed)" %
-                                          ', '.join(delcc),
-                                          self.COLS, ' ', ' ', '\n',
-                                          self.ambiwidth) + '\n'
+                            chgcc += '<li>' + wrap("cc: %s (removed)" % ', '.join(delcc),
+                                           self.COLS, ' ', ' ', BRCRLF) + '</li>'
+                            chgcc += CRLF
                         if addcc:
-                            chgcc += wrap(" * cc: %s (added)" %
-                                          ', '.join(addcc),
-                                          self.COLS, ' ', ' ', '\n',
-                                          self.ambiwidth) + '\n'
+                            chgcc += '<li>' + wrap("cc: %s (added)" % ', '.join(addcc),
+                                           self.COLS, ' ', ' ', BRCRLF) + '</li>'
+                            chgcc += CRLF
+
                         if chgcc:
                             changes_body += chgcc
                         self.prev_cc += old and self.parse_cc(old) or []
@@ -162,25 +161,23 @@
                         if len(old + new) + length > self.COLS:
                             length = 5
                             if len(old) + length > self.COLS:
-                                spacer_old = '\n'
+                                spacer_old = CRLF
                             if len(new) + length > self.COLS:
-                                spacer_new = '\n'
-                        chg = '* %s: %s%s%s=>%s%s' % (field, spacer_old, old,
-                                                      spacer_old, spacer_new,
-                                                      new)
-                        chg = chg.replace('\n', '\n' + length * ' ')
-                        chg = wrap(chg, self.COLS, '', length * ' ', '\n',
-                                   self.ambiwidth)
-                        changes_body += ' %s%s' % (chg, '\n')
+                                spacer_new = CRLF
+                        chg = wrap('%s &rarr; %s' % (old, new), self.COLS , '',
+                                    ' ', CRLF)
+                        changes_body += '<li>' + '%s:  %s%s' % (field, chg, CRLF) + '</li>'
+
                     if newv:
                         change_data[field] = {'oldvalue': old, 'newvalue': new}
+        if changes_body:
+            changes_body = '<ul>' + changes_body + '</ul>'
         
         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='\n',
-            ambiwidth=self.ambiwidth)
+        # convert wiki syntax to html
+        ticket_values['description'] = wiki_to_html(ticket_values.get('description', ''), env=self.env, req=self.req, absurls=True)
+
         ticket_values['new'] = self.newticket
         ticket_values['link'] = link
         
@@ -200,6 +197,7 @@

     def format_props(self):
         tkt = self.ticket
+        BRCRLF = '<br />' + CRLF
         fields = [f for f in tkt.fields
                   if f['name'] not in ('summary', 'cc', 'time', 'changetime')]
         width = [0, 0, 0, 0]
@@ -233,8 +231,11 @@
             else:
                 width_r = min((self.COLS - 1) * 2 / 3, width_r)         
                 width_l = self.COLS - width_r - 1
-        sep = width_l * '-' + '+' + width_r * '-'
-        txt = sep + '\n'
+        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)
+        #sep = width_l * '-' + '+' + width_r * '-'
+        sep = CRLF
+        txt = sep + CRLF
+        txt = '<table border="0" cellpadding="2" cellspacing="0">' + CRLF
         cell_tmp = [u'', u'']
         big = []
         i = 0
@@ -247,10 +248,11 @@
             if fname in ['owner', 'reporter']:
                 fval = self.obfuscate_email(fval)
             if f['type'] == 'textarea' or '\n' in unicode(fval):
-                big.append((f['label'], '\n'.join(fval.splitlines())))
+                big.append((f['label'], CRLF.join(fval.splitlines())))
             else:
                 # Note: f['label'] is a Babel's LazyObject, make sure its
                 # __str__ method won't be called.
+                txt += format[i % 2] % (f['label'], unicode(fval))
                 str_tmp = u'%s:  %s' % (f['label'], unicode(fval))
                 idx = i % 2
                 cell_tmp[idx] += wrap(str_tmp, width_lr[idx] - 2 + 2 * idx,
@@ -269,12 +271,19 @@
                 cell_r.append('')
             fmt_width = width_l - self.get_text_width(cell_l[i]) \
                         + len(cell_l[i])
-            txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], '\n')
+            #txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], '\n')
+
+        if i % 2:
+            txt += '<td colspan="2">&nbsp;</td></tr>' + CRLF
+            txt += CRLF
+
         if big:
             txt += sep
             for name, value in big:
-                txt += '\n'.join(['', name + ':', value, '', ''])
+                txt += CRLF.join(['<tr align="left"><td colspan="2"><b>' + name + ':' + '</b></td></tr>', '<tr align="left"><td colspan="2">' + value + '</td></tr>'])
+                """txt += CRLF.join(['', name + ':', value, '', ''])"""
         txt += sep
+        txt += '</table>' + CRLF
         return txt

     def parse_cc(self, txt):
@@ -283,15 +292,13 @@
     def diff_cc(self, old, new):
         oldcc = NotifyEmail.addrsep_re.split(old)
         newcc = NotifyEmail.addrsep_re.split(new)
-        added = [self.obfuscate_email(x) \
-                                for x in newcc if x and x not in oldcc]
-        removed = [self.obfuscate_email(x) \
-                                for x in oldcc if x and x not in newcc]
+        added = [x for x in newcc if x and x not in oldcc]
+        removed = [x for x in oldcc if x and x not in newcc]
         return (added, removed)

     def format_hdr(self):
         return '#%s: %s' % (self.ticket.id, wrap(self.ticket['summary'],
-                                                 self.COLS, linesep='\n',
+                                                 self.COLS, linesep=CRLF,
                                                  ambiwidth=self.ambiwidth))

     def format_subj(self, summary):
@@ -383,6 +390,7 @@
         hdrs = {}
         hdrs['Message-ID'] = self.get_message_id(dest, self.modtime)
         hdrs['X-Trac-Ticket-ID'] = str(self.ticket.id)
+        hdrs['Content-Type'] = 'text/html; charset=utf-8'
         hdrs['X-Trac-Ticket-URL'] = self.data['ticket']['link']
         if not self.newticket:
             msgid = self.get_message_id(dest)

            
--- Trac-0.12.3/trac/ticket/templates/ticket_notify_email.txt    Mon Feb  6 21:50:02 2012
+++ Trac-0.12.3-fizze/trac/ticket/templates/ticket_notify_email.txt    Thu Dec 29 08:58:44 2011
@@ -1,32 +1,51 @@
-$ticket_body_hdr
-$ticket_props
-{% choose ticket.new %}\
-{%   when True %}\
-$ticket.description
-{%   end %}\
-{%   otherwise %}\
-{%     if changes_body %}\
-${_('Changes (by %(author)s):', author=change.author)}
-
-$changes_body
+<div>  
+  <div style="font-family: Verdana, Arial, Helvetica, sans-serif; background-color:#f8f8f8">
+    <hr>
+    <a style="text-decoration:none;color:#069; font-size: 19px" href="${project.url or abs_href()}"><strong>$project.name</strong></a>
+    <hr>
+    <a style="text-decoration:none;color:#666666; font-size: 17px" href="$ticket.link">$ticket_body_hdr</a>
+    <hr>
+  </div>    
+ {% choose ticket.new %}\
+ {%   when True %}\
+    <div style="color:#069; font-size: 15px"><em>New ticket</em> (by <strong>$ticket.reporter</strong>)</div>
+    <br/>
+     <div style="padding:1.5em;">$ticket.description</div>
+    <br/>
+ {%   end %}\
+ {%   otherwise %}\
+ {%     if changes_body %}\
+      <div style="color:#069; font-size: 15px"><em>Changes</em> (by <strong>$change.author</strong>)</div>
+      $changes_body
 {%     end %}\
 {%     if changes_descr %}\
 {%       if not changes_body and not change.comment and change.author %}\
-${_('Description changed by %(author)s:', author=change.author)}
+        <div style="color:#069; font-size: 15px"><em>Description changed by</em> <strong>$change.author</strong></div>
 {%       end %}\
-$changes_descr
---
+{%      if changes_body or change.comment or not change.author %}\
+        <div style="color:#069; font-size: 15px"><em>Description</em></div>
 {%     end %}\
-{%     if change.comment %}\
+      <div style="padding:1.5em; font-size: 14px">$changes_descr</div>
+      <br/>
+{%    end %}\
+{%    if change.comment %}\
+      <div style="color:#069;"><em>Comment</em>  ${not changes_body and '(by <strong>%s</strong>)' % change.author or ''}</div>
+      <div style="padding:1.5em;">$change.comment</div>
+      <br/>
+{%    end %}\
+{%  end %}\
+  <hr/>
+{% end %}\

-${changes_body and _('Comment:') or _('Comment (by %(author)s):', author=change.author)}
+<style type="text/css">
+  th { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
+  td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
+</style>
+  
+    $ticket_props
+    
+    <hr/>
+    <div style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px;background-color:#f0f0f0;color:#999">$project.descr</div>
+    <cite style="display:block;padding:4px;background-color:#f0f0f0;color:#999;font-size:95%;border-top:1px dotted #ccc;">$project.descr</cite>    
+</div>

-$change.comment
-{%     end %}\
-{%   end %}\
-{% end %}\
-
---
-${_('Ticket URL: <%(link)s>', link=ticket.link)}
-$project.name <${project.url or abs_href()}>
-$project.descr

This proper diff patch was brought to you by TortoiseSVN's TortoiseMerge.

Read More
Posted in diff, html notifications, patch, trac | No comments

Wednesday, February 15, 2012

trac and HTML notifications

Posted on 12:23 AM by Unknown
Hacking Trac

Of course I used a modified trac 0.10 before, so there were some features I wanted to keep. The most important one are HTML notifications on ticket changes. Today, in trac 0.12.2 this feature still hasn't made it into their trunk. I find it sad really, but that's another story.

There are a few places that offer patches, but none is complete, and worst of all, none seems to work with trac 0.12.2 right away.

So, for any poor souls in the same situation, here's what I did to make it work. First of all download the sources for trac (v0.12.2 in my case), grabbed the best parts of the patches mentioned above, and set to work patching trac. Then I created an egg file, installed it and restarted apache. That's all!



Here's the diff to trac/notification.py:
278d277
<         self.from_email = self.env.config.get('notification', 'smtp_from')
312,315c311
<         #DNK I prefer to have "Author`s name" prefix in the From e-mail field instead of static label defined by 'smtp_from_name' in Trac config
<         #self.from_name = self.config['notification'].get('smtp_from_name')
<         #self.from_name =  self.data['change']['author'] + ' ' + self.config['notification'].get('smtp_from_name')
<         #DNK end
---
>         self.from_email = self.config['notification'].get('smtp_from')
464c460
<         msg = MIMEText(body, 'html')
---
>         msg = MIMEText(body, 'plain')
Here's the diff in  trac/ticket/web_ui.py:
 1197c1197
<             tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
---
>             tn = TicketNotifyEmail(self.env)
1241c1241
<                 tn = TicketNotifyEmail(self.env, req) #rlrj60:4/10/09
---
>                 tn = TicketNotifyEmail(self.env)
Here's the diff to trac/ticket/notification.py:
22c22
< from trac.wiki.formatter import *
---
>
78c78
<     def __init__(self, env, req):
---
>     def __init__(self, env):
81d80
<         self.req = req
108d106
<         BRCRLF = '<br />' + CRLF
119,120c117,119
<                     'author': change['author'],
<                     'comment': wiki_to_html(change['comment'], env=self.env, req=self.req, absurls=True)
---
>                     'author': obfuscate_email_address(change['author']),
>                     'comment': wrap(change['comment'], self.COLS, ' ', ' ',
>                                     CRLF)
128,131c127,130
<                         new_descr = wrap(new, self.COLS, ' ', ' ', BRCRLF)
<                         old_descr = wrap(old, self.COLS, '&gt;', '&gt;', BRCRLF)
<                         old_descr = old_descr.replace(2*CRLF, CRLF + '&gt;' + BRCRLF)
<
---
>                         new_descr = wrap(new, self.COLS, ' ', ' ', CRLF)
>                         old_descr = wrap(old, self.COLS, '> ', '> ', CRLF)
>                         old_descr = old_descr.replace(2 * CRLF, CRLF + '>' + \
>                                                       CRLF)
133,134c132,135
<                         cdescr += 'Old description:' + 2*CRLF + old_descr + CRLF + BRCRLF
<                         cdescr += 'New description:' + 2*CRLF + new_descr + CRLF + BRCRLF
---
>                         cdescr += 'Old description:' + 2 * CRLF + old_descr + \
>                                   2 * CRLF
>                         cdescr += 'New description:' + 2 * CRLF + new_descr + \
>                                   CRLF
142,144c143,145
<                             chgcc += '<li>' + wrap("cc: %s (removed)" % ', '.join(delcc),
<                                            self.COLS, ' ', ' ', BRCRLF) + '</li>'
<                             chgcc += CRLF
---
>                             chgcc += wrap(" * cc: %s (removed)" %
>                                           ', '.join(delcc),
>                                           self.COLS, ' ', ' ', CRLF) + CRLF
146,148c147,149
<                             chgcc += '<li>' + wrap("cc: %s (added)" % ', '.join(addcc),
<                                            self.COLS, ' ', ' ', BRCRLF) + '</li>'
<                             chgcc += CRLF
---
>                             chgcc += wrap(" * cc: %s (added)" %
>                                           ', '.join(addcc),
>                                           self.COLS, ' ', ' ', CRLF) + CRLF
152a154,156
>                         if field in ['owner', 'reporter']:
>                             old = obfuscate_email_address(old)
>                             new = obfuscate_email_address(new)
162,164c166,171
<                         chg = wrap('%s &rarr; %s' % (old, new), self.COLS , '',
<                                     ' ', CRLF)
<                         changes_body += '<li>' + '%s:  %s%s' % (field, chg, CRLF) + '</li>'
---
>                         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)
>                         changes_body += ' %s%s' % (chg, CRLF)
167,169d173
<
<         if changes_body:
<             changes_body = '<ul>' + changes_body + '</ul>'
173,174c177,179
<         # convert wiki syntax to html
<         ticket_values['description'] = wiki_to_html(ticket_values.get('description', ''), env=self.env, req=self.req, absurls=True)
---
>         ticket_values['description'] = wrap(
>             ticket_values.get('description', ''), self.COLS,
>             initial_indent=' ', subsequent_indent=' ', linesep=CRLF)
194d198
<         BRCRLF = '<br />' + CRLF
199,200c203,207
<         for f in [f['name'] for f in fields if f['type'] != 'textarea']:
<             if not tkt.values.has_key(f):
---
>         for f in fields:
>             if f['type'] == 'textarea':
>                 continue
>             fname = f['name']
>             if not fname in tkt.values:
202c209
<             fval = tkt[f] or ''
---
>             fval = tkt[fname] or ''
204a212,213
>             if fname in ['owner', 'reporter']:
>                 fval = obfuscate_email_address(fval)
206,209c215,216
<             if len(f) > width[idx]:
<                 width[idx] = len(f)
<             if len(fval) > width[idx + 1]:
<                 width[idx + 1] = len(fval)
---
>             width[idx] = max(self.get_text_width(f['label']), width[idx])
>             width[idx + 1] = max(self.get_text_width(fval), width[idx + 1])
211,214c218,232
<         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)
<         #l = (width[0] + width[1] + 5)
<         #sep = l * '-' + '+' + (self.COLS - l) * '-'
<         sep = CRLF       
---
>         width_l = width[0] + width[1] + 5
>         width_r = width[2] + width[3] + 5
>         half_cols = (self.COLS - 1) / 2
>         if width_l + width_r + 1 > self.COLS:
>             if ((width_l > half_cols and width_r > half_cols) or
>                     (width[0] > half_cols / 2 or width[2] > half_cols / 2)):
>                 width_l = half_cols
>                 width_r = half_cols
>             elif width_l > width_r:
>                 width_l = min((self.COLS - 1) * 2 / 3, width_l)
>                 width_r = self.COLS - width_l - 1
>             else:
>                 width_r = min((self.COLS - 1) * 2 / 3, width_r)        
>                 width_l = self.COLS - width_r - 1
>         sep = width_l * '-' + '+' + width_r * '-'
216c234
<         txt = '<table border="0" cellpadding="2" cellspacing="0">' + CRLF
---
>         cell_tmp = [u'', u'']
218a237
>         width_lr = [width_l, width_r]
231c250,257
<                 txt += format[i % 2] % (f['label'], unicode(fval))
---
>                 str_tmp = u'%s:  %s' % (f['label'], unicode(fval))
>                 idx = i % 2
>                 cell_tmp[idx] += wrap(str_tmp, width_lr[idx] - 2 + 2 * idx,
>                                       (width[2 * idx]
>                                        - self.get_text_width(f['label'])
>                                        + 2 * idx) * ' ',
>                                       2 * ' ', CRLF)
>                 cell_tmp[idx] += CRLF
233,235c259,268
<         if i % 2:
<             txt += '<td colspan="2">&nbsp;</td></tr>' + CRLF
<             txt += CRLF
---
>         cell_l = cell_tmp[0].splitlines()
>         cell_r = cell_tmp[1].splitlines()
>         for i in range(max(len(cell_l), len(cell_r))):
>             if i >= len(cell_l):
>                 cell_l.append(width_l * ' ')
>             elif i >= len(cell_r):
>                 cell_r.append('')
>             fmt_width = width_l - self.get_text_width(cell_l[i]) \
>                         + len(cell_l[i])
>             txt += u'%-*s|%s%s' % (fmt_width, cell_l[i], cell_r[i], CRLF)
239,240c272
<                 txt += CRLF.join(['<tr align="left"><td colspan="2"><b>' + name + ':' + '</b></td></tr>', '<tr align="left"><td colspan="2">' + value + '</td></tr>'])
<                 """txt += CRLF.join(['', name + ':', value, '', ''])"""
---
>                 txt += CRLF.join(['', name + ':', value, '', ''])
242d273
<         txt += '</table>' + CRLF       
251,252c282,285
<         added = [x for x in newcc if x and x not in oldcc]
<         removed = [x for x in oldcc if x and x not in newcc]
---
>         added = [obfuscate_email_address(x) \
>                                 for x in newcc if x and x not in oldcc]
>         removed = [obfuscate_email_address(x) \
>                                 for x in oldcc if x and x not in newcc]
348d380
<         hdrs['Content-Type'] = 'text/html; charset=utf-8'
 And finally, the diff of trac/ticket/templates/ticket_notify_email.txt
 1,19c1,11
< <div>  
<   <div style="font-family: Verdana, Arial, Helvetica, sans-serif; background-color:#f8f8f8">
<     <hr>
<     <a style="text-decoration:none;color:#069; font-size: 19px" href="${project.url or abs_href()}"><strong>$project.name</strong></a>
<     <hr>
<     <a style="text-decoration:none;color:#666666; font-size: 17px" href="$ticket.link">$ticket_body_hdr</a>
<     <hr>
<   </div>    
<  {% choose ticket.new %}\
<  {%   when True %}\
<     <div style="color:#069; font-size: 15px"><em>New ticket</em> (by <strong>$ticket.reporter</strong>)</div>
<     <br/>
<      <div style="padding:1.5em;">$ticket.description</div>
<     <br/>
<  {%   end %}\
<  {%   otherwise %}\
<  {%     if changes_body %}\
<       <div style="color:#069; font-size: 15px"><em>Changes</em> (by <strong>$change.author</strong>)</div>
<       $changes_body
---
> $ticket_body_hdr
> $ticket_props
> {% choose ticket.new %}\
> {%   when True %}\
> $ticket.description
> {%   end %}\
> {%   otherwise %}\
> {%     if changes_body %}\
> ${_('Changes (by %(author)s):', author=change.author)}
>
> $changes_body
23c15
<         <div style="color:#069; font-size: 15px"><em>Description changed by</em> <strong>$change.author</strong></div>
---
> ${_('Description changed by %(author)s:', author=change.author)}
25,26c17,18
< {%      if changes_body or change.comment or not change.author %}\
<         <div style="color:#069; font-size: 15px"><em>Description</em></div>
---
> $changes_descr
> --
28,38c20,22
<       <div style="padding:1.5em; font-size: 14px">$changes_descr</div>
<       <br/>
< {%    end %}\
< {%    if change.comment %}\
<       <div style="color:#069;"><em>Comment</em>  ${not changes_body and '(by <strong>%s</strong>)' % change.author or ''}</div>
<       <div style="padding:1.5em;">$change.comment</div>
<       <br/>
< {%    end %}\
< {%  end %}\
<   <hr/>
< {% end %}\
---
> {%     if change.comment %}\
>
> ${changes_body and _('Comment:') or _('Comment (by %(author)s):', author=change.author)}
40,50c24,27
< <style type="text/css">
<   th { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
<   td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px }
< </style>
<   
<     $ticket_props
<     
<     <hr/>
<     <div style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px;background-color:#f0f0f0;color:#999">$project.descr</div>
<     <cite style="display:block;padding:4px;background-color:#f0f0f0;color:#999;font-size:95%;border-top:1px dotted #ccc;">$project.descr</cite>    
< </div>
---
> $change.comment
> {%     end %}\
> {%   end %}\
> {% end %}\
51a29,32
> --
> ${_('Ticket URL: <%(link)s>', link=ticket.link)}
> $project.name <${project.url or abs_href()}>
> $project.descr

Obviously it's up to anyone to modify the template to their likes. I used winmerge to create those diffs.
Read More
Posted in diff, html notifications, patch, python, trac | No comments
Newer Posts Older Posts Home
Subscribe to: Comments (Atom)

Popular Posts

  • trac-0.12.3 and HTML notification
    Trac still does not do HTML mails. As I've written before , it can be hacked to send good looking HTML notifications, though. I happily...
  • CollabNet Subversion Edge 4.0 with SSPI WSGI and trac
    CollabNet released Subversion Edge 4.0 recently, as they've written here . As you know I've been using this to host our Subversi...
  • TortoiseSVN with kerberos authentication
    I'm a big fan of TortoiseSVN (TSVN, short) on Windows. Working with multiple SVN servers and different authentication methods has its p...
  • trac and HTML notifications
    Hacking Trac Of course I used a modified trac 0.10 before, so there were some features I wanted to keep. The most important one are HTML no...
  • TortoiseSVN 1.8.0 and NTLM Authentication on Windows
    Subversion 1.8.0 was released recently, and my favorite svn client was updated , too. I had hoped this update to go down smooth, just like...
  • Meme
    Grab the nearest book. Open it to page 56. Find the fifth sentence. Post the text of the sentence in your journal along with these instructi...
  • Logitech Media Server & MySQL
    Logitech, in their infinite wisdom, has decided to rename slimserver to SqueezeCenter then Squeezebox Server and finally Logitech Media S...
  • Seconal And Nembutal I Am On Seconal And Nembutal...?
    I am on Seconal and Nembutal...? - seconal and nembutal Seconal and Nembutal, I think, in the morning, afternoon and evening to help sleep b...
  • XenServer and Dell OMSA - addendum
    As I've written here it is indeed possible to install Dell OMSA on a XenServer 5.6 host without DDK. I've taken a brand new Dell R71...
  • SQL Server Update Woes
    There are various issues that can happen when trying to update SQL Server. For almost all there are workarounds, and they usually work well....

Categories

  • .net framework 1.1
  • .nk2
  • active directory
  • agent
  • angel eyes
  • apache
  • apple iphone
  • authentication
  • autocomplete
  • bandwidth
  • bash
  • BD390
  • bmw
  • bmw diy
  • c#
  • cab
  • can't add http network place fix
  • ccfl
  • CE
  • centos
  • cisco
  • citrix
  • collabnet
  • computers
  • configuration
  • corrupt user profile
  • debian
  • debug
  • dell
  • device
  • diff
  • django
  • dotnetfx
  • driver
  • e36
  • edge
  • embedded
  • FAN
  • firefly
  • fix
  • gregarius
  • hid/xenon
  • hid/xenon conversion
  • hooks
  • hot-plug
  • html notifications
  • http network place
  • import
  • intel
  • interface
  • internet
  • ios
  • iperf
  • iperf windows centos monit
  • iphone driver fix
  • ipmi
  • iscsi
  • java
  • jdk
  • KB2494113
  • kb953297
  • kerberos
  • linux
  • logitech
  • lost desktop
  • lost documents
  • lost icons
  • lost shortcuts
  • lost user files
  • microsoft
  • microsoft exchange
  • mod_auth_kerb
  • mod_auth_sspi
  • mod_authnz_sspi
  • mod_wsgi
  • monit
  • msvc
  • mtb usb driver
  • multipath
  • mysql
  • nagios
  • NAS
  • network
  • nickname cache
  • not installed
  • not successful
  • nslu2
  • ntlm
  • oem
  • oms
  • omsa
  • openmediavault
  • oracle
  • OTRS
  • outlook
  • outlook 2007
  • outlook 2010
  • patch
  • perl
  • preparing desktop
  • pygments
  • python
  • queue servicing report
  • rancid
  • regedit
  • registry
  • single sign on
  • slimserver
  • smtp
  • snmp
  • software
  • solution
  • sql
  • sql server
  • sqldeveloper
  • sqlite
  • squeeze
  • squeezebox
  • squeezecenter
  • ss4200
  • ssh
  • ssl
  • sspi
  • subversion
  • svn
  • trac
  • uclinux
  • updates
  • upgrade
  • upgrading
  • usb
  • user profile
  • visual studio 2008
  • web
  • webclient service
  • windows
  • windows 7
  • windows update
  • windows vista
  • windows xp
  • wireless
  • wizd
  • x64
  • xenserver

Blog Archive

  • ►  2013 (8)
    • ►  July (2)
    • ►  June (1)
    • ►  April (1)
    • ►  March (3)
    • ►  January (1)
  • ▼  2012 (8)
    • ►  December (2)
    • ►  September (1)
    • ►  May (1)
    • ►  March (2)
    • ▼  February (2)
      • trac-0.12.3 and HTML notification
      • trac and HTML notifications
  • ►  2011 (10)
    • ►  December (2)
    • ►  September (3)
    • ►  August (1)
    • ►  July (1)
    • ►  May (2)
    • ►  April (1)
  • ►  2010 (25)
    • ►  December (2)
    • ►  November (1)
    • ►  July (1)
    • ►  June (1)
    • ►  May (2)
    • ►  April (1)
    • ►  February (15)
    • ►  January (2)
  • ►  2009 (13)
    • ►  December (2)
    • ►  November (1)
    • ►  July (1)
    • ►  May (1)
    • ►  March (4)
    • ►  February (4)
  • ►  2008 (5)
    • ►  December (4)
    • ►  November (1)
Powered by Blogger.

About Me

Unknown
View my complete profile