moin2doku/text_dokuwiki.py
Jens Hofschröer 9b3341f836
Gtwiki (#1)
* Added Windows hints and v1.2 infos
* added some windows batch files
* switched from 'cliopts' to 'cli'
* "named args"
* All arguments passed to php are UTF-8 encoded
* removed local path from repository. Use doku.local.php
* added/changed support for
  - small
  - big
  - anchordef
  - definition_list
  - comment
  - include and search macros
* added #pragma directives as comments to dokuwiki
* recreate "_dokuwiki.changes"
* Support for non-ascii characters in media files
2019-03-26 12:47:14 +01:00

537 lines
17 KiB
Python
Executable file

# -*- coding: utf-8 -*-
# Setup VIM: ex: noet ts=2 sw=2 :
"""
MoinMoin - Dokuwiki Formatter
@copyright: 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
@copyright: 2011-2012 Elan Ruusamäe <glen@delfi.ee>
@license: GNU GPL, see COPYING for details.
"""
from xml.sax import saxutils
from MoinMoin.formatter import FormatterBase
from MoinMoin import config
from MoinMoin.Page import Page
from types import *
from MoinMoin import log
logging = log.getLogger(__name__)
# TODO: let base class MoinMoin/formatter/base.py handle not implemented methods
class Formatter(FormatterBase):
"""
Send Dokuwiki formatted data.
"""
hardspace = '&nbsp;'
# hardspace = ' '
def __init__(self, request, **kw):
apply(FormatterBase.__init__, (self, request), kw)
self._current_depth = 1
self._base_depth = 0
self.in_pre = 0
self.in_table = 0
self._text = None # XXX does not work with links in headings!!!!!
self.randomID= None
self.list_depth = 0
self.list_type = ' '
def setRandomID(self,ID):
self.randomID = str(ID)
def _escape(self, text, extra_mapping={"'": "&apos;", '"': "&quot;"}):
return saxutils.escape(text, extra_mapping)
def startDocument(self, pagename):
encoding = config.charset
return '<?xml version="1.0" encoding="%s"?>\n<s1 title="%s">' % (
encoding, self._escape(pagename))
def endDocument(self):
result = ""
while self._current_depth > 1:
result += "</s%d>" % self._current_depth
self._current_depth -= 1
return result + '</s1>'
def lang(self, on, lang_name):
return ('<div lang="">' % lang_name, '</div>')[not on]
def sysmsg(self, on, **kw):
return ('<sysmsg>', '</sysmsg>')[not on]
def rawHTML(self, markup):
#return '<html>' + markup + '</html>'
return ''
def pagelink(self, on, pagename='', page=None, **kw):
if on:
return '[[:' + ":".join(pagename.split("/")) + "|"
else:
return ']]'
def interwikilink(self, on, interwiki='', pagename='', **kw):
if on:
if interwiki == 'Self':
return self.pagelink(on, pagename, **kw)
interwikis = {
'WikiPedia':'wp',
'FrWikiPedia':'wpfr',
'DeWikiPedia':'wpde',
'MetaWikiPedia':'wpmeta'
}
if interwiki in interwikis:
return '[[%s>%s|' % (interwikis.get(interwiki), pagename)
return '[[%s>%s|' % (interwiki, pagename)
else:
return ']]'
def url(self, on, url='', css=None, **kw):
return ('[[%s|' % (self._escape(url)), ']]')[not on]
def attachment_link(self, on, url=None, querystr=None, **kw):
if on:
return '{{ %s | ' % (self.randomID+url)
else:
return ' }}'
def attachment_image(self, url, **kw):
return '{{%s|}}' % (self.randomID+url,)
def attachment_drawing(self, url, text, **kw):
return '{{%s|%s}}' % (self.randomID+url, text)
def text(self, text, **kw):
self._did_para = 0
if self._text is not None:
self._text.append(text)
return text
def rule(self, size=0, **kw):
# size not supported
if size >= 4:
return '----\n'
else:
return '-' * size + '\n'
def icon(self, type):
return '<icon type="%s" />' % type
def strong(self, on, **kw):
return ['**', '**'][not on]
def emphasis(self, on, **kw):
return ['//', '//'][not on]
def highlight(self, on, **kw):
return ['**', '**'][not on]
def number_list(self, on, type=None, start=None, **kw):
# list type not supported
if on:
self.list_depth += 1
self.list_type = '-'
else:
self.list_depth -= 1
self.list_type = ' '
return ['', '\n'][on]
def bullet_list(self, on, **kw):
if on:
self.list_depth += 1
self.list_type = '*'
else:
self.list_depth -= 1
if self.list_depth <= 0:
self.list_type = ' '
return ['', '\n'][on]
# generic transclude/include:
def transclusion(self, on, **kw):
return ''
def transclusion_param(self, **kw):
return ''
def listitem(self, on, **kw):
# somewhy blockquote uses "listitem" call
return [(' ' * self.list_depth * 2) + self.list_type + ' ', '\n'][not on]
def code(self, on, **kw):
""" `typewriter` or {{{typerwriter}}, for code blocks i hope codeblock works """
return ["''%%", "%%''"][not on]
def sup(self, on, **kw):
return ['<sup>', '</sup>'][not on]
def sub(self, on, **kw):
return ['<sub>', '</sub>'][not on]
def strike(self, on, **kw):
return ['<del>', '</del>'][not on]
def small(self, on, **kw):
#https://www.dokuwiki.org/plugin:wrap
return ['<wrap lo>', '</wrap>'][not on]
def big(self, on, **kw):
#https://www.dokuwiki.org/plugin:wrap
return ['<wrap hi>', '</wrap>'][not on]
def preformatted(self, on, **kw):
FormatterBase.preformatted(self, on)
result = ''
if self.in_p:
result = self.paragraph(0)
return result + ['<code>', '</code>\n'][not on]
def paragraph(self, on, **kw):
FormatterBase.paragraph(self, on)
if self.in_table or self.list_depth:
return ''
return ['', '\n\n'][not on]
def linebreak(self, preformatted=1):
return ['\n', '\\\n'][not preformatted]
def heading(self, on, depth, **kw):
# heading depth reversed in dokuwiki
heading_depth = 7 - depth
if on:
return u'%s ' % (u'=' * heading_depth)
else:
return u' %s\n' % (u'=' * heading_depth)
def table(self, on, attrs={}, **kw):
if on:
self.in_table = 1
else:
self.in_table = 0
return ['', '\n'][not on]
def table_row(self, on, attrs={}, **kw):
return ['\n', '|'][not on]
def table_cell(self, on, attrs={}, **kw):
return ['|', ''][not on]
def anchordef(self, id):
# https://www.dokuwiki.org/plugin:anchor
return '{{anchor:'+id+'}}'
def anchorlink(self, on, name='', **kw):
# kw.id not supported, we hope the anchor matches existing heading on page
return ('[[#', ']]') [not on]
def underline(self, on, **kw):
return ['__', '__'][not on]
def definition_list(self, on, **kw):
# https://www.dokuwiki.org/plugin:definitionlist
result = ''
if self.in_p:
result = self.paragraph(0)
return result
def definition_term(self, on, compact=0, **kw):
#MoinMoin does no wiki markup in DL-Terms
return [' ;%%', '%%\n'][not on]
def definition_desc(self, on, **kw):
return [' :', '\n'][not on]
def image(self, src=None, **kw):
valid_attrs = ['src', 'width', 'height', 'alt', 'title']
url = src
if '?' in url:
url += '&'
else:
url += '?'
attrs = {}
for key, value in kw.items():
if key in valid_attrs:
attrs[key] = value
# TODO: finish this
if attrs.has_key('width'):
url += attrs['width']
return '{{' + url + '}}'
def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1,msg=None):
syntax = ''
# switch for Python: http://simonwillison.net/2004/may/7/switch/
try:
syntax = {
'ColorizedPython': 'python',
'ColorizedPascal': 'pascal',
'ColorizedJava': 'java',
'ColorizedCPlusPlus': 'cpp',
}[code_type]
except KeyError:
pass
return ('<code %s>' % syntax , '</code>')[not on]
def code_line(self, on):
return ('', '\n')[on]
def code_token(self, on, tok_type):
# not supported
return ''
def comment(self, text):
# real comments (lines with two hash marks)
if text[0:2] == '##':
# https://www.dokuwiki.org/plugin:comment
comment = text[2:].strip()
if len(comment)>1:
return "/* %s */\n" % text[2:].strip()
return '\n'
# Some kind of Processing Instruction
# http://moinmo.in/HelpOnProcessingInstructions
tokens = text.lstrip('#').split(None, 1)
if tokens[0] in ('language', 'format', 'refresh'):
return ''
if tokens[0] == 'acl':
# TODO: fill acl.auth.php
logging.info('SKIPPING ACL: %s', text)
return ''
if tokens[0] == 'deprecated':
return '<note warning>This page is deprecated</note>\n'
if tokens[0] == 'redirect':
return text + "\n"
if tokens[0] == 'pragma':
# TODO: can do 'description' via 'meta' dokuwiki plugin
pargs = tokens[1].split(None, 1)
if pargs[0]=='section-numbers':
return '/* meta: %s */' % tokens
logging.info('SKIPPING PRAGMA: %s', tokens)
#return "/* pragma: %s */\n" % " ".join(tokens[1:])
return ''
#return "/* %s */\n" % text.lstrip('#')
return ''
def macro(self, macro_obj, name, args,markup):
def email(args):
mail = args.replace(' AT ', '@')
mail = mail.replace(' DOT ', '.')
return '[[%s|%s]]' % (mail, args)
def showAttachedFiles(args):
args = args.split(',')
if len(args)>1:
return '{{ %s | %s }}' % (self.randomID+args[0].strip(), args[1].strip())
else:
return ''
# function which will just do what parent class would
def inherit(args):
return apply(FormatterBase.macro, (self, macro_obj, name, args))
def randomQuote(args):
# https://www.dokuwiki.org/plugin:xfortune
return '{{xfortune>quote:'+args+'.txt}}'
def monthcal(args):
# https://www.dokuwiki.org/plugin:monthcal
selfname = self.page.page_name
return '{{monthcal:create_links=short,namespace='+selfname.replace('/',':')+'}}'
def navigation(args):
# https://www.dokuwiki.org/plugin:alphaindex
selfname = self.page.page_name
args = args.split(',')
if len(args)>0:
try:
result = {
'slides': '[<>]',
'children': '{{alphaindex>:%s#1|nons incol}}' % selfname.replace('/',':'),
'siblings': '{{alphaindex>.#1|nons incol}}',
'slideshow': '/* no support for slideshow navigation */'
}[args[0].strip()]
except KeyError:
result = '/* Unknown Navigation: %s #%s#*/' % args, args[0].strip()
else:
result = '/* Unsupported Navigation: %s */' % args
return result
def footnote(args):
return '((%s))' % args
def dateTimeMacro(args):
#https://www.dokuwiki.org/plugin:date
#args = args.split(',');
return '{{date>%%c|timestamp=strtotime("%s")|locale=de}}' % args
def dateMacro(args):
#https://www.dokuwiki.org/plugin:date
#args = args.split(',');
return '{{date>%%x|timestamp=strtotime("%s")|locale=de}}' % args
def includeMacro(args):
#https://www.dokuwiki.org/plugin:include
#logging.info('Include(%s)' % args)
args = map(unicode.strip, args.split(','));
#dokupage = ":".join(pagename.split("/"))
if len(args)==1:
return '{{page>%s&nodate}}' % ":".join(args[0].split("/"))
elif(u'titlesonly' in args):
#https://www.dokuwiki.org/plugin:changes
#https://www.dokuwiki.org/plugin:pagelist
selfname = self.page.page_name
selfNs = ":".join(selfname.split("/")).lower()
pairs = [arg.split('=') for arg in args]
# attrs = {}
# for key, value in pairs:
# attrs[key] = value
#logging.info('pairs:"%s"' % pairs)
incName = ''#pairs[0]
incCount = -1
incTitlesOnly = False
notNamedParam = 0
for pair in pairs:
if len(pair)==1:
if u'titlesonly'==pair:
notNamedParam = -1
incTitlesOnly = True
elif notNamedParam >=0:
if notNamedParam==0:
incName = pairs[notNamedParam]
notNamedParam += 1;
else:
notNamedParam = -1
if u'items'==pair[0]:
incCount = int(pair[1])
resultArgs = '-h1 -textPages=""'
#(keys,values) = map()
if incCount > 0:
resultArgs += ' -idAndTitle -simpleList -sortId -nbItemsMax=%d' % incCount
else:
resultArgs += ' -nbCol=2'
##<nspages fortran:mailarchiv -pregPagesOn="/2.*/" -h1 -nbCol=2 -textPages="">
## Lister der letzten 10 Mails:
##<nspages .:mailarchiv -nbItemsMax=10 -sortId -simpleList -idAndTitle -reverse -h1 -nbCol=1 -textPages="">
if incName[0]=='^':
nspagedelim = incName.rfind('/')
ns = ":".join(incName[1:nspagedelim].split('/')).lower()
incPageReg = incName[(nspagedelim+1):]
resultArgs += ' -pregPagesOn="/^%s/"' % incPageReg
else:
ns = selfNs
return '<nspages %s %s>' % (ns, resultArgs)
else:
logging.info('UNSUPPORTED INCLUDE "%s"' % args)
return '/* Unsupported Include: %s */' % args
def fullsearch(args):
#args=None >> {searchform ns=}
#args='' >> {{backlinks>.}}
#args!='' >> {{search><args>}}
#ignore special searches. see MoinMoin page "HilfeZumSuchen"
if args is None:
return '{searchform ns=}'
elif ':' in args or ' ' in args:
logging.info('UNSUPPORTED SEARCH %s(%s)' % (name, args))
return '/* Unsupported Search %s(%s). may be backlinks plugin will help */' % (name, args)
elif args=='':
return '{{backlinks>.}}'
elif name=='PageList':
return '{{backlinks>%s}}' % ":".join(args.split('/')).lower();
else:
logging.info('UNSUPPORTED SEARCH %s(%s)' % (name, args))
return '/* Unsupported Search %s(%s) */' % (name, args)
try:
lookup = {
'BR' : ' \\\\ ',
'br' : ' \\\\ ',
'MailTo' : email,
'GetText' : args,
'ShowSmileys' : inherit,
'ShowAttachedFiles' : showAttachedFiles,
'Include' : includeMacro,
#no real fulltext search!
'FullSearch' : fullsearch,
'FullSearchCached' : fullsearch,
'PageList' : fullsearch,
'MonthCalendar' : monthcal,
'Navigation' : navigation,
'TableOfContents' : '',
'RandomQuote': randomQuote,
'Anchor': inherit,
'Action': inherit,
'Icon': inherit,
'FootNote': footnote,
'Date': dateMacro,
'DateTime': dateTimeMacro
}[name]
except KeyError:
logging.info('UNDEFINED MACRO "%s"' % name)
lookup = '/* UndefinedMacro: %s(%s) */' % (name, args)
if type(lookup) == FunctionType:
text = lookup(args)
else:
text = lookup
return text
def smiley(self, text):
try:
# https://www.dokuwiki.org/devel:smileys.conf
return {
# note: reverse sorted so that longer smileys get matched first
'X-(' : ':-X',
'{X}' : ':!:',
'{*}' : '<ubu>',
'(./)' : u'',
':))' : ':-P',
':-))' : ':-P',
':-?' : ':-P',
':o' : ':-o',
'{OK}' : ':!:',
'{o}' : '<circ>',
'{i}' : ':!:',
':D' : ':-D',
'B)' : '8-)',
'B-)' : '8-)',
'{3}' : '<3>',
'{2}' : '<2>',
'{1}' : '<1>',
'(!)' : ':!:',
'/!\\' : ':!:',
':\\' : ':-\\',
':))' : ':-)',
':)' : ':-)',
':(' : ':-(',
':-))' : ':-)',
':-)' : ':-)',
':-(' : ':-(',
';)' : ';-)',
'|)' : ':-|',
'|-)' : ':-|',
'>:>' : '^_^',
'<!>' : ':!:',
'<:(' : ':-?',
}[text]
except KeyError:
return text