Attachment 'IncludeWithAttachment20070727.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - Include macro with support for attachments
4
5 @copyright: 2006 RaphaelBossek
6 @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>
7 @copyright: 2000-2001 by Richard Jones <richard@bizarsoftware.com.au>
8 @license: GNU GPL, see COPYING for details.
9
10 2006-11-17 RaphaelBossek
11 * Release v1.0.0
12 * Added support for attachments.
13 """
14
15 #Dependencies = ["pages"] # included page
16 Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
17
18 import os, re, StringIO, codecs
19 from MoinMoin import wikiutil
20 from MoinMoin.Page import Page
21 from MoinMoin.util import web
22 from MoinMoin.action import AttachFile
23 from MoinMoinCandies import RedirectOutputRequest
24
25 _sysmsg = '<p><strong class="%s">%s</strong></p>'
26
27 ## keep in sync with TableOfContents macro!
28 _arg_heading = r'(,\s*heading=(?P<hquote>[\'"])(?P<heading>[^,]+)(?P=hquote))?'
29 _arg_level = r'(,\s*level=(?P<level>\d*))?'
30 _arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>[^,]+?)(?P=fquote))?'
31 _arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>[^,]+?)(?P=tquote))?'
32 _arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
33 _arg_items = r'(,\s*items=(?P<items>\d+))?'
34 _arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
35 _arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
36 _arg_editlink = r'(,\s*(?P<editlink>editlink))?'
37 _arg_encoding = r'(,\s*encoding=(?P<encoding>[^,]+))?'
38 _arg_parser = r'(,\s*parser=(?P<parser>[^,]+))?'
39 _args_re_pattern = r'^(?P<name>[^,]+)(' + _arg_heading + _arg_level + _arg_from + _arg_to + _arg_sort + _arg_items + _arg_skipitems + _arg_titlesonly + _arg_editlink + _arg_encoding + _arg_parser + ')?$'
40
41 _title_re = r"^(?P<heading>\s*(?P<hmarker>=+)\s.*\s(?P=hmarker))$"
42
43 def extract_titles(body, title_re):
44 titles = []
45 for title, _ in title_re.findall(body):
46 h = title.strip()
47 level = 1
48 while h[level:level+1] == '=': level = level+1
49 depth = min(5, level)
50 title_text = h[level:-level].strip()
51 titles.append((title_text, level))
52 return titles
53
54 def execute(macro, text, args_re=re.compile(_args_re_pattern), title_re=re.compile(_title_re, re.M), called_by_toc=0):
55 request = macro.request
56 _ = request.getText
57
58 # return immediately if getting links for the current page
59 if request.mode_getpagelinks:
60 return ''
61
62 # parse and check arguments
63 args = text and args_re.match(text)
64 if not text:
65 return (_sysmsg % ('error', _('Missing arguments!')))
66 elif not args:
67 return (_sysmsg % ('error', _('XXX Invalid include arguments args="%s", text="%s" _args_re_pattern="%s"!')) % (args, text, _args_re_pattern,))
68
69 # prepare including page
70 result = []
71 # Not 1.5 compatible: print_mode = request.action in ("print", "format")
72 print_mode = macro.request.form.get(u'action', [u''])[0] in ("print", "format")
73 this_page = macro.formatter.page
74 if not hasattr(this_page, '_macroInclude_pagelist'):
75 this_page._macroInclude_pagelist = {}
76
77 # Get list of pages/files to include.
78 filelist = []
79 pagelist = []
80 page_name = args.group('name')
81 if page_name.startswith('attachment:'):
82 file_name = page_name[11:]
83 filelist = [file_name]
84 else:
85 inc_name = wikiutil.AbsPageName(request, this_page.page_name, page_name)
86 if page_name.startswith("^"):
87 try:
88 inc_match = re.compile(inc_name)
89 except re.error:
90 pass # treat as plain page name
91 else:
92 # Get user filtered readable page list
93 pagelist = request.rootpage.getPageList(filter=inc_match.match)
94 else:
95 pagelist = [inc_name]
96
97 # sort and limit page list
98 pagelist.sort()
99 sort_dir = args.group('sort')
100 if sort_dir == 'descending':
101 pagelist.reverse()
102 max_items = args.group('items')
103 if max_items:
104 pagelist = pagelist[:int(max_items)]
105
106 skipitems = 0
107 if args.group("skipitems"):
108 skipitems = int(args.group("skipitems"))
109 titlesonly = args.group('titlesonly')
110 editlink = args.group('editlink')
111
112 # iterate over pages
113 inc_pages = []
114 for inc_name in pagelist:
115 if not request.user.may.read(inc_name):
116 continue
117 if this_page._macroInclude_pagelist.has_key(inc_name):
118 result.append(u'<p><strong class="error">Recursive include of "%s" forbidden</strong></p>' % (inc_name,))
119 continue
120 if skipitems:
121 skipitems -= 1
122 continue
123 fmt = macro.formatter.__class__(request, is_included=True)
124 fmt._base_depth = macro.formatter._base_depth
125 inc_page = Page(request, inc_name, formatter=fmt)
126 if not inc_page.exists():
127 continue
128 inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
129
130 inc_pages.append(inc_page)
131
132 parser = args.group(u'parser')
133
134 # Iterate over file attachemnts.
135 for inc_name in filelist:
136 pagename, attach_fname = AttachFile.absoluteName (inc_name, this_page.page_name)
137 abspagename = wikiutil.AbsPageName (macro.request, this_page.page_name, pagename)
138 attach_fsfname = AttachFile.getFilename (request, abspagename, attach_fname)
139 inc_name = abspagename + u'/' + attach_fname
140 if os.path.isfile(attach_fsfname):
141 from MoinMoin import config
142 fmt = macro.formatter.__class__(request, is_included=True)
143 fmt._base_depth = macro.formatter._base_depth
144 inc_page = Page(request, this_page.page_name + '-attachment-' + inc_name, formatter=fmt)
145 inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
146 attach_encoding = args.group('encoding')
147 if not attach_encoding:
148 attach_encoding = config.charset
149 try:
150 infile = codecs.open(attach_fsfname, 'rb', attach_encoding)
151 if parser:
152 rawcontent = "{{{#!" + parser + "\n"
153 else:
154 rawcontent = ""
155 rawcontent = rawcontent + infile.read()
156 if parser:
157 rawcontent = rawcontent + "\n}}}\n"
158 inc_page.set_body(rawcontent)
159 inc_pages.append(inc_page)
160 except UnicodeEncodeError, err:
161 pass
162 except UnicodeDecodeError, err:
163 pass
164 else:
165 #attach_url = AttachFile.getAttachUrl (abspagename, attach_fname, macro.request)
166 result.append(macro.formatter.attachment_link(inc_name, u'---'))
167
168 # Iterate over page contents.
169 for inc_page in inc_pages:
170 # check for "from" and "to" arguments (allowing partial includes)
171 body = inc_page.get_raw_body() + '\n'
172 from_pos = 0
173 to_pos = -1
174 from_re = args.group('from')
175 if from_re:
176 try:
177 from_match = re.compile(from_re, re.M).search(body)
178 except re.error, e:
179 ##result.append("*** fe=%s ***" % e)
180 from_match = re.compile(re.escape(from_re), re.M).search(body)
181 if from_match:
182 from_pos = from_match.end()
183 else:
184 result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
185 to_re = args.group('to')
186 if to_re:
187 try:
188 to_match = re.compile(to_re, re.M).search(body, from_pos)
189 except re.error:
190 to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
191 if to_match:
192 to_pos = to_match.start()
193 else:
194 result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
195
196 if titlesonly:
197 newbody = []
198 levelstack = []
199 for title, level in extract_titles(body[from_pos:to_pos], title_re):
200 if levelstack:
201 if level > levelstack[-1]:
202 result.append(macro.formatter.bullet_list(1))
203 levelstack.append(level)
204 else:
205 while levelstack and level < levelstack[-1]:
206 result.append(macro.formatter.bullet_list(0))
207 levelstack.pop()
208 if not levelstack or level != levelstack[-1]:
209 result.append(macro.formatter.bullet_list(1))
210 levelstack.append(level)
211 else:
212 result.append(macro.formatter.bullet_list(1))
213 levelstack.append(level)
214 result.append(macro.formatter.listitem(1))
215 result.append(inc_page.link_to(request, title))
216 result.append(macro.formatter.listitem(0))
217 while levelstack:
218 result.append(macro.formatter.bullet_list(0))
219 levelstack.pop()
220 continue
221
222 if from_pos or to_pos != -1:
223 inc_page.set_raw_body(body[from_pos:to_pos], modified=True)
224 ##result.append("*** f=%s t=%s ***" % (from_re, to_re))
225 ##result.append("*** f=%d t=%d ***" % (from_pos, to_pos))
226
227 if called_by_toc:
228 result.append(inc_page.get_raw_body())
229 continue
230
231 if not hasattr(request, "_Include_backto"):
232 request._Include_backto = this_page.page_name
233
234 # do headings
235 level = None
236 if args.group('heading') and args.group('hquote'):
237 heading = args.group('htext') or inc_page.split_title(request)
238 level = 1
239 if args.group('level'):
240 level = int(args.group('level'))
241 if print_mode:
242 result.append(macro.formatter.heading(1, level) +
243 macro.formatter.text(heading) +
244 macro.formatter.heading(0, level))
245 else:
246 import sha
247 from MoinMoin import config
248 # this heading id might produce duplicate ids,
249 # if the same page is included multiple times
250 # Encode stuf we feed into sha module.
251 pntt = (inc_name + heading).encode(config.charset)
252 hid = "head-" + sha.new(pntt).hexdigest()
253 request._page_headings.setdefault(pntt, 0)
254 request._page_headings[pntt] += 1
255 if request._page_headings[pntt] > 1:
256 hid += '-%d'%(request._page_headings[pntt],)
257 result.append(
258 #macro.formatter.heading(1, level, hid,
259 # icons=edit_icon.replace('<img ', '<img align="right" ')) +
260 macro.formatter.heading(1, level, id=hid) +
261 inc_page.link_to(request, heading, css_class="include-heading-link") +
262 macro.formatter.heading(0, level)
263 )
264
265 # set or increment include marker
266 this_page._macroInclude_pagelist[inc_name] = \
267 this_page._macroInclude_pagelist.get(inc_name, 0) + 1
268
269 # Output the included page
270 ror = RedirectOutputRequest(request)
271 ror.start(sent_headers=True)
272 try:
273 cid = request.makeUniqueID("Include_%s" % wikiutil.quoteWikinameURL(inc_page.page_name))
274 inc_page.send_page(ror, content_only=1, content_id=cid, omit_footnotes=True)
275 result.append(ror.getoutput())
276 except UnicodeDecodeError, e:
277 result.append(_sysmsg % ('error', _('Encoding %s error: %s' % (attach_encoding, e,))))
278
279 # decrement or remove include marker
280 if this_page._macroInclude_pagelist[inc_name] > 1:
281 this_page._macroInclude_pagelist[inc_name] = \
282 this_page._macroInclude_pagelist[inc_name] - 1
283 else:
284 del this_page._macroInclude_pagelist[inc_name]
285
286 # if no heading and not in print mode, then output a helper link
287 if editlink and not (level or print_mode):
288 result.extend([
289 macro.formatter.div(1, css_class="include-link"),
290 inc_page.link_to(request, '[%s]' % (inc_name,), css_class="include-page-link"),
291 inc_page.link_to(request, '[%s]' % (_('edit'),), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
292 macro.formatter.div(0),
293 ])
294 # XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
295
296 # return include text
297 return ''.join(result)
298
299 # vim:ts=4:sw=4:et
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.