| 1 | """ |
|---|
| 2 | Display image in attachment or repository onto the wiki page. |
|---|
| 3 | You can use this macro in wiki or ticket module. |
|---|
| 4 | First argument is filename (file spec). |
|---|
| 5 | Rest of optional arguments are attribute/style string of IMG element. |
|---|
| 6 | If it is digits and unit, treat as size (ex. 120, 25%) of IMG. |
|---|
| 7 | If it is 'right', 'left', 'top' or 'bottom', treat as align of IMG. |
|---|
| 8 | if it is key=value style, treat as attribute of IMG. |
|---|
| 9 | if it is key:value style, treat as style of IMG. |
|---|
| 10 | |
|---|
| 11 | Ex. |
|---|
| 12 | [[Image(photo.jpg)]] # simplest |
|---|
| 13 | [[Image(photo.jpg,120px)]] # with size |
|---|
| 14 | [[Image(photo.jpg,right)]] # aligned by keyword |
|---|
| 15 | [[Image(photo.jpg,align=right)]] # aligned by attribute |
|---|
| 16 | [[Image(photo.jpg,float:right)]] # aligned by style |
|---|
| 17 | [[Image(photo.jpg,border:solid 5px green)]] # with any style |
|---|
| 18 | |
|---|
| 19 | You can use image from other page, other ticket or other module. |
|---|
| 20 | [[Image(OtherPage/foo.bmp)]] # if current module is wiki |
|---|
| 21 | [[Image(base/sub/bar.bmp)]] # from hierarchical wiki page |
|---|
| 22 | [[Image(3/baz.bmp)]] # if in a ticket, point to #3 |
|---|
| 23 | [[Image(ticket/36/boo.jpg)]] |
|---|
| 24 | [[Image(file/images/bee.jpg)]] |
|---|
| 25 | |
|---|
| 26 | """ |
|---|
| 27 | |
|---|
| 28 | import os |
|---|
| 29 | import re |
|---|
| 30 | import string |
|---|
| 31 | |
|---|
| 32 | from trac.util import escape |
|---|
| 33 | |
|---|
| 34 | def execute(hdf, txt, env): |
|---|
| 35 | # args will be null if the macro is called without parentesis. |
|---|
| 36 | if not txt: |
|---|
| 37 | return '' |
|---|
| 38 | # parse arguments |
|---|
| 39 | # we expect 1st arguemnt is filename (filespec) |
|---|
| 40 | args = txt.split(',') |
|---|
| 41 | if len(args) == 0: |
|---|
| 42 | raise Exception("No argument.") |
|---|
| 43 | file = args[0] |
|---|
| 44 | size_re = re.compile('^[0-9]+%?$') |
|---|
| 45 | align_re = re.compile('^(?:left|right|top|bottom)$') |
|---|
| 46 | keyval_re = re.compile('^([-a-z0-9]+)([=:])(.*)') |
|---|
| 47 | quoted_re = re.compile("^(?:"|')(.*)(?:"|')$") |
|---|
| 48 | attr = {} |
|---|
| 49 | style = {} |
|---|
| 50 | for arg in args[1:]: |
|---|
| 51 | arg = arg.strip() |
|---|
| 52 | if size_re.search(arg): |
|---|
| 53 | # 'width' keyword |
|---|
| 54 | attr['width'] = arg |
|---|
| 55 | continue |
|---|
| 56 | if align_re.search(arg): |
|---|
| 57 | # 'align' keyword |
|---|
| 58 | attr['align'] = arg |
|---|
| 59 | continue |
|---|
| 60 | match = keyval_re.search(arg) |
|---|
| 61 | if match: |
|---|
| 62 | key = match.group(1) |
|---|
| 63 | sep = match.group(2) |
|---|
| 64 | val = match.group(3) |
|---|
| 65 | m = quoted_re.search(val) # unquote " character " |
|---|
| 66 | if m: |
|---|
| 67 | val = m.group(1) |
|---|
| 68 | if sep == '=': |
|---|
| 69 | attr[key] = val; |
|---|
| 70 | elif sep == ':': |
|---|
| 71 | style[key] = val |
|---|
| 72 | |
|---|
| 73 | # get current module and id from hdf |
|---|
| 74 | module = hdf.getValue('args.mode', 'wiki') |
|---|
| 75 | if module == 'wiki': |
|---|
| 76 | id = hdf.getValue('args.page', 'WikiStart') |
|---|
| 77 | elif module == 'ticket': |
|---|
| 78 | id = hdf['args.id'] # for ticket |
|---|
| 79 | else: |
|---|
| 80 | # limit of use |
|---|
| 81 | raise Exception('Cannot use this macro in %s module' % module) |
|---|
| 82 | # parse file argument to get module and id if contained. |
|---|
| 83 | # we allow 'file', 'id/file', 'module/id/file' format. |
|---|
| 84 | # id can be dir/dir/node. |
|---|
| 85 | # module can be 'wiki', 'ticket' or 'file' |
|---|
| 86 | # if for file module id (means path) can be omitted. |
|---|
| 87 | parts = file.split('/') |
|---|
| 88 | file = parts.pop() # anyway, last one is filename |
|---|
| 89 | if len(parts) == 0: |
|---|
| 90 | raise Exception("1st argument is invalid") |
|---|
| 91 | if parts[0] in ['wiki', 'ticket', 'file']: |
|---|
| 92 | # full spec |
|---|
| 93 | id = string.join(parts[1:], '/') |
|---|
| 94 | module = parts[0] |
|---|
| 95 | else: |
|---|
| 96 | # module name is not speciifed |
|---|
| 97 | id = string.join(parts, '/') |
|---|
| 98 | if module == 'file': |
|---|
| 99 | path = "%s/%s" % (id, file) |
|---|
| 100 | url = env.href.file(path) |
|---|
| 101 | raw_url = env.href.file(path, format='raw') |
|---|
| 102 | desc = file |
|---|
| 103 | else: |
|---|
| 104 | url = env.href.attachment(module, id, file) |
|---|
| 105 | raw_url = env.href.attachment(module, id, file, 'raw') |
|---|
| 106 | # get description for file from db. |
|---|
| 107 | cnx = env.get_db_cnx() |
|---|
| 108 | cursor = cnx.cursor() |
|---|
| 109 | cursor.execute("SELECT description from attachment where type='%s' and id='%s' and filename='%s'" % (module, id, file)) |
|---|
| 110 | row = cursor.fetchone() |
|---|
| 111 | if row: |
|---|
| 112 | desc = row[0] |
|---|
| 113 | else: |
|---|
| 114 | raise Exception("Attachment is missed: module=%s, id=%s, file=%s" %(module, id, file)) |
|---|
| 115 | |
|---|
| 116 | for key in ['title', 'alt']: |
|---|
| 117 | if desc and not attr.has_key(key): |
|---|
| 118 | attr[key] = desc |
|---|
| 119 | a_style = 'padding:0; border:none' # style of anchor |
|---|
| 120 | img_attr = string.join(map(lambda x: '%s="%s"' % x, attr.iteritems()), |
|---|
| 121 | ' ') |
|---|
| 122 | img_style = string.join(map(lambda x: "%s:%s" % x, style.iteritems()), |
|---|
| 123 | ' ') |
|---|
| 124 | return "<a href='%s' style='%s'><img src='%s' %s style=\"%s\" /></a>" % ( |
|---|
| 125 | url, a_style, raw_url, img_attr, img_style) |
|---|