Add the ImageMap parser, version 1.2.
[matthijs/projects/wipi.git] / plugin / parser / ImageMap.py
1 # -*- coding: iso-8859-1 -*-
2 """
3     MoinMoin - ImageMap Parser
4
5     This parser is used to create clickable image maps.
6     
7     Syntax:
8     
9     {{{#!ImageMap
10     picsrc[;width=".."][;height=".."][;alt=".."][;title=".."] 
11     link_area1;shape="rect|circle|poly";coords="..";alt=".."[;title=".."]
12     link_area2;shape="rect|circle|poly";coords="..";alt=".."[;title=".."]
13     }}}
14
15     For a detailed explanation of the syntax see:
16     http://moinmoin.wikiwikiweb.de/ParserMarket/ImageMap
17
18     Please note: Image maps are not accessible by blind people. Do also have a
19     look at the best-practise-examples for using image maps
20     on http://moinmoin.wikiwikiweb.de/ParserMarket/ImageMap.
21     
22     Example:
23
24     {{{#!ImageMap
25     picture.jpg;width="345";height="312";alt="Clickable Organizational Chart" 
26     FrontPage;shape="rect";coords="11,10,59,29";alt="Area1"
27     http://www.xyz.com/;shape="circle";coords="42,36,96";alt="Area2"
28     FrontPage/SubPage;shape="poly";coords="48,311,105,248,96,210";alt="Area3"
29     Another Site in This Wiki;shape="rect";coords="88,10,59,29";alt="Area4"
30     InterWiki:RemotePage;shape="rect";coords="181,120,59,29";alt="Area5"
31     }}}
32
33     ImageMap Parser is partly based on ImageLink Macro
34     ImageLink Macro
35     @copyright: 2001 by Jeff Kunce,
36                 2004 by Marcin Zalewski,
37                 2004-2006 by Reimar Bauer,
38                 2006 by Thomas Waldmann
39     @license: GNU GPL, see COPYING for details.
40
41     ImageMap Parser
42     @copyright: 2006,2007 by Oliver Siemoneit
43     @license: GNU GPL, see COPYING for details.
44
45     Changes:
46
47     Version 1.1
48     * Made code PEP8 compatible.
49     * Parameter checking and stripping added to prevent inserting of malicious
50       code in html page via parser call.
51
52     Version 1.2
53     * Fixed ouput abstraction violations: on other formatters than html-formatter
54       just the specified image is output via formatter.image and map information
55       dropped.
56     * In case of missing alt texts: "alt" ist set to alt="" (for the whole map)
57       and to alt="area_url" (for the different clickable areas). 
58     * Now also "title" supported to generate tooltips for the map areas.
59     * Interwiki links can also be specified in "wiki:MoinMoin/Page" syntax now.
60     
61 """
62
63 import os, random
64 from MoinMoin import wikiutil, config
65 from MoinMoin.action import AttachFile
66
67
68 def _is_URL(text):
69     return '://' in text
70
71 def _is_InterWiki(text):
72     return ':' in text
73
74 def _is_allowed_Para(para, allowed_paras):
75     found = False
76     for p in allowed_paras:
77         if para.startswith(p):
78             found = True
79     return found
80
81 def _strip_Para(para):
82     _para = wikiutil.escape(para)
83     if para.count('"') < 2:
84         return _para
85     shortend_para = _para[0:_para.find('"')+1]
86     cut_para = _para[_para.find('"')+1:len(_para)]
87     shortend_para += cut_para[0:cut_para.find('"')+1]
88     return shortend_para
89
90
91 class Parser:
92
93     def __init__(self, raw, request, **kw):
94         self.raw = raw
95         self.request = request
96
97     def format(self, formatter):
98         request = self.request
99         _ = request.getText
100         row = self.raw.split('\n')
101                
102         # Produce <img ...> html-code stuff
103         paras = row[0].split(';')
104         image = wikiutil.escape(paras[0])
105         mapname = '%s_%s' % ([image, image[:15]][(len(image) > 14)], str(random.randint(1, 999999)))
106         
107         if _is_URL(image):
108             imgurl = image
109         elif _is_InterWiki(image):
110             if image.startswith('wiki:'):
111                 image = image[5:]
112             wikitag, wikiurl, wikitail, err = wikiutil.resolve_wiki(request, image)
113             imgurl = wikiutil.join_wiki(wikiurl, wikitail)
114         else:
115             pagename, attname = AttachFile.absoluteName(image, formatter.page.page_name)
116             imgurl = AttachFile.getAttachUrl(pagename, attname, request)
117             attachment_fname = AttachFile.getFilename(request, pagename, attname)
118
119             if not os.path.exists(attachment_fname):
120                 linktext = _('Upload new attachment "%(filename)s"')
121                 output = wikiutil.link_tag(request,
122                                          ('%s?action=AttachFile&rename=%s' % (
123                                              wikiutil.quoteWikinameURL(pagename),
124                                              wikiutil.url_quote_plus(attname))),
125                                           text=linktext % {'filename': attname},
126                                           formatter=formatter)
127                 request.write(output)
128                 return
129         
130         html = '''
131 <img src="%s"''' % imgurl
132         paras.pop(0)
133
134         kw = {}
135         kw['src'] = imgurl
136         for p in paras:
137             # Prevent attacks like: pic.png;height="10" onmouseover="ExecuteBadCode()";alt="..";
138             # and: pic.png;height="10&#34; onmouseover=&#34;ExecuteBadCode()";alt="..";
139             # and: pic.png;height=&#34;10&#34; onmouseover="ExecuteBadCode()";alt="..";
140             p = _strip_Para(p)
141             if _is_allowed_Para(p, ['width="', 'height="', 'alt="', 'title="']): 
142                 html += ' %s' % p
143                 # Prepare dict for formatter.image if formatter.rawHTML call fails
144                 key, value = p.split('=', 1)
145                 kw[str(key.lower())] = value.strip('"')
146
147         # If there is no alt provided, create one
148         if not 'alt' in kw:
149             kw['alt'] = image
150             html += ' alt=""'
151
152         html += ' usemap="#%s"> ' % mapname
153         row.pop(0)
154         
155         # Produce <map ..> html-code stuff
156         html += '''
157 <map name="%s">''' % mapname
158
159         for p in row:
160             paras = p.split(';')
161             paras[0] = wikiutil.escape(paras[0])
162
163             if _is_URL(paras[0]):
164                 area_url = paras[0]
165             elif _is_InterWiki(paras[0]):
166                 if paras[0].startswith('wiki:'):
167                     paras[0] = paras[0][5:]
168                 wikitag, wikiurl, wikitail, err = wikiutil.resolve_wiki(request, paras[0])
169                 area_url = wikiutil.join_wiki(wikiurl, wikitail)
170             else:
171                 area_url = wikiutil.quoteWikinameURL(paras[0])
172             paras.pop(0)
173             
174             html += '''
175     <area href="%s"''' % area_url
176
177             for i in paras:
178                 # Prevent attacks like: FrontPage;shape="rect" onmouseover="ExecuteBadCode()";coords="..";
179                 # and: FrontPage;shape="rect&#34; onmouseover=&#34;ExecuteBadCode()";coords="..";
180                 # and: FrontPage;shape=&#34;rect&#34; onmouseover="ExecuteBadCode()";coords="..";
181                 i = _strip_Para(i) 
182                 if _is_allowed_Para(i, ['shape="', 'coords="', 'alt="', 'title="']): 
183                     html += ' %s' % i
184             # If there is no alt provided at all, set alt to area_url
185             if p.lower().find('alt="') == -1:
186                 html += ' alt="%s"' % area_url
187
188             html += '>'
189
190         html += '''
191 </map>
192 '''
193         # If current formatter is a HTML formatter, output image map with formatter.rawHTML().
194         # Otherwise just output image with formatter.image()
195         try:
196             request.write(formatter.rawHTML(html))
197         except:
198             request.write(formatter.image(**kw))