1
# -*- coding: iso-8859-1 -*-
3
MoinMoin - convert content in 1.6.0alpha (rev 1844: 58ebb64243cc) wiki markup to 1.6.0 style
4
by using a modified 1.6.0alpha parser as translator.
6
PLEASE NOTE: most moin users will never need to execute this code,
7
because it is just for users of 1.6.0alpha version,
8
that used modified link markup, but was never released.
9
The 1.5.x/1.6.x releases use a different link markup than 1.6.0a.
11
@copyright: 2007 MoinMoin:JohannesBerg,
12
2007-2009 MoinMoin:ThomasWaldmann
13
@license: GNU GPL, see COPYING for details.
18
from MoinMoin import i18n
19
i18n.wikiLanguages = lambda: {}
21
from MoinMoin import config, macro, wikiutil
22
from MoinMoin.action import AttachFile
23
from MoinMoin.Page import Page
24
from MoinMoin.support.python_compatibility import rsplit
27
from text_moin160a_wiki import Parser
31
def convert_wiki(request, pagename, intext, renames):
32
""" Convert content written in wiki markup """
34
if not intext.endswith('\r\n'):
37
c = Converter(request, pagename, intext, renames)
38
result = request.redirectedOutput(c.convert, request)
39
if noeol and result.endswith('\r\n'):
44
STONEAGE_IMAGELINK = False # True for ImageLink(target,image), False for ImageLink(image,target)
46
# copied from moin 1.6.0 macro/ImageLink.py (to be safe in case we remove ImageLink some day)
47
# ... and slightly modified/refactored for our needs here.
48
# hint: using parse_quoted_separated from wikiutil does NOT work here, because we do not have
49
# quoted urls when they contain a '=' char in the 1.5 data input.
50
def explore_args(args):
51
""" explore args for positional and keyword parameters """
53
args = args.split(',')
54
args = [arg.strip() for arg in args]
59
kw = {} # keyword args
60
pp = [] # positional parameters
62
kwAllowed = ('width', 'height', 'alt')
66
key, value = arg.split('=', 1)
67
key_lowerstr = str(key.lower())
68
# avoid that urls with "=" are interpreted as keyword
69
if key_lowerstr in kwAllowed:
71
kw[key_lowerstr] = value
72
elif not kw_count and '://' in arg:
73
# assuming that this is the image
78
if STONEAGE_IMAGELINK and len(pp) >= 2:
79
pp[0], pp[1] = pp[1], pp[0]
84
class Converter(Parser):
85
def __init__(self, request, pagename, raw, renames):
86
self.pagename = pagename
88
self.renames = renames
89
self.request = request
93
self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(['ImageLink', ] + macro.getNames(self.request.cfg))}
96
def return_word(self, word):
98
_emph_repl = return_word
99
_emph_ibb_repl = return_word
100
_emph_ibi_repl = return_word
101
_emph_ib_or_bi_repl = return_word
102
_u_repl = return_word
103
_strike_repl = return_word
104
_sup_repl = return_word
105
_sub_repl = return_word
106
_small_repl = return_word
107
_big_repl = return_word
108
_tt_repl = return_word
109
_tt_bt_repl = return_word
110
_remark_repl = return_word
111
_table_repl = return_word
112
_tableZ_repl = return_word
113
_rule_repl = return_word
114
_smiley_repl = return_word
115
_smileyA_repl = return_word
116
_ent_repl = return_word
117
_ent_numeric_repl = return_word
118
_ent_symbolic_repl = return_word
119
_heading_repl = return_word
120
_email_repl = return_word
121
_notword_repl = return_word
122
_indent_repl = return_word
123
_li_none_repl = return_word
124
_li_repl = return_word
125
_ol_repl = return_word
126
_dl_repl = return_word
127
_comment_repl = return_word
129
# translate pagenames using pagename translation map
131
def _replace(self, key):
132
""" replace a item_name if it is in the renames dict
133
key is either a 2-tuple ('PAGE', pagename)
134
or a 3-tuple ('FILE', pagename, filename)
136
current_page = self.pagename
137
item_type, page_name, file_name = (key + (None, ))[:3]
138
abs_page_name = wikiutil.AbsPageName(current_page, page_name)
139
if item_type == 'PAGE':
140
key = (item_type, abs_page_name)
141
new_name = self.renames.get(key)
143
# we don't have an entry in rename map - apply the same magic
144
# to the page name as 1.5 did (" " -> "_") and try again:
145
abs_magic_name = abs_page_name.replace(u' ', u'_')
146
key = (item_type, abs_magic_name)
147
new_name = self.renames.get(key)
149
# we didn't find it under the magic name either -
150
# that means we do not rename it!
152
if new_name != page_name and abs_page_name != page_name:
153
# we have to fix the (absolute) new_name to be a relative name (as it was before)
154
new_name = wikiutil.RelPageName(current_page, new_name)
155
elif item_type == 'FILE':
156
key = (item_type, abs_page_name, file_name)
157
new_name = self.renames.get(key)
159
# we don't have an entry in rename map - apply the same magic
160
# to the page name as 1.5 did (" " -> "_") and try again:
161
abs_magic_name = abs_page_name.replace(u' ', u'_')
162
key = (item_type, abs_magic_name, file_name)
163
new_name = self.renames.get(key)
165
# we didn't find it under the magic name either -
166
# that means we do not rename it!
170
def _replace_target(self, target):
171
target_and_anchor = rsplit(target, '#', 1)
172
if len(target_and_anchor) > 1:
173
target, anchor = target_and_anchor
174
target = self._replace(('PAGE', target))
175
return '%s#%s' % (target, anchor)
177
target = self._replace(('PAGE', target))
182
def _macro_repl(self, word):
183
# we use [[...]] for links now, macros will be <<...>>
187
(\((?P<macro_args>.*?)\))?
190
word = unicode(word) # XXX why is word not unicode before???
191
m = re.match(macro_rule, word, re.X|re.U)
192
macro_name = m.group('macro_name')
193
macro_args = m.group('macro_args')
194
if macro_name == 'ImageLink':
195
fixed, kw = explore_args(macro_args)
196
#print "macro_args=%r" % macro_args
197
#print "fixed=%r, kw=%r" % (fixed, kw)
198
image, target = (fixed + ['', ''])[:2]
203
if '://' not in image:
204
# if it is not a URL, it is meant as attachment
205
image = u'attachment:%s' % image
208
elif target.startswith('inline:'):
209
target = 'attachment:' + target[7:] # we don't support inline:
210
elif target.startswith('wiki:'):
211
target = target[5:] # drop wiki:
213
alt = kw.get('alt') or ''
214
width = kw.get('width')
215
if width is not None:
216
image_attrs.append(u"width=%s" % width)
217
height = kw.get('height')
218
if height is not None:
219
image_attrs.append(u"height=%s" % height)
220
image_attrs = u", ".join(image_attrs)
222
image_attrs = u'|' + image_attrs
223
if alt or image_attrs:
225
result = u'[[%s|{{%s%s%s}}]]' % (target, image, alt, image_attrs)
228
macro_args = u"(%s)" % macro_args
231
result = u"<<%s%s>>" % (macro_name, macro_args)
232
# XXX later check whether some to be renamed pagename is used as macro param
235
def _word_repl(self, word, text=None):
236
"""Handle WikiNames."""
238
if wikiutil.isStrictWikiname(word):
241
return '[[%s]]' % word
242
else: # internal use:
243
return '[[%s|%s]]' % (word, text)
245
def _wikiname_bracket_repl(self, text):
246
"""Handle special-char wikinames with link text, like:
247
["Jim O'Brian" Jim's home page] or ['Hello "world"!' a page with doublequotes]
249
word = text[1:-1] # strip brackets
251
if first_char in QUOTE_CHARS:
252
# split on closing quote
253
target, linktext = word[1:].split(first_char, 1)
255
# split on whitespace
256
target, linktext = word.split(None, 1)
258
target = self._replace(('PAGE', target))
259
linktext = linktext.strip()
260
if linktext and linktext != target:
261
return '[[%s|%s]]' % (target, linktext)
263
return '[[%s]]' % target
267
def _interwiki_repl(self, word):
268
"""Handle InterWiki links."""
269
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
273
return self.interwiki("wiki:" + word)
275
def interwiki(self, target_and_text, **kw):
276
scheme, rest = target_and_text.split(':', 1)
277
wikiname, pagename, text = wikiutil160a.split_wiki(rest)
279
#if (pagename.startswith(wikiutil.CHILD_PREFIX) or # fancy link to subpage [wiki:/SubPage text]
280
# Page(self.request, pagename).exists()): # fancy link to local page [wiki:LocalPage text]
281
# # XXX OtherWiki:FooPage markup -> checks for local FooPage -sense???
282
# pagename = wikiutil.url_unquote(pagename)
283
# pagename = self._replace_target(pagename)
284
# return '[[%s%s]]' % (pagename, text)
286
if wikiname in ('Self', self.request.cfg.interwikiname, ''): # [wiki:Self:LocalPage text] or [:LocalPage:text]
287
orig_pagename = pagename
288
pagename = wikiutil.url_unquote(pagename)
289
pagename = self._replace_target(pagename)
290
camelcase = wikiutil.isStrictWikiname(pagename)
291
if camelcase and (not text or text == orig_pagename):
292
return pagename # optimize special case
296
return '[[%s%s]]' % (pagename, text)
298
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, wikiname+':')
299
if wikitag_bad: # likely we got some /InterWiki as wikitail, we don't want that!
300
pagename = wikiutil.url_unquote(pagename)
301
pagename = self._replace_target(pagename)
304
wikitail = wikiutil.url_unquote(pagename)
307
if wikiutil.isPicture(wikitail):
308
return '{{%s:%s%s}}' % (wikitag, wikitail, text)
310
if ' ' not in wikitail and not text:
311
return '%s:%s' % (wikitag, wikitail)
315
return '[[%s:%s%s]]' % (wikitag, wikitail, text)
317
def attachment(self, target_and_text, **kw):
318
""" This gets called on attachment URLs """
320
scheme, fname, text = wikiutil160a.split_wiki(target_and_text)
322
pagename, fname = AttachFile.absoluteName(fname, self.pagename)
323
from_this_page = pagename == self.pagename
324
fname = self._replace(('FILE', pagename, fname))
325
#fname = wikiutil.url_unquote(fname)
326
#fname = self._replace(('FILE', pagename, fname))
327
pagename = self._replace(('PAGE', pagename))
331
name = "%s/%s" % (pagename, fname)
337
if scheme == 'drawing':
338
return "{{drawing:%s}}" % fn_txt
340
# check for image, and possibly return IMG tag (images are always inlined)
341
if not kw.get('pretty_url', 0) and wikiutil.isPicture(fname):
342
return "{{attachment:%s}}" % fn_txt
344
# inline the attachment
345
if scheme == 'inline':
346
return '{{attachment:%s}}' % fn_txt
348
return '[[attachment:%s]]' % fn_txt
350
def _url_repl(self, word):
351
"""Handle literal URLs including inline images."""
352
scheme = word.split(":", 1)[0]
355
return self.interwiki(word)
356
if scheme in self.attachment_schemas:
357
return '%s' % self.attachment(word)
359
if wikiutil.isPicture(word): # magic will go away in 1.6!
360
return '{{%s}}' % word # new markup for inline images
365
def _url_bracket_repl(self, word):
366
"""Handle bracketed URLs."""
367
word = word[1:-1] # strip brackets
369
# Local extended link? [:page name:link text] XXX DEPRECATED
371
words = word[1:].split(':', 1)
372
link, text = (words + ['', ''])[:2]
373
if link.strip() == text.strip():
375
link = self._replace_target(link)
378
return '[[%s%s]]' % (link, text)
380
scheme_and_rest = word.split(":", 1)
381
if len(scheme_and_rest) == 1: # no scheme
382
# Traditional split on space
383
words = word.split(None, 1)
384
if words[0].startswith('#'): # anchor link
385
link, text = (words + ['', ''])[:2]
386
if link.strip() == text.strip():
390
return '[[%s%s]]' % (link, text)
392
scheme = scheme_and_rest[0]
394
return self.interwiki(word, pretty_url=1)
395
if scheme in self.attachment_schemas:
396
m = self.attachment(word)
397
if scheme == 'attachment':
398
# with url_bracket markup, 1.6.0a parser does not embed pictures, but link!
399
return '[[%s]]' % m[2:-2]
404
words = word.split(None, 1)
409
if wikiutil.isPicture(text) and re.match(self.url_rule, text):
410
return '[[%s|{{%s}}]]' % (target, text)
413
return '[[%s]]' % target
415
return '[[%s|%s]]' % (target, text)
417
def _parser_repl(self, word):
418
self.in_pre = 'no_parser'
421
def _pre_repl(self, word):
423
if w == '{{{' and not self.in_pre:
424
self.in_pre = 'no_parser'
425
elif w == '}}}' and self.in_pre:
429
def scan(self, scan_re, line):
430
""" Scans one line - append text before match, invoke replace() with match, and add text after match. """
434
for match in scan_re.finditer(line):
435
# Add text before the match
436
if lastpos < match.start():
437
result.append(line[lastpos:match.start()])
438
# Replace match with markup
439
result.append(self.replace(match))
440
lastpos = match.end()
442
# Add remainder of the line
443
result.append(line[lastpos:])
444
return u''.join(result)
447
def replace(self, match):
448
""" Replace match using type name """
450
for _type, hit in match.groupdict().items():
451
if hit is not None and not _type in ["hmarker", ]:
452
# Get replace method and replace hit
453
replace = getattr(self, '_' + _type + '_repl')
455
result.append(replace(hit))
456
return ''.join(result)
458
# We should never get here
460
raise Exception("Can't handle match %r\n%s\n%s" % (
462
pprint.pformat(match.groupdict()),
463
pprint.pformat(match.groups()),
468
def convert(self, request):
469
""" For each line, scan through looking for magic
470
strings, outputting verbatim any intervening text.
472
self.request = request
473
# prepare regex patterns
474
rules = self.formatting_rules.replace('\n', '|')
475
if self.request.cfg.bang_meta:
476
rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
477
'word_rule': self.word_rule,
480
pre_rules = r'''(?P<pre>\}\}\})'''
481
pre_scan_re = re.compile(pre_rules, re.UNICODE)
482
scan_re = re.compile(rules, re.UNICODE)
483
eol_re = re.compile(r'\r?\n', re.UNICODE)
487
# remove last item because it's guaranteed to be empty
488
self.lines = eol_re.split(rawtext)[:-1]
489
self.in_processing_instructions = True
492
for line in self.lines:
493
# ignore processing instructions
494
if self.in_processing_instructions:
496
for pi in ("##", "#format", "#refresh", "#redirect", "#deprecated",
497
"#pragma", "#form", "#acl", "#language"):
498
if line.lower().startswith(pi):
499
self.request.write(line + '\r\n')
503
self.in_processing_instructions = False
505
continue # do not parse this line
507
self.request.write(line + '\r\n')
509
# Scan line, format and write
510
scanning_re = self.in_pre and pre_scan_re or scan_re
511
formatted_line = self.scan(scanning_re, line)
512
self.request.write(formatted_line + '\r\n')