DiaViewPlugin: DiaView.py

File DiaView.py, 7.3 kB (added by arkemp, 11 months ago)

Update for #1773

Line 
1 import re
2 import sys
3 import os
4 import popen2
5 from trac.config import default_dir
6 from StringIO import StringIO
7 from trac.core import *
8 from trac.wiki.macros import WikiMacroBase
9 from trac.wiki.macros import ImageMacro
10 from trac.util.html import escape, html, Markup
11
12 class DiaViewMacro(WikiMacroBase):
13
14     def render_macro(self, req, name, content):
15         # args will be null if the macro is called without parenthesis.
16         if not content:
17             return ''
18         # parse arguments
19         # we expect the 1st argument to be a filename (filespec)
20         args = content.split(',')
21         if len(args) == 0:
22             raise Exception("No argument.")
23         filespec = args[0]
24         size_re = re.compile('[0-9]+%?$')
25         attr_re = re.compile('(align|border|width|height|alt'
26                              '|title|longdesc|class|id|usemap)=(.+)')
27         quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
28         attr = {}
29         style = {}
30         nolink = False
31         for arg in args[1:]:
32             arg = arg.strip()
33             if size_re.match(arg):
34                 # 'width' keyword
35                 attr['width'] = arg
36                 continue
37             if arg == 'nolink':
38                 nolink = True
39                 continue
40             if arg in ('left', 'right', 'top', 'bottom'):
41                 style['float'] = arg
42                 continue
43             match = attr_re.match(arg)
44             if match:
45                 key, val = match.groups()
46                 m = quoted_re.search(val) # unquote "..." and '...'
47                 if m:
48                     val = m.group(1)
49                 if key == 'align':
50                     style['float'] = val
51                 elif key == 'border':
52                     style['border'] = ' %dpx solid' % int(val);
53                 else:
54                     attr[str(key)] = val # will be used as a __call__ keyword
55
56         # parse filespec argument to get module and id if contained.
57         parts = filespec.split(':')
58         url = None
59         if len(parts) == 3:                 # module:id:attachment
60             if parts[0] in ['wiki', 'ticket']:
61                 module, id, file = parts
62             else:
63                 raise Exception("%s module can't have attachments" % parts[0])
64         elif len(parts) == 2:
65             from trac.versioncontrol.web_ui import BrowserModule
66             try:
67                 browser_links = [link for link,_ in
68                                  BrowserModule(self.env).get_link_resolvers()]
69             except Exception:
70                 browser_links = []
71             if parts[0] in browser_links:   # source:path
72                 module, file = parts
73                 rev = None
74                 if '@' in file:
75                     file, rev = file.split('@')
76                 url = req.href.browser(file, rev=rev)
77                 raw_url = req.href.browser(file, rev=rev, format='raw')
78                 desc = filespec
79             else: # #ticket:attachment or WikiPage:attachment
80                 # FIXME: do something generic about shorthand forms...
81                 id, file = parts
82                 if id and id[0] == '#':
83                     module = 'ticket'
84                     id = id[1:]
85                 elif id == 'htdocs':
86                     raw_url = url = req.href.chrome('site', file)
87                     desc = os.path.basename(file)
88                 elif id in ('http', 'https', 'ftp'): # external URLs
89                     raw_url = url = desc = id+':'+file
90                 else:
91                     module = 'wiki'
92         elif len(parts) == 1:               # attachment
93             # determine current object
94             # FIXME: should be retrieved from the formatter...
95             # ...and the formatter should be provided to the macro
96             file = filespec
97             module, id = 'wiki', 'WikiStart'
98             path_info = req.path_info.split('/',2)
99             if len(path_info) > 1:
100                 module = path_info[1]
101             if len(path_info) > 2:
102                 id = path_info[2]
103             if module not in ['wiki', 'ticket']:
104                 raise Exception('Cannot reference local attachment from here')
105         else:
106             raise Exception('No filespec given')
107         if not url: # this is an attachment
108             from trac.attachment import Attachment
109             attachment = Attachment(self.env, module, id, file)
110             url = attachment.href(req)
111
112             dia_url = attachment.href(req, format='raw')
113             dia_path = attachment.path
114             dia_filename = attachment.filename
115             img_url = dia_url.replace(".dia",".png")
116             img_path = dia_path.replace('.dia','.png')
117             img_filename = dia_filename.replace('.dia','.png')
118
119             self.env.log.info('Getting file modification times.')
120             #get file modification times
121             try:
122               dia_mtime = os.path.getmtime(dia_path)
123             except Exception:
124               raise Exception('File does not exist: %s', dia_path)
125
126             try:
127               img_mtime = os.path.getmtime(img_path)
128             except Exception:
129               img_mtime = 0
130                
131             self.env.log.info('Comparing dia and png file modification times : %s, %s',dia_mtime,img_mtime)
132
133             # if diagram is newer than image, then regenerate image
134             if (dia_mtime > img_mtime):
135               try:
136                 # TODO: read this comment and adjust the command line to taste
137                 # You should probably use the correct path
138                 # The options are correct for the 0.96.1, but you may bee to adjust them
139                 diacmd = 'dia -l --filter=png --export='+img_path+' '+dia_path
140                 self.env.log.info('Running Dia : %s',diacmd)
141                 f = popen2.Popen4(diacmd)
142                 lines = []
143                 while (f.poll() == -1):
144                   lines += f.fromchild.readlines()
145                   f.wait()     
146                 #ecode = os.spawnl(os.P_WAIT,'/usr/bin/dia','/usr/bin/dia','-l','--export-to-format=png', '--export='+img_path, dia_path)
147                 self.env.log.info('Exiting Dia')
148               except Exception, e:
149                 self.env.log.info('Dia failed with exception= %s',e)
150                 raise Exception('Dia execution failed.')
151             try:
152               attachment._fetch(img_filename)
153             except Exception:
154               db = self.env.get_db_cnx()
155               handle_ta = True
156               attachment.size = 0
157               attachment.time = 0
158               cursor = db.cursor()
159               cursor.execute("INSERT INTO attachment "
160                              "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
161                              (attachment.parent_type, attachment.parent_id, img_filename,
162                               attachment.size, attachment.time, 'PNG render of a DIA file', attachment.author,
163                               attachment.ipnr))
164               attachment.filename = img_filename
165
166               self.env.log.info('New attachment: %s', img_filename)
167
168               if handle_ta:
169                   db.commit()
170
171             desc = 'JPG render of a DIA file.'
172         for key in ['title', 'alt']:
173             if desc and not attr.has_key(key):
174                 attr[key] = desc
175         if style:
176             attr['style'] = '; '.join(['%s:%s' % (k, escape(v))
177                                        for k, v in style.iteritems()])
178         result = Markup(html.IMG(src=img_url, **attr)).sanitize()
179         #if not nolink:
180         #    result = html.A(result, href=url, style='padding:0; border:none')
181         return result