Strange Error Dell

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

Monday, December 17, 2012

Introducing django and mysql

Posted on 6:08 AM by Unknown
I wanted to do some rapid prototyping with web applications, and decided to give python and django a go. There are various tutorials out there, but none of them seem to focus on using existing database structures and views.

So this is primarily to document my own findings. If someone else finds it useful: be my guest. ;-)



1. URL configs

These serve as the entry point to django from the web-browser's point of view. You can specify regex's and assign django views. The simplest one is probably this one:
urlpatterns = patterns('',
url(r'^$', views.index, name='index')
)
For those that are regular-expresionally challenged, this regex matches an empty url. It looks for start of line immediately followed by end of line.


2. views

These views can be simple functions that return a HttpResponse, or more sophisticated classes. The simplest one is probably this one:
def index(request):
return HttpResponse("Hello, world.")
This view always returns the text "Hello, world.". Yes, it really is that simple. There isn't even HTML code required. Most browsers will just render this text anyway.

3. manage.py syncdb

If you start out with an empty DB, you can use django to create it for you. You just need to define the data model in django. If you use multiple models with relationships, like in the excellent django tutorial here, you might wonder about foreign keys if you use MySQL. At least if InnoDB isn't your default storage engine. Fear not, one can easily tell django to use InnoDB anyway, by adding this to your settings.py file in the DATABASES section:
'OPTIONS': {
"init_command": "SET storage_engine=INNODB",

 

4. manage.py inspectdb

Everyone knows that django can create a DB schema according to a python model. This is very nice, but quite often you are presented with a pre-existing DB schema. In tha case, just run "python manage.py inspectdb". This will churn out a python model according to the DB schema. I've tried it with MySQL on MyISAM and InnoDB alike. It works better with InnoDB because it supports foreign keys. Of course it won't make a difference if your schema doesn't actually have any. ;-)




Read More
Posted in django, python, web | No comments

Oracle OMS Agent migration

Posted on 5:33 AM by Unknown
I've recently migrated a few Oracle databases to Enterprise Manager 12c. While this is worth a few posts on it's own, here's just a quick hint of how to update the IP/hostname of an agent after it's been registered at OEM.

First, verify that the agent is responding to requests, by navigating to the new IP/host with your browser:
https://my.new_server.lan:3872/emd/main/
Then log on to the OEM repository DB as sysman. The table MGMT_TARGETS holds all the juicy agent details. Just update the column EMD_URL with the new value, and everything is fine.
For instance
update mgmt_targets set emd_url=replace(emd_url,'my.server.lan','my.new_server.lan')  where target_name like '%my.server.lan%';
Verify your changes in the view MGMT$TARGET.
Finally the agent should be available in Setup - Agents at once. I then resync'd the agent from EMS, and secured the agent. All targets were up and running without issues then.

Read More
Posted in agent, oem, oms, oracle | No comments

Tuesday, September 18, 2012

trac-0.12.3 and HTML notifications and commit_updater

Posted on 7:15 AM by Unknown
As I've written before, trac-0.12.3 still doesn't HTML notifications. Maurice found out how to work around a limitation with the commit_updater plugin here.



--- trac/ticket/notification.py    Tue Sep 18 15:08:52 2012
+++ trac/ticket/notification.py.bak    Tue Feb 28 10:06:34 2012
@@ -27,9 +27,6 @@
 from trac.util.datefmt import to_utimestamp
 from trac.util.text import obfuscate_email_address, text_width, wrap, CRLF
 from trac.util.translation import deactivate, reactivate
-from trac.web.api import Request
-from StringIO import StringIO
-import trac.perm as perm

 class TicketNotificationSystem(Component):

@@ -117,22 +114,11 @@
                                                 ticket, self.db, when=modtime):
                 if not change['permanent']: # attachment with same time...
                     continue
-                if self.req is None:
-                    self.env.log.warning("Req is None - a dummy Req is made so that wiki to html works")
-                    tracurl = self.env.base_url.replace("http://","")
-                    environ = {'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_PORT': 80, 'SERVER_NAME': tracurl,
-                               'wsgi.url_scheme': 'http', 'wsgi.input': StringIO('')}
-                    self.req = Request(environ, None)
-                    self.req.perm = perm.PermissionCache(self.env)
-                    change_data.update({
-                        'author': change['author'],
-                        'comment': wiki_to_html(change['comment'], env=self.env, req=self.req, absurls=True)
-                        })
-                else:
-                    change_data.update({
-                        'author': change['author'],
-                        'comment': wiki_to_html(change['comment'], env=self.env, req=self.req, absurls=True)
-                        })
+                change_data.update({
+                    '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():
                     old = values['old']
Or download the diff file from pastebin here.
That fixes the commit_updater.py for me. Thank you Maurice!
Read More
Posted in | No comments

Tuesday, May 29, 2012

XenServer and "The VDI is not available"

Posted on 12:55 AM by Unknown
I've been happily running XenServer for a few years now. Last week I encountered a weird issue with seemingly corrupted VDIs, which turned out to be fine after a server reboot.
In the aftermath I had a corrupted storage repository, that manifested like this:
  • Unable to scan the SR:
Error code: SR_BACKEND_FAILURE_46
Error parameters: , The VDI is not available [opterr=Error scanning VDI <uuid> ]
  • Unable to copy VDIs from or to the SR
    This was nasty, because this meant it wasn't possible to backup virtual disks.
After reading comments on the XenServer forums for many hours I found a working solution. First of all check the file /var/log/SMlog on the pool master server and then trigger a rescan of the bad SR. In my case I had a VDI with an invalid footer.
The log looks like this:
***** VHD scan error: vhd=VHD-<uuid> scan-error=-22 error-message='invalid footer'
*** vhd-scan error: <uuid>
Raising exception [46, The VDI is not available [opterr=Error scanning VDI <uuid>]]
I verified this by running
/usr/sbin/vhd-util scan -f -m "VHD-*" -l VG_XenStorage-<storage-reporitory-uuid> -p
Which, amongst others, gave me this line:
vhd=VHD-<uuid> scan-error=-22 error-message='invalid footer'
 Now many people recommended running vdi-forget to get rid of this VDI. I tried that, but that didn't fix anything. Others recommended running vdi-destroy. After I ran vdi-forget I wasn't able to run vdi-destroy anymore. So I had to work it out using lvremove like this:
lvremove /dev/VG_XenStorage-<storage-repository-uuid>/VHD-<uuid>
This gave me a few warnings like "open failed: Read-only file system" but in the end it did the trick. I re-scanned the storage repository and it finally worked. A few orphaned VDIs showed up which I promptly removed. Subsequent rescans of my SR coalesced VDIs without problems.

I found these links to be helpful: CTX122001 and Thread: Issue with reclaiming disk space in xenserver 6.0.

For the record, I'm running XenServer 5.6 SP2.
Read More
Posted in xenserver | No comments

Thursday, March 15, 2012

check_email_delivery and SMTP SSL

Posted on 2:35 AM by Unknown
For all of you who want/need/have to monitor an email server's performance or delay, there is a nice plugin for nagios out there written by Jonathan Buhacoff. It's not only a single plugin either, but a suite of scripts to check various smtp- and imap-related things.


I've set up a gmail account to send emails to my own email server to check its functions and - more importantly - to log its delay. I found out that there are problems with perl and Net::SMTP::TLS in Centos 5.7. Using SMTP::SSL worked fine. Jonathan's script check_email_delivery doesn't support sending mails via SSL however, although the underlying script does.

Here's a quick patch that adds a new parameter "--smtpssl" which enables sending emails via SSL.
31d30
< my $smtp_ssl = "";
86d84
<       "smtpssl!"=>\$smtp_ssl,
190d187
< $smtp_options .= "--ssl " if defined $smtp_ssl and $smtp_ssl;
Please bear in mind that this is a quick and dirty hack, and that I take no responsibility in any unwanted or ill effects whatsoever. Use purely on your own risk!
Read More
Posted in linux, nagios, perl, smtp, ssl | No comments

Friday, March 2, 2012

Logitech Media Server & MySQL

Posted on 1:40 AM by Unknown
Logitech, in their infinite wisdom, has decided to rename slimserver to SqueezeCenter then Squeezebox Server and finally Logitech Media Server.
Because I like to run it on my NAS, I despise the embedded SQLite and always want it to use MySQL instead. This has always been well-documented in the slimdevices wiki.
When these steps are applied to Logitech Media Server, it reverts to SQLite on it's own.

After some troubleshooting i found updated instructions here. For the tl;dr folks, here's the relevant lines to server.prefs:
dbtype: MySQL
dbusername: slimserver
dbpassword: slimserver
dbsource: dbi:mysql:database=slimserver;mysql_socket=/var/lib/mysql/mysql.sock
 I can't wait to see how they will name v8 - and how they'll change the config settings once more. Sigh.
Read More
Posted in logitech, mysql, slimserver, sqlite, squeezebox, squeezecenter | No comments

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)
      • Introducing django and mysql
      • Oracle OMS Agent migration
    • ►  September (1)
      • trac-0.12.3 and HTML notifications and commit_updater
    • ►  May (1)
      • XenServer and "The VDI is not available"
    • ►  March (2)
      • check_email_delivery and SMTP SSL
      • Logitech Media Server & MySQL
    • ►  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