Dateianhang 'gaga2.py'
Herunterladen 1 """
2 MoinMoin - MoinMoin Gaga Markup Parser
3
4 Gaga is the same markup as wiki except that you do not need WikiNames for
5 linking. Linking is done WHENEVER POSSIBLE, based on a dictionary of all
6 pages. Linking is done case-sensitive (blue link) and case-insensitive
7 (violet link). Self-references are suppressed.
8
9 Copyright (c) 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
10 modifications done by ThomasWaldmann@gmx.de
11 All rights reserved, see COPYING for details.
12
13 $Id: wiki.py,v 1.91.8.7 2002/12/30 22:26:30 thomaswaldmann Exp $
14
15 """
16
17 # Imports
18 import cgi, os, re, string, sys
19 from MoinMoin import config, user, wikimacro, wikiutil, util
20 from MoinMoin.Page import Page
21 from MoinMoin.i18n import _
22
23 #############################################################################
24 ### MoinMoin Gaga Markup Parser
25 #############################################################################
26
27 class Parser:
28 """
29 Object that turns Gaga markup into HTML.
30
31 All formatting commands can be parsed one line at a time, though
32 some state is carried over between lines.
33
34 Methods named like _*_repl() are responsible to handle the named regex
35 patterns defined in print_html().
36 """
37
38 # some common strings
39 attachment_schemas = ["attachment", "inline", "drawing"]
40 punct_pattern = re.escape('''"'}]|:,.)?!''')
41 url_pattern = ('http|https|ftp|nntp|news|mailto|telnet|wiki|file|' +
42 string.join(attachment_schemas, '|') +
43 (config.url_schemas and '|' + string.join(config.url_schemas, '|') or ''))
44
45 # some common rules
46 word_rule = r'(?:%(subpages)s([%(l)s%(u)s]+(_[%(l)s%(u)s]+)*))+' % {
47 'u': config.upperletters,
48 'l': config.lowerletters,
49 'subpages': config.allow_subpages and '/?' or '',
50 }
51 wikiname_rule = r'(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+' % {
52 'u': config.upperletters,
53 'l': config.lowerletters,
54 'subpages': config.allow_subpages and '/?' or '',
55 }
56 url_rule = r'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
57 'url_guard': ('(^|(?<!\w))', '')[sys.version < "2"],
58 'url': url_pattern,
59 'punct': punct_pattern,
60 }
61
62 ol_rule = r"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
63 dl_rule = r"^\s+.*?::\s"
64
65 # the big, fat, ugly one ;)
66 formatting_rules = r"""(?:(?P<emph_ibb>'''''(?=[^']+'''))
67 (?P<emph_ibi>'''''(?=[^']+''))
68 (?P<emph_ib_or_bi>'{5}(?=[^']))
69 (?P<emph>'{2,3})
70 (?P<sup>\^.*?\^)
71 (?P<uline>__)
72 (?P<tt>\{\{\{.*?\}\}\})
73 (?P<processor>(\{\{\{#!.*))
74 (?P<pre>(\{\{\{ ?|\}\}\}))
75 (?P<rule>-{4,})
76 (?P<comment>^\#\#.*$)
77 (?P<macro>\[\[(%(macronames)s)(?:\(.*?\))?\]\]))
78 (?P<li>^\s+\*)
79 (?P<ol>%(ol_rule)s)
80 (?P<dl>%(dl_rule)s)
81 (?P<tableZ>\|\| $)
82 (?P<table>(?:\|\|)+(?:<[^>]*?>)?(?=.))
83 (?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
84 (?P<interwiki>[A-Z][a-zA-Z]+\:[^\s'\"\:\<]([^\s%(punct)s]|([%(punct)s][^\s%(punct)s]))+)
85 (?P<url_bracket>\[((%(url)s)\:|#)[^\s\]]+(\s[^\]]+)?\])
86 (?P<url>%(url_rule)s)
87 (?P<email>[-\w._+]+\@[\w-]+\.[\w.-]+)
88 (?P<word>%(word_rule)s)
89 (?P<smiley>\s(%(smiley)s)\s)
90 (?P<smileyA>^(%(smiley)s)\s)
91 (?P<ent>[<>&])""" % {
92 'url': url_pattern,
93 'punct': punct_pattern,
94 'macronames': string.join(wikimacro.names, '|'),
95 'ol_rule': ol_rule,
96 'dl_rule': dl_rule,
97 'url_rule': url_rule,
98 'word_rule': word_rule,
99 'smiley': string.join(map(re.escape, wikiutil.smileys.keys()), '|')}
100
101 def __init__(self, raw, request, **kw):
102 self.raw = raw
103 self.link_all_words = kw.get('link_all_words',0)
104 self.request = request
105 self.form = request.form
106
107 self.macro = None
108
109 self.is_em = 0
110 self.is_b = 0
111 self.is_underlined = 0
112 self.lineno = 0
113 self.in_li = 0
114 self.in_dd = 0
115 self.in_pre = 0
116 self.in_table = 0
117
118 # holds the nesting level (in chars) of open lists
119 self.list_indents = []
120 self.list_types = []
121
122 # put all pagenames in the TRIE
123 # also add versions with blanks instead of underscores
124 # (maybe also add a lowercased version?)
125 from MoinMoin.util import trie
126 self.pagelist = wikiutil.getPageList(config.text_dir)
127 self.pagetrie = trie.Trie()
128 for name in self.pagelist:
129 name = str(name)
130 self.pagetrie.add(name)
131 if "_" in name:
132 namewithblanks = string.replace(name, "_", " ")
133 self.pagetrie.add(namewithblanks)
134
135 # build blacklist for words that must not become links
136 self.gagablacklist = {}
137 gbl_page = Page(config.gaga_blacklist)
138 if gbl_page.exists():
139 file = open(gbl_page._text_filename(), 'rt')
140 try:
141 while 1:
142 lines = file.readlines(32768)
143 if not lines: break
144 for line in lines:
145 words = string.split(line)
146 for word in words: self.gagablacklist[word] = ''
147 finally:
148 file.close()
149
150 def _check_p(self):
151 if not (self.formatter.in_p or self.in_pre):
152 sys.stdout.write(self.formatter.paragraph(1))
153
154
155 def _close_item(self, result):
156 if self.formatter.in_p:
157 result.append(self.formatter.paragraph(0))
158 if self.in_li:
159 self.in_li = 0
160 result.append(self.formatter.listitem(0))
161 if self.in_dd:
162 self.in_dd = 0
163 result.append(self.formatter.definition_desc(0))
164
165
166 def interwiki(self, url_and_text, **kw):
167 self._check_p()
168
169 if len(url_and_text) == 1:
170 url = url_and_text[0]
171 text = None
172 else:
173 url, text = url_and_text
174
175 url = url[5:] # remove "wiki:"
176 if text is None:
177 tag, tail = wikiutil.split_wiki(url)
178 if tag:
179 text = tail
180 else:
181 text = url
182 url = ""
183 elif config.allow_subpages and url[0] == '/':
184 # fancy link to subpage [wiki:/SubPage text]
185 return self._word_repl(url, text)
186 elif Page(url).exists():
187 # fancy link to local page [wiki:LocalPage text]
188 return self._word_repl(url, text)
189
190 import urllib
191 wikitag, wikiurl, wikitail = wikiutil.resolve_wiki(url)
192 wikiurl = wikiutil.mapURL(wikiurl)
193 href = wikiutil.join_wiki(wikiurl, wikitail)
194
195 # check for image URL, and possibly return IMG tag
196 if not kw.get('pretty_url', 0) and wikiutil.isPicture(wikitail):
197 return self.formatter.image(border=0, src=href)
198
199 # link to self?
200 if wikitag is None:
201 return self._word_repl(wikitail)
202
203 # return InterWiki hyperlink
204 prefix = config.url_prefix
205 badwiki = wikitag == "BadWikiTag"
206 text = self.highlight_text(text) # also cgi.escapes if necessary
207
208 icon = ''
209 if self.request.user.show_fancy_links:
210 icon = self.formatter.image(width=16, height=16, hspace=2,
211 border=badwiki,
212 src=prefix+"/img/moin-inter.gif",
213 alt="[%s]" % wikitag)
214 return self.formatter.url(href, icon + text,
215 title=wikitag, unescaped=1, pretty_url=kw.get('pretty_url', 0))
216
217
218 def attachment(self, url_and_text, **kw):
219 """ This gets called on attachment URLs.
220 """
221 self._check_p()
222
223 if len(url_and_text) == 1:
224 url = url_and_text[0]
225 text = None
226 else:
227 url, text = url_and_text
228
229 inline = url[0] == 'i'
230 drawing = url[0] == 'd'
231 url = string.split(url, ":", 1)[1]
232 text = text or url
233
234 pagename = self.formatter.page.page_name
235 parts = string.split(url, '/')
236 if len(parts) > 1:
237 # get attachment from other page
238 pagename = string.join(parts[:-1], '/')
239 url = parts[-1]
240
241 import urllib
242 from MoinMoin.action import AttachFile
243 fname = wikiutil.taintfilename(url)
244 if drawing:
245 drawing = fname
246 fname = fname + ".gif"
247 url = url + ".gif"
248 fpath = os.path.join(AttachFile.getAttachDir(pagename), fname)
249
250 # check whether attachment exists, possibly point to upload form
251 if not os.path.exists(fpath):
252 if drawing:
253 linktext = _('Create new drawing "%(filename)s"')
254 else:
255 linktext = _('Upload new attachment "%(filename)s"')
256 return wikiutil.link_tag(
257 '%s?action=AttachFile&rename=%s%s' % (
258 wikiutil.quoteWikiname(pagename),
259 urllib.quote_plus(fname),
260 drawing and ('&drawing=%s' % urllib.quote(drawing)) or ''),
261 linktext % {'filename': fname})
262
263 # check for image URL, and possibly return IMG tag
264 # (images are always inlined, just like for other URLs)
265 if not kw.get('pretty_url', 0) and wikiutil.isPicture(url):
266 return self.formatter.image(border=0, alt=url,
267 src=AttachFile.getAttachUrl(pagename, url, addts=1))
268
269 # try to inline the attachment (we only accept a list
270 # of known extensions)
271 base, ext = os.path.splitext(url)
272 if inline and ext in ['.py']:
273 if ext == '.py':
274 import cStringIO
275 from MoinMoin.parser import python
276
277 buff = cStringIO.StringIO()
278 colorizer = python.Parser(open(fpath, 'r').read(), self.request, out = buff)
279 colorizer.format(self.formatter)
280 return self.formatter.preformatted(1) + \
281 self.formatter.rawHTML(buff.getvalue()) + \
282 self.formatter.preformatted(0)
283
284 return self.formatter.url(
285 AttachFile.getAttachUrl(pagename, url),
286 text, pretty_url=kw.get('pretty_url', 0))
287
288 def _uline_repl(self, word):
289 """Handle underline, i.e. __underlinedword__"""
290 self._check_p()
291 self.is_underlined = not self.is_underlined
292 return self.formatter.underline(self.is_underlined)
293
294 def _emph_repl(self, word):
295 """Handle emphasis, i.e. '' and '''."""
296 self._check_p()
297 ##print "#", self.is_b, self.is_em, "#"
298 if len(word) == 3:
299 self.is_b = not self.is_b
300 if self.is_em and self.is_b: self.is_b = 2
301 return self.formatter.strong(self.is_b)
302 else:
303 self.is_em = not self.is_em
304 if self.is_em and self.is_b: self.is_em = 2
305 return self.formatter.emphasis(self.is_em)
306
307 def _emph_ibb_repl(self, word):
308 """Handle mixed emphasis, i.e. ''''' followed by '''."""
309 self._check_p()
310 self.is_b = not self.is_b
311 self.is_em = not self.is_em
312 if self.is_em and self.is_b: self.is_b = 2
313 return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
314
315 def _emph_ibi_repl(self, word):
316 """Handle mixed emphasis, i.e. ''''' followed by ''."""
317 self._check_p()
318 self.is_b = not self.is_b
319 self.is_em = not self.is_em
320 if self.is_em and self.is_b: self.is_em = 2
321 return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
322
323 def _emph_ib_or_bi_repl(self, word):
324 """Handle mixed emphasis, exactly five '''''."""
325 self._check_p()
326 ##print "*", self.is_b, self.is_em, "*"
327 b_before_em = self.is_b > self.is_em > 0
328 self.is_b = not self.is_b
329 self.is_em = not self.is_em
330 if b_before_em:
331 return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
332 else:
333 return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
334
335 def _sup_repl(self, word):
336 """Handle superscript."""
337 self._check_p()
338 return self.formatter.sup(1) + \
339 self.highlight_text(word[1:-1]) + \
340 self.formatter.sup(0)
341
342 def _rule_repl(self, word):
343 """Handle sequences of dashes."""
344 result = self._undent()
345 if len(word) <= 4:
346 result = result + self.formatter.rule()
347 else:
348 result = result + self.formatter.rule(min(len(word), 10) - 2)
349 return result
350
351 def _word_repl(self, word, text=None, forcelink=0):
352 """Handle any word."""
353 self._check_p()
354 if not text:
355 text = word.replace("_", " ")
356 thispage = self.formatter.page.page_name
357 iswikiname = forcelink # XXX or re.match("^"+self.wikiname_rule+"$", word)
358 if config.allow_subpages and word[0] == '/':
359 word = thispage + word
360 text = self.highlight_text(text)
361 if not forcelink and self.gagablacklist.has_key(word): # we don't want to link words on blacklist
362 return text
363 if 1:
364 if iswikiname:
365 if word == text:
366 return self.formatter.pagelink(word)
367 else:
368 return self.formatter.pagelink(word, text)
369 else:
370 if self.link_all_words:
371 return self.formatter.pagelink(word, text, gaga='gaganopage')
372 else:
373 return text
374
375 def _notword_repl(self, word):
376 """Handle !NotWikiNames."""
377 self._check_p()
378 return self.highlight_text(word[1:])
379
380
381 def _interwiki_repl(self, word):
382 """Handle InterWiki links."""
383 self._check_p()
384 return self.interwiki(["wiki:" + word])
385
386
387 def _url_repl(self, word):
388 """Handle literal URLs including inline images."""
389 self._check_p()
390 scheme = string.split(word, ":", 1)[0]
391
392 if scheme == "wiki": return self.interwiki([word])
393 if scheme in self.attachment_schemas:
394 return self.attachment([word])
395
396 return self.formatter.url(word, text=self.highlight_text(word))
397
398
399 def _wikiname_bracket_repl(self, word):
400 """Handle special-char wikinames."""
401 return self._word_repl(word[2:-2], None, 1)
402
403 def _wikiname_qm_repl(self, word):
404 """Handle ?wikiname."""
405 return self._word_repl(word[1:], None, 1)
406
407 def _url_bracket_repl(self, word):
408 """Handle bracketed URLs."""
409 self._check_p()
410 words = string.split(word[1:-1], None, 1)
411 if len(words) == 1: words = words * 2
412
413 if words[0][0] == '#':
414 # anchor link
415 return self.formatter.url(words[0], self.highlight_text(words[1]))
416
417 scheme = string.split(words[0], ":", 1)[0]
418 if scheme == "wiki": return self.interwiki(words, pretty_url=1)
419 if scheme in self.attachment_schemas:
420 return self.attachment(words, pretty_url=1)
421
422 icon = ("www", 11, 11)
423 if scheme == "mailto": icon = ("email", 14, 10)
424 if scheme == "news": icon = ("news", 10, 11)
425 if scheme == "telnet": icon = ("telnet", 10, 11)
426 if scheme == "ftp": icon = ("ftp", 11, 11)
427 if scheme == "file": icon = ("ftp", 11, 11)
428 #!!! use a map?
429 # http|https|ftp|nntp|news|mailto|wiki|file
430
431 text = ''
432 if self.request.user.show_fancy_links:
433 text = text + self.formatter.image(
434 src="%s/img/moin-%s.gif" % (config.url_prefix, icon[0]),
435 width=icon[1], height=icon[2], border=0, hspace=4,
436 alt="[%s]" % string.upper(icon[0])
437 )
438 text = text + self.highlight_text(words[1])
439 return self.formatter.url(words[0], text, 'external', pretty_url=1, unescaped=1)
440
441
442 def _email_repl(self, word):
443 """Handle email addresses (without a leading mailto:)."""
444 self._check_p()
445 return self.formatter.url("mailto:" + word, self.highlight_text(word))
446
447
448 def _ent_repl(self, word):
449 """Handle SGML entities."""
450 self._check_p()
451 return self.formatter.text(word)
452 #return {'&': '&',
453 # '<': '<',
454 # '>': '>'}[word]
455
456
457 def _ent_numeric_repl(self, word):
458 """Handle numeric SGML entities."""
459 self._check_p()
460 return self.formatter.rawHTML(word)
461
462
463 def _li_repl(self, match):
464 """Handle bullet lists."""
465 result = []
466 self._close_item(result)
467 self.in_li = 1
468 result.append(self.formatter.listitem(1))
469 result.append(self.formatter.paragraph(1))
470 return string.join(result, '')
471
472
473 def _ol_repl(self, match):
474 """Handle numbered lists."""
475 return self._li_repl(match)
476
477
478 def _dl_repl(self, match):
479 """Handle definition lists."""
480 result = []
481 self._close_item(result)
482 self.in_dd = 1
483 result.extend([
484 self.formatter.definition_term(1),
485 match[:-3],
486 self.formatter.definition_term(0),
487 self.formatter.definition_desc(1)
488 ])
489 return string.join(result, '')
490
491
492 def _tt_repl(self, word):
493 """Handle inline code."""
494 self._check_p()
495 return self.formatter.code(1) + \
496 self.highlight_text(word[3:-3]) + \
497 self.formatter.code(0)
498
499
500 def _tt_bt_repl(self, word):
501 """Handle backticked inline code."""
502 if len(word) == 2: return ""
503 self._check_p()
504 return self.formatter.code(1) + \
505 self.highlight_text(word[1:-1]) + \
506 self.formatter.code(0)
507
508
509 def _getTableAttrs(self, attrdef):
510 # skip "|" and initial "<"
511 while attrdef and attrdef[0] == "|":
512 attrdef = attrdef[1:]
513 if not attrdef or attrdef[0] != "<":
514 return {}, ''
515 attrdef = attrdef[1:]
516
517 # extension for special table markup
518 def table_extension(key, parser, attrs):
519 msg = ''
520 if key[0] in string.digits:
521 token = parser.get_token()
522 if token != '%':
523 wanted = '%'
524 msg = _('Expected "%(wanted)s" after "%(key)s", got "%(token)s"') % locals()
525 else:
526 try:
527 dummy = int(key)
528 except ValueError:
529 msg = _('Expected an integer "%(key)s" before "%(token)s"') % locals()
530 else:
531 attrs['width'] = '"%s"' % key
532 elif key == '-':
533 arg = parser.get_token()
534 try:
535 dummy = int(arg)
536 except ValueError:
537 msg = _('Expected an integer "%(arg)s" after "%(key)s"') % locals()
538 else:
539 attrs['colspan'] = '"%s"' % arg
540 elif key == '|':
541 arg = parser.get_token()
542 try:
543 dummy = int(arg)
544 except ValueError:
545 msg = _('Expected an integer "%(arg)s" after "%(key)s"') % locals()
546 else:
547 attrs['rowspan'] = '"%s"' % arg
548 elif key == '(':
549 attrs['align'] = '"left"'
550 elif key == ':':
551 attrs['align'] = '"center"'
552 elif key == ')':
553 attrs['align'] = '"right"'
554 elif key == '^':
555 attrs['valign'] = '"top"'
556 elif key == 'v':
557 attrs['valign'] = '"bottom"'
558 elif key == '#':
559 arg = parser.get_token()
560 try:
561 if len(arg) != 6: raise ValueError
562 dummy = string.atoi(arg, 16)
563 except ValueError:
564 msg = _('Expected a color value "%(arg)s" after "%(key)s"' % locals())
565 else:
566 attrs['bgcolor'] = '"%s"' % arg
567 else:
568 msg = None
569 return msg
570
571 # scan attributes
572 attr, msg = wikiutil.parseAttributes(attrdef, '>', table_extension)
573 if msg: msg = '<strong class="highlight">%s</strong>' % msg
574 return attr, msg
575
576 def _tableZ_repl(self, word):
577 """Handle table row end."""
578 if self.in_table:
579 return self.formatter.table_cell(0) + self.formatter.table_row(0)
580 else:
581 return word
582
583
584 def _table_repl(self, word):
585 """Handle table cell separator."""
586 if self.in_table:
587 # check for attributes
588 attrs, attrerr = self._getTableAttrs(word)
589
590 # start the table row?
591 if self.table_rowstart:
592 self.table_rowstart = 0
593 leader = self.formatter.table_row(1, attrs)
594 else:
595 leader = self.formatter.table_cell(0)
596
597 # check for adjacent cell markers
598 if string.count(word, "|") > 2:
599 if not attrs.has_key('align'):
600 attrs['align'] = '"center"'
601 if not attrs.has_key('colspan'):
602 attrs['colspan'] = '"%d"' % (string.count(word, "|")/2)
603
604 # return the complete cell markup
605 return leader + self.formatter.table_cell(1, attrs) + attrerr
606 else:
607 return word
608
609
610 def _heading_repl(self, word):
611 """Handle section headings."""
612 import sha
613
614 icons = ''
615 if self.request.user.show_topbottom:
616 bottom = self.formatter.image(width=14, height=10, hspace=2, vspace=6, align="right",
617 border=0, src=config.url_prefix+"/img/moin-bottom.gif", alt="[BOTTOM]")
618 icons = icons + self.formatter.url("#bottom", bottom, unescaped=1)
619 top = self.formatter.image(width=14, height=10, hspace=2, vspace=6, align="right",
620 border=0, src=config.url_prefix+"/img/moin-top.gif", alt="[TOP]")
621 icons = icons + self.formatter.url("#top", top, unescaped=1)
622
623 h = string.strip(word)
624 level = 1
625 while h[level:level+1] == '=': level = level+1
626 depth = min(5,level)
627 headline = string.strip(h[level:-level])
628 return \
629 self.formatter.anchordef("head-"+sha.new(headline).hexdigest()) + \
630 self.formatter.heading(depth, self.highlight_text(headline, flow=0), icons=icons)
631
632
633 def _processor_repl(self, word):
634 """Handle processed code displays."""
635 if word[:3] == '{{{': word = word[3:]
636
637 from MoinMoin.processor import processors
638 self.processor = None
639 processor_name = string.split(word[2:])[0]
640 if processor_name in processors:
641 from MoinMoin.util import pysupport
642
643 self.processor = pysupport.importName("MoinMoin.processor." +
644 processor_name, "process")
645
646 if self.processor:
647 self.in_pre = 2
648 self.colorize_lines = [word]
649 return ''
650 else:
651 self._check_p()
652 self.in_pre = 1
653 return self.formatter.preformatted(self.in_pre) + \
654 self.formatter.text(word)
655
656
657 def _pre_repl(self, word):
658 """Handle code displays."""
659 word = string.strip(word)
660 if word == '{{{' and not self.in_pre:
661 self._check_p()
662 self.in_pre = 1
663 return self.formatter.preformatted(self.in_pre)
664 elif word == '}}}' and self.in_pre:
665 self.in_pre = 0
666 return self.formatter.preformatted(self.in_pre)
667
668 return word
669
670
671 def _smiley_repl(self, word):
672 """Handle smileys."""
673 self._check_p()
674 return wikiutil.getSmiley(word, self.formatter)
675
676 _smileyA_repl = _smiley_repl
677
678
679 def _comment_repl(self, word):
680 return ''
681
682
683 def _macro_repl(self, word):
684 """Handle macros ([[macroname]])."""
685 self._check_p()
686 macro_name = word[2:-2]
687
688 # check for arguments
689 args = None
690 if string.count(macro_name, "("):
691 macro_name, args = string.split(macro_name, '(', 1)
692 args = args[:-1]
693
694 # create macro instance
695 if self.macro is None:
696 self.macro = wikimacro.Macro(self)
697
698 # call the macro
699 return self.macro.execute(macro_name, args)
700
701
702 def _indent_level(self):
703 """Return current char-wise indent level."""
704 return len(self.list_indents) and self.list_indents[-1]
705
706
707 def _indent_to(self, new_level, list_type, numtype, numstart):
708 """Close and open lists."""
709 close = []
710 open = ''
711
712 # Close open paragraphs and list items
713 if self._indent_level() != new_level:
714 self._close_item(close)
715
716 # Close lists while char-wise indent is greater than the current one
717 while self._indent_level() > new_level:
718 if self.list_types[-1] == 'ol':
719 close.append(self.formatter.number_list(0))
720 elif self.list_types[-1] == 'dl':
721 close.append(self.formatter.definition_list(0))
722 else:
723 close.append(self.formatter.bullet_list(0))
724
725 del(self.list_indents[-1])
726 del(self.list_types[-1])
727
728 # Open new list, if necessary
729 if self._indent_level() < new_level:
730 self.list_indents.append(new_level)
731 self.list_types.append(list_type)
732 if list_type == 'ol':
733 open = open + self.formatter.number_list(1, numtype, numstart)
734 elif list_type == 'dl':
735 open = open + self.formatter.definition_list(1)
736 else:
737 open = open + self.formatter.bullet_list(1)
738
739 # If list level changes, close an open table
740 if self.in_table and (open or close):
741 close[0:0] = [self.formatter.table(0)]
742 self.in_table = 0
743
744 return string.join(close, '') + open
745
746
747 def _undent(self):
748 """Close all open lists."""
749 result = []
750 self._close_item(result)
751 for type in self.list_types:
752 if type == 'ol':
753 result.append(self.formatter.number_list(0))
754 elif type == 'dl':
755 result.append(self.formatter.definition_list(0))
756 else:
757 result.append(self.formatter.bullet_list(0))
758 self.list_indents = []
759 self.list_types = []
760 return string.join(result, '')
761
762
763 def highlight_text(self, text, **kw):
764 if kw.get('flow', 1): self._check_p()
765 if not self.hilite_re: return self.formatter.text(text)
766
767 result = []
768 lastpos = 0
769 match = self.hilite_re.search(text)
770 while match and lastpos < len(text):
771 # add the match we found
772 result.append(self.formatter.text(text[lastpos:match.start()]))
773 result.append(self.formatter.highlight(1))
774 result.append(self.formatter.text(match.group(0)))
775 result.append(self.formatter.highlight(0))
776
777 # search for the next one
778 lastpos = match.end() + (match.end() == lastpos)
779 match = self.hilite_re.search(text, lastpos)
780
781 result.append(self.formatter.text(text[lastpos:]))
782 return string.join(result, '')
783
784 def highlight_scan(self, scan_re, line):
785 result = []
786 lastpos = 0
787 match = scan_re.search(line)
788 while match and lastpos < len(line):
789 # add the match we found
790 result.append(self.highlight_text(line[lastpos:match.start()]))
791 result.append(self.replace(match))
792
793 # search for the next one
794 lastpos = match.end() + (match.end() == lastpos)
795 match = scan_re.search(line, lastpos)
796
797 result.append(self.highlight_text(line[lastpos:]))
798 return string.join(result, '')
799
800
801 def replace(self, match):
802 #hit = filter(lambda g: g[1], match.groupdict().items())
803 for type, hit in match.groupdict().items():
804 if hit is not None and type != "hmarker":
805 ##print "###", cgi.escape(`type`), cgi.escape(`hit`), "###"
806 if self.in_pre and type not in ['pre', 'ent']:
807 return self.highlight_text(hit)
808 else:
809 return getattr(self, '_' + type + '_repl')(hit)
810 else:
811 import pprint
812 raise Exception("Can't handle match " + `match`
813 + "\n" + pprint.pformat(match.groupdict())
814 + "\n" + pprint.pformat(match.groups()) )
815
816 return ""
817
818
819 def format(self, formatter):
820 """ For each line, scan through looking for magic
821 strings, outputting verbatim any intervening text.
822 """
823 self.formatter = formatter
824 self.hilite_re = self.formatter.page.hilite_re
825 prepend_rules = ""
826 append_rules = ""
827
828 # prepare regex patterns
829 if config.allow_extended_names:
830 append_rules += r'|(?P<wikiname_bracket>\[".*?"\])'
831 if config.qm_meta:
832 append_rules += r'|(?P<wikiname_qm>\?%(word_rule)s)' % {
833 'word_rule': self.word_rule,
834 }
835 if config.bang_meta:
836 prepend_rules += r'(?P<notword>!%(word_rule)s)|' % {
837 'word_rule': self.word_rule,
838 }
839 if config.backtick_meta:
840 append_rules += r'|(?P<tt_bt>`.*?`)'
841 if config.allow_numeric_entities:
842 prepend_rules += r'(?P<ent_numeric>&#\d{1,5};)|'
843
844 rules = prepend_rules + string.replace(self.formatting_rules, '\n', '|') + append_rules
845 scan_re = re.compile(rules)
846 number_re = re.compile(self.ol_rule)
847 term_re = re.compile(self.dl_rule)
848 indent_re = re.compile("^\s*")
849 eol_re = re.compile(r'\r?\n')
850
851 # get text and replace TABs
852 rawtext = string.expandtabs(self.raw)
853
854 # go through the lines
855 self.lineno = 0
856 self.lines = eol_re.split(rawtext)
857 for line in self.lines:
858 self.lineno = self.lineno + 1
859 self.table_rowstart = 1
860
861 if self.in_pre:
862 if self.in_pre == 2:
863 # processing mode
864 endpos = string.find(line, "}}}")
865 if endpos == -1:
866 self.colorize_lines.append(line)
867 continue
868
869 self.processor(self.request, self.formatter, self.colorize_lines)
870 del self.colorize_lines
871 self.in_pre = 0
872
873 # send rest of line through regex machinery
874 line = line[endpos+3:]
875 elif string.strip(line)[:2] == "#!" and string.find(line, 'python') > 0:
876 from MoinMoin.processor.Colorize import process
877 self.processor = process
878 self.in_pre = 2
879 self.colorize_lines = [line]
880 continue
881 else:
882 # paragraph break on empty lines
883 if not string.strip(line):
884 if self.formatter.in_p:
885 sys.stdout.write(self.formatter.paragraph(0))
886 if self.in_table:
887 sys.stdout.write(self.formatter.table(0))
888 self.in_table = 0
889 continue
890
891 # check indent level
892 indent = indent_re.match(line)
893 indlen = len(indent.group(0))
894 indtype = "ul"
895 numtype = None
896 numstart = None
897 if indlen:
898 match = number_re.match(line)
899 if match:
900 numtype, numstart = string.split(string.strip(match.group(0)), '.')
901 numtype = numtype[0]
902
903 if numstart and numstart[0] == "#":
904 numstart = int(numstart[1:])
905 else:
906 numstart = None
907
908 indtype = "ol"
909 else:
910 match = term_re.match(line)
911 if match:
912 indtype = "dl"
913
914 # output proper indentation tags
915 print self._indent_to(indlen, indtype, numtype, numstart)
916
917 # start or end table mode
918 if not self.in_table and line[indlen:indlen+2] == "||" and line[-2:] == "||":
919 attrs, attrerr = self._getTableAttrs(line[indlen+2:])
920 sys.stdout.write(self.formatter.table(1, attrs) + attrerr)
921 self.in_table = self.lineno
922 elif self.in_table and not(line[indlen:indlen+2] == "||" and line[-2:] == "||"):
923 sys.stdout.write(self.formatter.table(0))
924 self.in_table = 0
925
926 # convert line from wiki markup to HTML and print it
927 if self.hilite_re:
928 sys.stdout.write(self.highlight_scan(scan_re, line + " "))
929 else:
930 # gaga2 preprocessing:
931 pos = 0
932 t = self.pagetrie
933 linelength = len(line)
934 while pos < linelength:
935 matchlength = t.has(line[pos:])
936 if matchlength:
937 # ensure that we are not in the mid of a bigger "word"
938 startok = pos == 0 or line[pos-1] in r' .,!:('
939 endok = pos+matchlength >= linelength or line[pos+matchlength] in r' .,!:)'
940 if startok and endok and "Hacker" != line[pos:pos+matchlength]: # XXX "Hacker" -> self.formatter.page.page_name
941 # we found a reference to an existing page! so we temporarily add freelink markup if it is not on the blacklist
942 namewithunderscores = line[pos:pos+matchlength].replace(" ","_")
943 if not self.gagablacklist.has_key(namewithunderscores):
944 line = '%s["%s"]%s' % (line[:pos], namewithunderscores, line [pos+matchlength:])
945 linelength += 4
946 pos += matchlength + 4
947 else:
948 pos += 1
949 else:
950 pos += 1
951 else:
952 pos += 1
953
954 # this does most of the work!
955 line, count = re.subn(scan_re, self.replace, line + " ")
956 ##if not count: self._check_p()
957 self._check_p()
958 sys.stdout.write(line)
959
960 if self.in_pre:
961 sys.stdout.write(self.formatter.linebreak())
962 #if self.in_li:
963 # self.in_li = 0
964 # sys.stdout.write(self.formatter.listitem(0))
965
966 # close code displays, paragraphs, tables and open lists
967 if self.in_pre: sys.stdout.write(self.formatter.preformatted(0))
968 if self.formatter.in_p: sys.stdout.write(self.formatter.paragraph(0))
969 if self.in_table: sys.stdout.write(self.formatter.table(0))
970 sys.stdout.write(self._undent())
Gespeicherte Dateianhänge
Um Dateianhänge in eine Seite einzufügen sollte unbedingt eine Angabe wie attachment:dateiname benutzt werden, wie sie auch in der folgenden Liste der Dateien erscheint. Es sollte niemals die URL des Verweises ("laden") kopiert werden, da sich diese jederzeit ändern kann und damit der Verweis auf die Datei brechen würde.Sie dürfen keine Anhänge an diese Seite anhängen!