[wiki-lenny] Suppresion de share/
Qui est maintenant directement inclut dans le paquet debian custom darcs-hash:20090310190607-bd074-6f18fc89ebf2acc3e566cce67afbb41958b8162c.gz
This commit is contained in:
parent
eae0d21f83
commit
264f35fcd5
16 changed files with 0 additions and 14505 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,432 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Formatter Package and FormatterBase
|
||||
|
||||
See "base.py" for the formatter interface.
|
||||
|
||||
@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
import re
|
||||
|
||||
from MoinMoin import log
|
||||
logging = log.getLogger(__name__)
|
||||
|
||||
from MoinMoin.util import pysupport
|
||||
from MoinMoin import wikiutil
|
||||
from MoinMoin.support.python_compatibility import rsplit
|
||||
|
||||
modules = pysupport.getPackageModules(__file__)
|
||||
|
||||
|
||||
class FormatterBase:
|
||||
""" This defines the output interface used all over the rest of the code.
|
||||
|
||||
Note that no other means should be used to generate _content_ output,
|
||||
while navigational elements (HTML page header/footer) and the like
|
||||
can be printed directly without violating output abstraction.
|
||||
"""
|
||||
|
||||
hardspace = ' '
|
||||
|
||||
def __init__(self, request, **kw):
|
||||
self.request = request
|
||||
self._ = request.getText
|
||||
|
||||
self._store_pagelinks = kw.get('store_pagelinks', 0)
|
||||
self._terse = kw.get('terse', 0)
|
||||
self.pagelinks = []
|
||||
self.in_p = 0
|
||||
self.in_pre = 0
|
||||
self._highlight_re = None
|
||||
self._base_depth = 0
|
||||
|
||||
def set_highlight_re(self, hi_re=None):
|
||||
""" set the highlighting regular expression (e.g. for search terms)
|
||||
|
||||
@param hi_re: either a valid re as str/unicode (you may want to use
|
||||
re.escape before passing generic strings!) or a compiled
|
||||
re object. raises re.error for invalid re.
|
||||
"""
|
||||
if isinstance(hi_re, (str, unicode)):
|
||||
hi_re = re.compile(hi_re, re.U + re.IGNORECASE)
|
||||
self._highlight_re = hi_re
|
||||
|
||||
def lang(self, on, lang_name):
|
||||
return ""
|
||||
|
||||
def setPage(self, page):
|
||||
self.page = page
|
||||
|
||||
def sysmsg(self, on, **kw):
|
||||
""" Emit a system message (embed it into the page).
|
||||
|
||||
Normally used to indicate disabled options, or invalid markup.
|
||||
"""
|
||||
return ""
|
||||
|
||||
# Document Level #####################################################
|
||||
|
||||
def startDocument(self, pagename):
|
||||
return ""
|
||||
|
||||
def endDocument(self):
|
||||
return ""
|
||||
|
||||
def startContent(self, content_id="content", **kw):
|
||||
if self.page:
|
||||
self.request.begin_include(self.page.page_name)
|
||||
return ""
|
||||
|
||||
def endContent(self):
|
||||
if self.page:
|
||||
self.request.end_include()
|
||||
return ""
|
||||
|
||||
# Links ##############################################################
|
||||
|
||||
def pagelink(self, on, pagename='', page=None, **kw):
|
||||
""" make a link to page <pagename>. Instead of supplying a pagename,
|
||||
it is also possible to give a live Page object, then page.page_name
|
||||
will be used.
|
||||
"""
|
||||
if not self._store_pagelinks or not on or kw.get('generated'):
|
||||
return ''
|
||||
if not pagename and page:
|
||||
pagename = page.page_name
|
||||
pagename = self.request.normalizePagename(pagename)
|
||||
if pagename and pagename not in self.pagelinks:
|
||||
self.pagelinks.append(pagename)
|
||||
|
||||
def interwikilink(self, on, interwiki='', pagename='', **kw):
|
||||
""" calls pagelink() for internal interwikilinks
|
||||
to make sure they get counted for self.pagelinks.
|
||||
IMPORTANT: on and off must be called with same parameters, see
|
||||
also the text_html formatter.
|
||||
"""
|
||||
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_interwiki(self.request, interwiki, pagename)
|
||||
if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
|
||||
if '#' in wikitail:
|
||||
wikitail, kw['anchor'] = rsplit(wikitail, '#', 1)
|
||||
wikitail = wikiutil.url_unquote(wikitail)
|
||||
return self.pagelink(on, wikitail, **kw)
|
||||
return ''
|
||||
|
||||
def url(self, on, url=None, css=None, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Attachments ######################################################
|
||||
|
||||
def attachment_link(self, on, url=None, **kw):
|
||||
raise NotImplementedError
|
||||
def attachment_image(self, url, **kw):
|
||||
raise NotImplementedError
|
||||
def attachment_drawing(self, url, text, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def attachment_inlined(self, url, text, **kw):
|
||||
from MoinMoin.action import AttachFile
|
||||
import os
|
||||
_ = self.request.getText
|
||||
pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
|
||||
fname = wikiutil.taintfilename(filename)
|
||||
fpath = AttachFile.getFilename(self.request, pagename, fname)
|
||||
ext = os.path.splitext(filename)[1]
|
||||
Parser = wikiutil.getParserForExtension(self.request.cfg, ext)
|
||||
if Parser is not None:
|
||||
try:
|
||||
content = file(fpath, 'r').read()
|
||||
# Try to decode text. It might return junk, but we don't
|
||||
# have enough information with attachments.
|
||||
content = wikiutil.decodeUnknownInput(content)
|
||||
colorizer = Parser(content, self.request, filename=filename)
|
||||
colorizer.format(self)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return (self.attachment_link(1, url) +
|
||||
self.text(text) +
|
||||
self.attachment_link(0))
|
||||
|
||||
def anchordef(self, name):
|
||||
return ""
|
||||
|
||||
def line_anchordef(self, lineno):
|
||||
return ""
|
||||
|
||||
def anchorlink(self, on, name='', **kw):
|
||||
return ""
|
||||
|
||||
def line_anchorlink(self, on, lineno=0):
|
||||
return ""
|
||||
|
||||
def image(self, src=None, **kw):
|
||||
"""An inline image.
|
||||
|
||||
Extra keyword arguments are according to the HTML <img> tag attributes.
|
||||
In particular an 'alt' or 'title' argument should give a description
|
||||
of the image.
|
||||
"""
|
||||
title = src
|
||||
for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
|
||||
if titleattr in kw:
|
||||
title = kw[titleattr]
|
||||
break
|
||||
if title:
|
||||
return '[Image:%s]' % title
|
||||
return '[Image]'
|
||||
|
||||
# generic transclude/include:
|
||||
def transclusion(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
def transclusion_param(self, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def smiley(self, text):
|
||||
return text
|
||||
|
||||
def nowikiword(self, text):
|
||||
return self.text(text)
|
||||
|
||||
# Text and Text Attributes ###########################################
|
||||
|
||||
def text(self, text, **kw):
|
||||
if not self._highlight_re:
|
||||
return self._text(text)
|
||||
|
||||
result = []
|
||||
lastpos = 0
|
||||
match = self._highlight_re.search(text)
|
||||
while match and lastpos < len(text):
|
||||
# add the match we found
|
||||
result.append(self._text(text[lastpos:match.start()]))
|
||||
result.append(self.highlight(1))
|
||||
result.append(self._text(match.group(0)))
|
||||
result.append(self.highlight(0))
|
||||
|
||||
# search for the next one
|
||||
lastpos = match.end() + (match.end() == lastpos)
|
||||
match = self._highlight_re.search(text, lastpos)
|
||||
|
||||
result.append(self._text(text[lastpos:]))
|
||||
return ''.join(result)
|
||||
|
||||
def _text(self, text):
|
||||
raise NotImplementedError
|
||||
|
||||
def strong(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def emphasis(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def underline(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def highlight(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def sup(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def sub(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def strike(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def code(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def preformatted(self, on, **kw):
|
||||
self.in_pre = on != 0
|
||||
|
||||
def small(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def big(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# special markup for syntax highlighting #############################
|
||||
|
||||
def code_area(self, on, code_id, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def code_line(self, on):
|
||||
raise NotImplementedError
|
||||
|
||||
def code_token(self, tok_text, tok_type):
|
||||
raise NotImplementedError
|
||||
|
||||
# Paragraphs, Lines, Rules ###########################################
|
||||
|
||||
def linebreak(self, preformatted=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def paragraph(self, on, **kw):
|
||||
self.in_p = on != 0
|
||||
|
||||
def rule(self, size=0, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def icon(self, type):
|
||||
return type
|
||||
|
||||
# Lists ##############################################################
|
||||
|
||||
def number_list(self, on, type=None, start=None, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def bullet_list(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def listitem(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_list(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_term(self, on, compact=0, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_desc(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def heading(self, on, depth, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Tables #############################################################
|
||||
|
||||
def table(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def table_row(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def table_cell(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Dynamic stuff / Plugins ############################################
|
||||
|
||||
def macro(self, macro_obj, name, args, markup=None):
|
||||
# call the macro
|
||||
try:
|
||||
return macro_obj.execute(name, args)
|
||||
except ImportError, err:
|
||||
errmsg = unicode(err)
|
||||
if not name in errmsg:
|
||||
raise
|
||||
if markup:
|
||||
return (self.span(1, title=errmsg) +
|
||||
self.text(markup) +
|
||||
self.span(0))
|
||||
else:
|
||||
return self.text(errmsg)
|
||||
def _get_bang_args(self, line):
|
||||
if line.startswith('#!'):
|
||||
try:
|
||||
name, args = line[2:].split(None, 1)
|
||||
except ValueError:
|
||||
return ''
|
||||
else:
|
||||
return args
|
||||
return None
|
||||
|
||||
def parser(self, parser_name, lines):
|
||||
""" parser_name MUST be valid!
|
||||
writes out the result instead of returning it!
|
||||
"""
|
||||
# attention: this is copied into text_python!
|
||||
parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", parser_name)
|
||||
args = None
|
||||
if lines:
|
||||
args = self._get_bang_args(lines[0])
|
||||
logging.debug("formatter.parser: parser args %r" % args)
|
||||
if args is not None:
|
||||
lines = lines[1:]
|
||||
if lines and not lines[0]:
|
||||
lines = lines[1:]
|
||||
if lines and not lines[-1].strip():
|
||||
lines = lines[:-1]
|
||||
p = parser('\n'.join(lines), self.request, format_args=args)
|
||||
p.format(self)
|
||||
del p
|
||||
return ''
|
||||
|
||||
# Other ##############################################################
|
||||
|
||||
def div(self, on, **kw):
|
||||
""" open/close a blocklevel division """
|
||||
return ""
|
||||
|
||||
def span(self, on, **kw):
|
||||
""" open/close a inline span """
|
||||
return ""
|
||||
|
||||
def rawHTML(self, markup):
|
||||
""" This allows emitting pre-formatted HTML markup, and should be
|
||||
used wisely (i.e. very seldom).
|
||||
|
||||
Using this event while generating content results in unwanted
|
||||
effects, like loss of markup or insertion of CDATA sections
|
||||
when output goes to XML formats.
|
||||
"""
|
||||
|
||||
import formatter, htmllib
|
||||
from MoinMoin.util import simpleIO
|
||||
|
||||
# Regenerate plain text
|
||||
f = simpleIO()
|
||||
h = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(f)))
|
||||
h.feed(markup)
|
||||
h.close()
|
||||
|
||||
return self.text(f.getvalue())
|
||||
|
||||
def escapedText(self, on, **kw):
|
||||
""" This allows emitting text as-is, anything special will
|
||||
be escaped (at least in HTML, some text output format
|
||||
would possibly do nothing here)
|
||||
"""
|
||||
return ""
|
||||
|
||||
def comment(self, text, **kw):
|
||||
return ""
|
||||
|
||||
# ID handling #################################################
|
||||
|
||||
def sanitize_to_id(self, text):
|
||||
'''
|
||||
Take 'text' and return something that is a valid ID
|
||||
for this formatter.
|
||||
The default returns the first non-space character of the string.
|
||||
|
||||
Because of the way this is used, it must be idempotent,
|
||||
i.e. calling it on an already sanitized id must yield the
|
||||
original id.
|
||||
'''
|
||||
return text.strip()[:1]
|
||||
|
||||
def make_id_unique(self, id):
|
||||
'''
|
||||
Take an ID and make it unique in the current namespace.
|
||||
'''
|
||||
ns = self.request.include_id
|
||||
if not ns is None:
|
||||
ns = self.sanitize_to_id(ns)
|
||||
id = self.sanitize_to_id(id)
|
||||
id = self.request.make_unique_id(id, ns)
|
||||
return id
|
||||
|
||||
def qualify_id(self, id):
|
||||
'''
|
||||
Take an ID and return a string that is qualified by
|
||||
the current namespace; this default implementation
|
||||
is suitable if the dot ('.') is valid in IDs for your
|
||||
formatter.
|
||||
'''
|
||||
ns = self.request.include_id
|
||||
if not ns is None:
|
||||
ns = self.sanitize_to_id(ns)
|
||||
return '%s.%s' % (ns, id)
|
||||
return id
|
|
@ -1,438 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Formatter Package and FormatterBase
|
||||
|
||||
See "base.py" for the formatter interface.
|
||||
|
||||
@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
import re
|
||||
|
||||
from MoinMoin import log
|
||||
logging = log.getLogger(__name__)
|
||||
|
||||
from MoinMoin.util import pysupport
|
||||
from MoinMoin import wikiutil
|
||||
from MoinMoin.support.python_compatibility import rsplit
|
||||
|
||||
modules = pysupport.getPackageModules(__file__)
|
||||
|
||||
|
||||
class FormatterBase:
|
||||
""" This defines the output interface used all over the rest of the code.
|
||||
|
||||
Note that no other means should be used to generate _content_ output,
|
||||
while navigational elements (HTML page header/footer) and the like
|
||||
can be printed directly without violating output abstraction.
|
||||
"""
|
||||
|
||||
hardspace = ' '
|
||||
|
||||
def __init__(self, request, **kw):
|
||||
self.request = request
|
||||
self._ = request.getText
|
||||
|
||||
self._store_pagelinks = kw.get('store_pagelinks', 0)
|
||||
self._terse = kw.get('terse', 0)
|
||||
self.pagelinks = []
|
||||
self.in_p = 0
|
||||
self.in_pre = 0
|
||||
self._highlight_re = None
|
||||
self._base_depth = 0
|
||||
|
||||
def set_highlight_re(self, hi_re=None):
|
||||
""" set the highlighting regular expression (e.g. for search terms)
|
||||
|
||||
@param hi_re: either a valid re as str/unicode (you may want to use
|
||||
re.escape before passing generic strings!) or a compiled
|
||||
re object. raises re.error for invalid re.
|
||||
"""
|
||||
if isinstance(hi_re, (str, unicode)):
|
||||
hi_re = re.compile(hi_re, re.U + re.IGNORECASE)
|
||||
self._highlight_re = hi_re
|
||||
|
||||
def lang(self, on, lang_name):
|
||||
return ""
|
||||
|
||||
def setPage(self, page):
|
||||
self.page = page
|
||||
|
||||
def sysmsg(self, on, **kw):
|
||||
""" Emit a system message (embed it into the page).
|
||||
|
||||
Normally used to indicate disabled options, or invalid markup.
|
||||
"""
|
||||
return ""
|
||||
|
||||
# Document Level #####################################################
|
||||
|
||||
def startDocument(self, pagename):
|
||||
return ""
|
||||
|
||||
def endDocument(self):
|
||||
return ""
|
||||
|
||||
def startContent(self, content_id="content", **kw):
|
||||
if self.page:
|
||||
self.request.begin_include(self.page.page_name)
|
||||
return ""
|
||||
|
||||
def endContent(self):
|
||||
if self.page:
|
||||
self.request.end_include()
|
||||
return ""
|
||||
|
||||
# Links ##############################################################
|
||||
|
||||
def pagelink(self, on, pagename='', page=None, **kw):
|
||||
""" make a link to page <pagename>. Instead of supplying a pagename,
|
||||
it is also possible to give a live Page object, then page.page_name
|
||||
will be used.
|
||||
"""
|
||||
if not self._store_pagelinks or not on or kw.get('generated'):
|
||||
return ''
|
||||
if not pagename and page:
|
||||
pagename = page.page_name
|
||||
pagename = self.request.normalizePagename(pagename)
|
||||
if pagename and pagename not in self.pagelinks:
|
||||
self.pagelinks.append(pagename)
|
||||
|
||||
def interwikilink(self, on, interwiki='', pagename='', **kw):
|
||||
""" calls pagelink() for internal interwikilinks
|
||||
to make sure they get counted for self.pagelinks.
|
||||
IMPORTANT: on and off must be called with same parameters, see
|
||||
also the text_html formatter.
|
||||
"""
|
||||
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_interwiki(self.request, interwiki, pagename)
|
||||
if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
|
||||
if '#' in wikitail:
|
||||
wikitail, kw['anchor'] = rsplit(wikitail, '#', 1)
|
||||
wikitail = wikiutil.url_unquote(wikitail)
|
||||
return self.pagelink(on, wikitail, **kw)
|
||||
return ''
|
||||
|
||||
def url(self, on, url=None, css=None, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Attachments ######################################################
|
||||
|
||||
def attachment_link(self, on, url=None, **kw):
|
||||
raise NotImplementedError
|
||||
def attachment_image(self, url, **kw):
|
||||
raise NotImplementedError
|
||||
def attachment_drawing(self, url, text, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def attachment_inlined(self, url, text, **kw):
|
||||
from MoinMoin.action import AttachFile
|
||||
import os
|
||||
_ = self.request.getText
|
||||
pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
|
||||
fname = wikiutil.taintfilename(filename)
|
||||
fpath = AttachFile.getFilename(self.request, pagename, fname)
|
||||
ext = os.path.splitext(filename)[1]
|
||||
Parser = wikiutil.getParserForExtension(self.request.cfg, ext)
|
||||
if Parser is not None:
|
||||
try:
|
||||
content = file(fpath, 'r').read()
|
||||
# Try to decode text. It might return junk, but we don't
|
||||
# have enough information with attachments.
|
||||
content = wikiutil.decodeUnknownInput(content)
|
||||
colorizer = Parser(content, self.request, filename=filename)
|
||||
colorizer.format(self)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return (self.attachment_link(1, url) +
|
||||
self.text(text) +
|
||||
self.attachment_link(0))
|
||||
|
||||
def anchordef(self, name):
|
||||
return ""
|
||||
|
||||
def line_anchordef(self, lineno):
|
||||
return ""
|
||||
|
||||
def anchorlink(self, on, name='', **kw):
|
||||
return ""
|
||||
|
||||
def line_anchorlink(self, on, lineno=0):
|
||||
return ""
|
||||
|
||||
def image(self, src=None, **kw):
|
||||
"""An inline image.
|
||||
|
||||
Extra keyword arguments are according to the HTML <img> tag attributes.
|
||||
In particular an 'alt' or 'title' argument should give a description
|
||||
of the image.
|
||||
"""
|
||||
title = src
|
||||
for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
|
||||
if titleattr in kw:
|
||||
title = kw[titleattr]
|
||||
break
|
||||
if title:
|
||||
return '[Image:%s]' % title
|
||||
return '[Image]'
|
||||
|
||||
# generic transclude/include:
|
||||
def transclusion(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
def transclusion_param(self, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def smiley(self, text):
|
||||
return text
|
||||
|
||||
def nowikiword(self, text):
|
||||
return self.text(text)
|
||||
|
||||
# Text and Text Attributes ###########################################
|
||||
|
||||
def text(self, text, **kw):
|
||||
if not self._highlight_re:
|
||||
return self._text(text)
|
||||
|
||||
result = []
|
||||
lastpos = 0
|
||||
match = self._highlight_re.search(text)
|
||||
while match and lastpos < len(text):
|
||||
# add the match we found
|
||||
result.append(self._text(text[lastpos:match.start()]))
|
||||
result.append(self.highlight(1))
|
||||
result.append(self._text(match.group(0)))
|
||||
result.append(self.highlight(0))
|
||||
|
||||
# search for the next one
|
||||
lastpos = match.end() + (match.end() == lastpos)
|
||||
match = self._highlight_re.search(text, lastpos)
|
||||
|
||||
result.append(self._text(text[lastpos:]))
|
||||
return ''.join(result)
|
||||
|
||||
def _text(self, text):
|
||||
raise NotImplementedError
|
||||
|
||||
def strong(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def emphasis(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def underline(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def highlight(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def sup(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def sub(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def strike(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def code(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def preformatted(self, on, **kw):
|
||||
self.in_pre = on != 0
|
||||
|
||||
def small(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def big(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# special markup for syntax highlighting #############################
|
||||
|
||||
def code_area(self, on, code_id, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def code_line(self, on):
|
||||
raise NotImplementedError
|
||||
|
||||
def code_token(self, tok_text, tok_type):
|
||||
raise NotImplementedError
|
||||
|
||||
def crans_box(self, title, content, color=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def crans_portal(self, entries):
|
||||
raise NotImplementedError
|
||||
|
||||
# Paragraphs, Lines, Rules ###########################################
|
||||
|
||||
def linebreak(self, preformatted=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def paragraph(self, on, **kw):
|
||||
self.in_p = on != 0
|
||||
|
||||
def rule(self, size=0, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def icon(self, type):
|
||||
return type
|
||||
|
||||
# Lists ##############################################################
|
||||
|
||||
def number_list(self, on, type=None, start=None, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def bullet_list(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def listitem(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_list(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_term(self, on, compact=0, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def definition_desc(self, on, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def heading(self, on, depth, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Tables #############################################################
|
||||
|
||||
def table(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def table_row(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
def table_cell(self, on, attrs={}, **kw):
|
||||
raise NotImplementedError
|
||||
|
||||
# Dynamic stuff / Plugins ############################################
|
||||
|
||||
def macro(self, macro_obj, name, args, markup=None):
|
||||
# call the macro
|
||||
try:
|
||||
return macro_obj.execute(name, args)
|
||||
except ImportError, err:
|
||||
errmsg = unicode(err)
|
||||
if not name in errmsg:
|
||||
raise
|
||||
if markup:
|
||||
return (self.span(1, title=errmsg) +
|
||||
self.text(markup) +
|
||||
self.span(0))
|
||||
else:
|
||||
return self.text(errmsg)
|
||||
def _get_bang_args(self, line):
|
||||
if line.startswith('#!'):
|
||||
try:
|
||||
name, args = line[2:].split(None, 1)
|
||||
except ValueError:
|
||||
return ''
|
||||
else:
|
||||
return args
|
||||
return None
|
||||
|
||||
def parser(self, parser_name, lines):
|
||||
""" parser_name MUST be valid!
|
||||
writes out the result instead of returning it!
|
||||
"""
|
||||
# attention: this is copied into text_python!
|
||||
parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", parser_name)
|
||||
args = None
|
||||
if lines:
|
||||
args = self._get_bang_args(lines[0])
|
||||
logging.debug("formatter.parser: parser args %r" % args)
|
||||
if args is not None:
|
||||
lines = lines[1:]
|
||||
if lines and not lines[0]:
|
||||
lines = lines[1:]
|
||||
if lines and not lines[-1].strip():
|
||||
lines = lines[:-1]
|
||||
p = parser('\n'.join(lines), self.request, format_args=args)
|
||||
p.format(self)
|
||||
del p
|
||||
return ''
|
||||
|
||||
# Other ##############################################################
|
||||
|
||||
def div(self, on, **kw):
|
||||
""" open/close a blocklevel division """
|
||||
return ""
|
||||
|
||||
def span(self, on, **kw):
|
||||
""" open/close a inline span """
|
||||
return ""
|
||||
|
||||
def rawHTML(self, markup):
|
||||
""" This allows emitting pre-formatted HTML markup, and should be
|
||||
used wisely (i.e. very seldom).
|
||||
|
||||
Using this event while generating content results in unwanted
|
||||
effects, like loss of markup or insertion of CDATA sections
|
||||
when output goes to XML formats.
|
||||
"""
|
||||
|
||||
import formatter, htmllib
|
||||
from MoinMoin.util import simpleIO
|
||||
|
||||
# Regenerate plain text
|
||||
f = simpleIO()
|
||||
h = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(f)))
|
||||
h.feed(markup)
|
||||
h.close()
|
||||
|
||||
return self.text(f.getvalue())
|
||||
|
||||
def escapedText(self, on, **kw):
|
||||
""" This allows emitting text as-is, anything special will
|
||||
be escaped (at least in HTML, some text output format
|
||||
would possibly do nothing here)
|
||||
"""
|
||||
return ""
|
||||
|
||||
def comment(self, text, **kw):
|
||||
return ""
|
||||
|
||||
# ID handling #################################################
|
||||
|
||||
def sanitize_to_id(self, text):
|
||||
'''
|
||||
Take 'text' and return something that is a valid ID
|
||||
for this formatter.
|
||||
The default returns the first non-space character of the string.
|
||||
|
||||
Because of the way this is used, it must be idempotent,
|
||||
i.e. calling it on an already sanitized id must yield the
|
||||
original id.
|
||||
'''
|
||||
return text.strip()[:1]
|
||||
|
||||
def make_id_unique(self, id):
|
||||
'''
|
||||
Take an ID and make it unique in the current namespace.
|
||||
'''
|
||||
ns = self.request.include_id
|
||||
if not ns is None:
|
||||
ns = self.sanitize_to_id(ns)
|
||||
id = self.sanitize_to_id(id)
|
||||
id = self.request.make_unique_id(id, ns)
|
||||
return id
|
||||
|
||||
def qualify_id(self, id):
|
||||
'''
|
||||
Take an ID and return a string that is qualified by
|
||||
the current namespace; this default implementation
|
||||
is suitable if the dot ('.') is valid in IDs for your
|
||||
formatter.
|
||||
'''
|
||||
ns = self.request.include_id
|
||||
if not ns is None:
|
||||
ns = self.sanitize_to_id(ns)
|
||||
return '%s.%s' % (ns, id)
|
||||
return id
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - pagelinks Formatter
|
||||
|
||||
@copyright: 2005 Nir Soffer <nirs@freeshell.org>
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
from MoinMoin.formatter import FormatterBase
|
||||
|
||||
class Formatter(FormatterBase):
|
||||
""" Collect pagelinks and format nothing :-) """
|
||||
|
||||
def pagelink(self, on, pagename='', page=None, **kw):
|
||||
FormatterBase.pagelink(self, on, pagename, page, **kw)
|
||||
return self.null()
|
||||
|
||||
def null(self, *args, **kw):
|
||||
return ''
|
||||
|
||||
# All these must be overriden here because they raise
|
||||
# NotImplementedError!@#! or return html?! in the base class.
|
||||
set_highlight_re = rawHTML = url = image = smiley = text = null
|
||||
strong = emphasis = underline = highlight = sup = sub = strike = null
|
||||
code = preformatted = small = big = code_area = code_line = null
|
||||
code_token = linebreak = paragraph = rule = icon = null
|
||||
number_list = bullet_list = listitem = definition_list = null
|
||||
definition_term = definition_desc = heading = table = null
|
||||
table_row = table_cell = attachment_link = attachment_image = attachment_drawing = null
|
||||
transclusion = transclusion_param = null
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - pagelinks Formatter
|
||||
|
||||
@copyright: 2005 Nir Soffer <nirs@freeshell.org>
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
from MoinMoin.formatter import FormatterBase
|
||||
|
||||
class Formatter(FormatterBase):
|
||||
""" Collect pagelinks and format nothing :-) """
|
||||
|
||||
def pagelink(self, on, pagename='', page=None, **kw):
|
||||
FormatterBase.pagelink(self, on, pagename, page, **kw)
|
||||
return self.null()
|
||||
|
||||
def null(self, *args, **kw):
|
||||
return ''
|
||||
|
||||
# All these must be overriden here because they raise
|
||||
# NotImplementedError!@#! or return html?! in the base class.
|
||||
set_highlight_re = rawHTML = url = image = smiley = text = null
|
||||
strong = emphasis = underline = highlight = sup = sub = strike = null
|
||||
code = preformatted = small = big = code_area = code_line = null
|
||||
code_token = linebreak = paragraph = rule = icon = null
|
||||
number_list = bullet_list = listitem = definition_list = null
|
||||
definition_term = definition_desc = heading = table = null
|
||||
table_row = table_cell = attachment_link = attachment_image = attachment_drawing = null
|
||||
transclusion = transclusion_param = null
|
||||
### HACK SAUVAGE 1/1
|
||||
crans_box = crans_portal = null
|
||||
### FIN HACK 1/1
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,234 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Dump a MoinMoin wiki to static pages
|
||||
|
||||
@copyright: 2002-2004 Juergen Hermann <jh@web.de>,
|
||||
2005-2006 MoinMoin:ThomasWaldmann
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
import sys, os, time, codecs, shutil, re, errno
|
||||
|
||||
from MoinMoin import config, wikiutil, Page, user
|
||||
from MoinMoin import script
|
||||
from MoinMoin.action import AttachFile
|
||||
|
||||
url_prefix_static = "."
|
||||
logo_html = '<img src="logo.png">'
|
||||
HTML_SUFFIX = ".html"
|
||||
|
||||
page_template = u'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=%(charset)s">
|
||||
<title>%(pagename)s</title>
|
||||
<link rel="stylesheet" type="text/css" media="all" charset="utf-8" href="%(theme)s/css/common.css">
|
||||
<link rel="stylesheet" type="text/css" media="screen" charset="utf-8" href="%(theme)s/css/screen.css">
|
||||
<link rel="stylesheet" type="text/css" media="print" charset="utf-8" href="%(theme)s/css/print.css">
|
||||
<style type="text/css">
|
||||
ul.pagetitle{
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
li.pagetitle{
|
||||
display: inline;
|
||||
margin: 0;
|
||||
}
|
||||
td.noborder {
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="noborder">
|
||||
%(logo_html)s
|
||||
</td>
|
||||
<td class="noborder">
|
||||
<ul class="pagetitle">
|
||||
<li class="pagetitle"><a class="backlink">%(pagename)s</a>
|
||||
</ul>
|
||||
<br><br>
|
||||
%(navibar_html)s
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<div id="page">
|
||||
%(pagehtml)s
|
||||
</div>
|
||||
<hr>
|
||||
%(timestamp)s
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
|
||||
def _attachment(request, pagename, filename, outputdir, **kw):
|
||||
filename = filename.encode(config.charset)
|
||||
source_dir = AttachFile.getAttachDir(request, pagename)
|
||||
source_file = os.path.join(source_dir, filename)
|
||||
dest_dir = os.path.join(outputdir, "attachments", wikiutil.quoteWikinameFS(pagename))
|
||||
dest_file = os.path.join(dest_dir, filename)
|
||||
dest_url = "attachments/%s/%s" % (wikiutil.quoteWikinameFS(pagename), wikiutil.url_quote(filename))
|
||||
if os.access(source_file, os.R_OK):
|
||||
if not os.access(dest_dir, os.F_OK):
|
||||
try:
|
||||
os.makedirs(dest_dir)
|
||||
except:
|
||||
script.fatal("Cannot create attachment directory '%s'" % dest_dir)
|
||||
elif not os.path.isdir(dest_dir):
|
||||
script.fatal("'%s' is not a directory" % dest_dir)
|
||||
|
||||
shutil.copyfile(source_file, dest_file)
|
||||
script.log('Writing "%s"...' % dest_url)
|
||||
return dest_url
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class PluginScript(script.MoinScript):
|
||||
"""\
|
||||
Purpose:
|
||||
========
|
||||
This tool allows you to dump MoinMoin wiki pages to static HTML files.
|
||||
|
||||
Detailed Instructions:
|
||||
======================
|
||||
General syntax: moin [options] export dump [dump-options]
|
||||
|
||||
[options] usually should be:
|
||||
--config-dir=/path/to/my/cfg/ --wiki-url=wiki.example.org/
|
||||
|
||||
[dump-options] see below:
|
||||
0. You must run this script as owner of the wiki files, usually this is the
|
||||
web server user.
|
||||
|
||||
1. To dump all the pages on the wiki to the directory '/mywiki'
|
||||
moin ... export dump --target-dir=/mywiki
|
||||
|
||||
2. To dump all the pages readable by 'JohnSmith' on the wiki to the directory
|
||||
'/mywiki'
|
||||
moin ... export dump --target-dir=/mywiki --username JohnSmith
|
||||
"""
|
||||
|
||||
def __init__(self, argv=None, def_values=None):
|
||||
script.MoinScript.__init__(self, argv, def_values)
|
||||
self.parser.add_option(
|
||||
"-t", "--target-dir", dest = "target_dir",
|
||||
help = "Write html dump to DIRECTORY"
|
||||
)
|
||||
self.parser.add_option(
|
||||
"-u", "--username", dest = "dump_user",
|
||||
help = "User the dump will be performed as (for ACL checks, etc)"
|
||||
)
|
||||
|
||||
def mainloop(self):
|
||||
""" moin-dump's main code. """
|
||||
|
||||
# Prepare output directory
|
||||
if not self.options.target_dir:
|
||||
script.fatal("you must use --target-dir=/your/output/path to specify the directory we write the html files to")
|
||||
outputdir = os.path.abspath(self.options.target_dir)
|
||||
try:
|
||||
os.mkdir(outputdir)
|
||||
script.log("Created output directory '%s'!" % outputdir)
|
||||
except OSError, err:
|
||||
if err.errno != errno.EEXIST:
|
||||
script.fatal("Cannot create output directory '%s'!" % outputdir)
|
||||
|
||||
# Insert config dir or the current directory to the start of the path.
|
||||
config_dir = self.options.config_dir
|
||||
if config_dir and os.path.isfile(config_dir):
|
||||
config_dir = os.path.dirname(config_dir)
|
||||
if config_dir and not os.path.isdir(config_dir):
|
||||
script.fatal("bad path given to --config-dir option")
|
||||
sys.path.insert(0, os.path.abspath(config_dir or os.curdir))
|
||||
|
||||
self.init_request()
|
||||
request = self.request
|
||||
|
||||
# fix url_prefix_static so we get relative paths in output html
|
||||
request.cfg.url_prefix_static = url_prefix_static
|
||||
|
||||
# use this user for permissions checks
|
||||
request.user = user.User(request, name=self.options.dump_user)
|
||||
|
||||
pages = request.rootpage.getPageList(user='') # get list of all pages in wiki
|
||||
pages.sort()
|
||||
if self.options.page: # did user request a particular page or group of pages?
|
||||
try:
|
||||
namematch = re.compile(self.options.page)
|
||||
pages = [page for page in pages if namematch.match(page)]
|
||||
if not pages:
|
||||
pages = [self.options.page]
|
||||
except:
|
||||
pages = [self.options.page]
|
||||
|
||||
wikiutil.quoteWikinameURL = lambda pagename, qfn=wikiutil.quoteWikinameFS: (qfn(pagename) + HTML_SUFFIX)
|
||||
|
||||
AttachFile.getAttachUrl = lambda pagename, filename, request, **kw: _attachment(request, pagename, filename, outputdir, **kw)
|
||||
|
||||
errfile = os.path.join(outputdir, 'error.log')
|
||||
errlog = open(errfile, 'w')
|
||||
errcnt = 0
|
||||
|
||||
page_front_page = wikiutil.getLocalizedPage(request, request.cfg.page_front_page).page_name
|
||||
page_title_index = wikiutil.getLocalizedPage(request, 'TitleIndex').page_name
|
||||
page_word_index = wikiutil.getLocalizedPage(request, 'WordIndex').page_name
|
||||
|
||||
navibar_html = ''
|
||||
for p in [page_front_page, page_title_index, page_word_index]:
|
||||
navibar_html += '[<a href="%s">%s</a>] ' % (wikiutil.quoteWikinameURL(p), wikiutil.escape(p))
|
||||
|
||||
urlbase = request.url # save wiki base url
|
||||
for pagename in pages:
|
||||
# we have the same name in URL and FS
|
||||
file = wikiutil.quoteWikinameURL(pagename)
|
||||
script.log('Writing "%s"...' % file)
|
||||
try:
|
||||
pagehtml = ''
|
||||
request.url = urlbase + pagename # add current pagename to url base
|
||||
page = Page.Page(request, pagename)
|
||||
request.page = page
|
||||
try:
|
||||
request.reset()
|
||||
pagehtml = request.redirectedOutput(page.send_page, count_hit=0, content_only=1)
|
||||
except:
|
||||
errcnt = errcnt + 1
|
||||
print >> sys.stderr, "*** Caught exception while writing page!"
|
||||
print >> errlog, "~" * 78
|
||||
print >> errlog, file # page filename
|
||||
import traceback
|
||||
traceback.print_exc(None, errlog)
|
||||
finally:
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M")
|
||||
filepath = os.path.join(outputdir, file)
|
||||
fileout = codecs.open(filepath, 'w', config.charset)
|
||||
fileout.write(page_template % {
|
||||
'charset': config.charset,
|
||||
'pagename': pagename,
|
||||
'pagehtml': pagehtml,
|
||||
'logo_html': logo_html,
|
||||
'navibar_html': navibar_html,
|
||||
'timestamp': timestamp,
|
||||
'theme': request.cfg.theme_default,
|
||||
})
|
||||
fileout.close()
|
||||
|
||||
# copy FrontPage to "index.html"
|
||||
indexpage = page_front_page
|
||||
if self.options.page:
|
||||
indexpage = pages[0] # index page has limited use when dumping specific pages, but create one anyway
|
||||
shutil.copyfile(
|
||||
os.path.join(outputdir, wikiutil.quoteWikinameFS(indexpage) + HTML_SUFFIX),
|
||||
os.path.join(outputdir, 'index' + HTML_SUFFIX)
|
||||
)
|
||||
|
||||
errlog.close()
|
||||
if errcnt:
|
||||
print >> sys.stderr, "*** %d error(s) occurred, see '%s'!" % (errcnt, errfile)
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Dump a MoinMoin wiki to static pages
|
||||
|
||||
@copyright: 2002-2004 Juergen Hermann <jh@web.de>,
|
||||
2005-2006 MoinMoin:ThomasWaldmann
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
import sys, os, time, codecs, shutil, re, errno
|
||||
|
||||
from MoinMoin import config, wikiutil, Page, user
|
||||
from MoinMoin import script
|
||||
from MoinMoin.action import AttachFile
|
||||
|
||||
url_prefix_static = "."
|
||||
logo_html = '<img src="logo.png">'
|
||||
HTML_SUFFIX = ".html"
|
||||
|
||||
### TEMPLATE MODIFIÉ
|
||||
page_template = u'''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=%(charset)s">
|
||||
<title>%(pagename)s</title>
|
||||
<link rel="stylesheet" type="text/css" media="all" charset="utf-8" href="/style.css">
|
||||
<link rel="stylesheet" type="text/css" media="all" charset="utf-8" href="%(theme)s/css/common.css">
|
||||
<link rel="stylesheet" type="text/css" media="screen" charset="utf-8" href="%(theme)s/css/screen.css">
|
||||
<link rel="stylesheet" type="text/css" media="print" charset="utf-8" href="%(theme)s/css/print.css">
|
||||
</head>
|
||||
<body>
|
||||
<p class="avertissement">
|
||||
Ce site est une copie statique et partielle de ce que l'on peut trouver
|
||||
sur le <a href="http://wiki.crans.org">wiki</a> de l'association. Si
|
||||
vous êtes ici, alors que vous avez demandé un autre site, c'est sans
|
||||
doute que vous êtes connecté au réseau wifi de l'association mais que
|
||||
vous n'avez pas encore complété toutes les étapes nécessaires pour avoir
|
||||
une connexion pleinement fonctionnelle. Ce site contient donc les infos
|
||||
pour configurer correctement votre connexion.
|
||||
</p>
|
||||
<div id="title"><h1>%(pagename)s</h1></div>
|
||||
%(pagehtml)s
|
||||
<p class="creation">
|
||||
Cette page a été extraite du wiki le %(timestamp)s. Vous pouvez l'<a href="http://wiki.crans.org/%(pagename)s?action=edit">éditer</a> ou <a href="http://www.crans.org/%(pagename)s">voir</a> la page originale.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
|
||||
def _attachment(request, pagename, filename, outputdir, **kw):
|
||||
filename = filename.encode(config.charset)
|
||||
source_dir = AttachFile.getAttachDir(request, pagename)
|
||||
source_file = os.path.join(source_dir, filename)
|
||||
dest_dir = os.path.join(outputdir, "attachments", wikiutil.quoteWikinameFS(pagename))
|
||||
dest_file = os.path.join(dest_dir, filename)
|
||||
dest_url = "attachments/%s/%s" % (wikiutil.quoteWikinameFS(pagename), wikiutil.url_quote(filename))
|
||||
if os.access(source_file, os.R_OK):
|
||||
if not os.access(dest_dir, os.F_OK):
|
||||
try:
|
||||
os.makedirs(dest_dir)
|
||||
except:
|
||||
script.fatal("Cannot create attachment directory '%s'" % dest_dir)
|
||||
elif not os.path.isdir(dest_dir):
|
||||
script.fatal("'%s' is not a directory" % dest_dir)
|
||||
|
||||
shutil.copyfile(source_file, dest_file)
|
||||
script.log('Writing "%s"...' % dest_url)
|
||||
return dest_url
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class PluginScript(script.MoinScript):
|
||||
"""\
|
||||
Purpose:
|
||||
========
|
||||
This tool allows you to dump MoinMoin wiki pages to static HTML files.
|
||||
|
||||
Detailed Instructions:
|
||||
======================
|
||||
General syntax: moin [options] export dump [dump-options]
|
||||
|
||||
[options] usually should be:
|
||||
--config-dir=/path/to/my/cfg/ --wiki-url=wiki.example.org/
|
||||
|
||||
[dump-options] see below:
|
||||
0. You must run this script as owner of the wiki files, usually this is the
|
||||
web server user.
|
||||
|
||||
1. To dump all the pages on the wiki to the directory '/mywiki'
|
||||
moin ... export dump --target-dir=/mywiki
|
||||
|
||||
2. To dump all the pages readable by 'JohnSmith' on the wiki to the directory
|
||||
'/mywiki'
|
||||
moin ... export dump --target-dir=/mywiki --username JohnSmith
|
||||
"""
|
||||
|
||||
def __init__(self, argv=None, def_values=None):
|
||||
script.MoinScript.__init__(self, argv, def_values)
|
||||
self.parser.add_option(
|
||||
"-t", "--target-dir", dest = "target_dir",
|
||||
help = "Write html dump to DIRECTORY"
|
||||
)
|
||||
self.parser.add_option(
|
||||
"-u", "--username", dest = "dump_user",
|
||||
help = "User the dump will be performed as (for ACL checks, etc)"
|
||||
)
|
||||
|
||||
def mainloop(self):
|
||||
""" moin-dump's main code. """
|
||||
|
||||
# Prepare output directory
|
||||
if not self.options.target_dir:
|
||||
script.fatal("you must use --target-dir=/your/output/path to specify the directory we write the html files to")
|
||||
outputdir = os.path.abspath(self.options.target_dir)
|
||||
try:
|
||||
os.mkdir(outputdir)
|
||||
script.log("Created output directory '%s'!" % outputdir)
|
||||
except OSError, err:
|
||||
if err.errno != errno.EEXIST:
|
||||
script.fatal("Cannot create output directory '%s'!" % outputdir)
|
||||
|
||||
# Insert config dir or the current directory to the start of the path.
|
||||
config_dir = self.options.config_dir
|
||||
if config_dir and os.path.isfile(config_dir):
|
||||
config_dir = os.path.dirname(config_dir)
|
||||
if config_dir and not os.path.isdir(config_dir):
|
||||
script.fatal("bad path given to --config-dir option")
|
||||
sys.path.insert(0, os.path.abspath(config_dir or os.curdir))
|
||||
|
||||
self.init_request()
|
||||
request = self.request
|
||||
|
||||
# fix url_prefix_static so we get relative paths in output html
|
||||
request.cfg.url_prefix_static = url_prefix_static
|
||||
|
||||
# use this user for permissions checks
|
||||
request.user = user.User(request, name=self.options.dump_user)
|
||||
|
||||
pages = request.rootpage.getPageList(user='') # get list of all pages in wiki
|
||||
pages.sort()
|
||||
if self.options.page: # did user request a particular page or group of pages?
|
||||
try:
|
||||
namematch = re.compile(self.options.page)
|
||||
pages = [page for page in pages if namematch.match(page)]
|
||||
if not pages:
|
||||
pages = [self.options.page]
|
||||
except:
|
||||
pages = [self.options.page]
|
||||
|
||||
wikiutil.quoteWikinameURL = lambda pagename, qfn=wikiutil.quoteWikinameFS: (qfn(pagename) + HTML_SUFFIX)
|
||||
|
||||
AttachFile.getAttachUrl = lambda pagename, filename, request, **kw: _attachment(request, pagename, filename, outputdir, **kw)
|
||||
|
||||
errfile = os.path.join(outputdir, 'error.log')
|
||||
errlog = open(errfile, 'w')
|
||||
errcnt = 0
|
||||
|
||||
page_front_page = wikiutil.getLocalizedPage(request, request.cfg.page_front_page).page_name
|
||||
page_title_index = wikiutil.getLocalizedPage(request, 'TitleIndex').page_name
|
||||
page_word_index = wikiutil.getLocalizedPage(request, 'WordIndex').page_name
|
||||
|
||||
navibar_html = ''
|
||||
for p in [page_front_page, page_title_index, page_word_index]:
|
||||
navibar_html += '[<a href="%s">%s</a>] ' % (wikiutil.quoteWikinameURL(p), wikiutil.escape(p))
|
||||
|
||||
urlbase = request.url # save wiki base url
|
||||
for pagename in pages:
|
||||
# we have the same name in URL and FS
|
||||
file = wikiutil.quoteWikinameURL(pagename)
|
||||
script.log('Writing "%s"...' % file)
|
||||
try:
|
||||
pagehtml = ''
|
||||
request.url = urlbase + pagename # add current pagename to url base
|
||||
page = Page.Page(request, pagename)
|
||||
request.page = page
|
||||
try:
|
||||
request.reset()
|
||||
pagehtml = request.redirectedOutput(page.send_page, count_hit=0, content_only=1)
|
||||
except:
|
||||
errcnt = errcnt + 1
|
||||
print >> sys.stderr, "*** Caught exception while writing page!"
|
||||
print >> errlog, "~" * 78
|
||||
print >> errlog, file # page filename
|
||||
import traceback
|
||||
traceback.print_exc(None, errlog)
|
||||
finally:
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M")
|
||||
filepath = os.path.join(outputdir, file)
|
||||
fileout = codecs.open(filepath, 'w', config.charset)
|
||||
fileout.write(page_template % {
|
||||
'charset': config.charset,
|
||||
'pagename': pagename,
|
||||
'pagehtml': pagehtml,
|
||||
'logo_html': logo_html,
|
||||
'navibar_html': navibar_html,
|
||||
'timestamp': timestamp,
|
||||
'theme': request.cfg.theme_default,
|
||||
})
|
||||
fileout.close()
|
||||
|
||||
# copy FrontPage to "index.html"
|
||||
indexpage = page_front_page
|
||||
if self.options.page:
|
||||
indexpage = pages[0] # index page has limited use when dumping specific pages, but create one anyway
|
||||
shutil.copyfile(
|
||||
os.path.join(outputdir, wikiutil.quoteWikinameFS(indexpage) + HTML_SUFFIX),
|
||||
os.path.join(outputdir, 'index' + HTML_SUFFIX)
|
||||
)
|
||||
|
||||
errlog.close()
|
||||
if errcnt:
|
||||
print >> sys.stderr, "*** %d error(s) occurred, see '%s'!" % (errcnt, errfile)
|
||||
|
|
@ -1,454 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Wiki Security Interface and Access Control Lists
|
||||
|
||||
|
||||
This implements the basic interface for user permissions and
|
||||
system policy. If you want to define your own policy, inherit
|
||||
from the base class 'Permissions', so that when new permissions
|
||||
are defined, you get the defaults.
|
||||
|
||||
Then assign your new class to "SecurityPolicy" in wikiconfig;
|
||||
and I mean the class, not an instance of it!
|
||||
|
||||
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
|
||||
2003-2008 MoinMoin:ThomasWaldmann,
|
||||
2003 Gustavo Niemeyer,
|
||||
2005 Oliver Graf,
|
||||
2007 Alexander Schremmer
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from MoinMoin import wikiutil, user
|
||||
from MoinMoin.Page import Page
|
||||
|
||||
#############################################################################
|
||||
### Basic Permissions Interface -- most features enabled by default
|
||||
#############################################################################
|
||||
|
||||
def _check(request, pagename, username, right):
|
||||
""" Check <right> access permission for user <username> on page <pagename>
|
||||
|
||||
For cfg.acl_hierarchic=False we just check the page in question.
|
||||
|
||||
For cfg.acl_hierarchic=True we, we check each page in the hierarchy. We
|
||||
start with the deepest page and recurse to the top of the tree.
|
||||
If one of those permits, True is returned.
|
||||
|
||||
For both configurations, we check acl_rights_before before the page/default
|
||||
acl and acl_rights_after after the page/default acl, of course.
|
||||
|
||||
This method should not be called by users, use __getattr__ instead.
|
||||
|
||||
@param request: the current request object
|
||||
@param pagename: pagename to get permissions from
|
||||
@param username: the user name
|
||||
@param right: the right to check
|
||||
|
||||
@rtype: bool
|
||||
@return: True if you have permission or False
|
||||
"""
|
||||
cache = request.cfg.cache
|
||||
allowed = cache.acl_rights_before.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
if request.cfg.acl_hierarchic:
|
||||
pages = pagename.split('/') # create page hierarchy list
|
||||
some_acl = False
|
||||
for i in range(len(pages), 0, -1):
|
||||
# Create the next pagename in the hierarchy
|
||||
# starting at the leaf, going to the root
|
||||
name = '/'.join(pages[:i])
|
||||
# Get page acl and ask for permission
|
||||
acl = Page(request, name).getACL(request)
|
||||
if acl.acl:
|
||||
some_acl = True
|
||||
allowed = acl.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
if not some_acl:
|
||||
allowed = cache.acl_rights_default.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
else:
|
||||
if request.page is not None and pagename == request.page.page_name:
|
||||
p = request.page # reuse is good
|
||||
else:
|
||||
p = Page(request, pagename)
|
||||
acl = p.getACL(request) # this will be fast in a reused page obj
|
||||
allowed = acl.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
allowed = cache.acl_rights_after.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Permissions:
|
||||
""" Basic interface for user permissions and system policy.
|
||||
|
||||
Note that you still need to allow some of the related actions, this
|
||||
just controls their behavior, not their activation.
|
||||
|
||||
When sub classing this class, you must extend the class methods, not
|
||||
replace them, or you might break the acl in the wiki. Correct sub
|
||||
classing looks like this:
|
||||
|
||||
def read(self, pagename):
|
||||
# Your special security rule
|
||||
if something:
|
||||
return false
|
||||
|
||||
# Do not return True or you break acl!
|
||||
# This call will use the default acl rules
|
||||
return Permissions.read(pagename)
|
||||
"""
|
||||
|
||||
def __init__(self, user):
|
||||
self.name = user.name
|
||||
self.request = user._request
|
||||
|
||||
def save(self, editor, newtext, rev, **kw):
|
||||
""" Check whether user may save a page.
|
||||
|
||||
`editor` is the PageEditor instance, the other arguments are
|
||||
those of the `PageEditor.saveText` method.
|
||||
|
||||
@param editor: PageEditor instance.
|
||||
@param newtext: new page text, you can enable of disable saving according
|
||||
to the content of the text, e.g. prevent link spam.
|
||||
@param rev: new revision number? XXX
|
||||
@param kw: XXX
|
||||
@rtype: bool
|
||||
@return: True if you can save or False
|
||||
"""
|
||||
return self.write(editor.page_name)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
""" Shortcut to export getPermission function for all known ACL rights
|
||||
|
||||
if attr is one of the rights in acl_rights_valid, then return a
|
||||
checking function for it. Else raise an AttributeError.
|
||||
|
||||
@param attr: one of ACL rights as defined in acl_rights_valid
|
||||
@rtype: function
|
||||
@return: checking function for that right, accepting a pagename
|
||||
"""
|
||||
request = self.request
|
||||
if attr not in request.cfg.acl_rights_valid:
|
||||
raise AttributeError, attr
|
||||
return lambda pagename: _check(self.request, pagename, self.name, attr)
|
||||
|
||||
|
||||
# make an alias for the default policy
|
||||
Default = Permissions
|
||||
|
||||
|
||||
class AccessControlList:
|
||||
''' Access Control List
|
||||
|
||||
Control who may do what on or with a wiki page.
|
||||
|
||||
Syntax of an ACL string:
|
||||
|
||||
[+|-]User[,User,...]:[right[,right,...]] [[+|-]SomeGroup:...] ...
|
||||
... [[+|-]Known:...] [[+|-]All:...]
|
||||
|
||||
"User" is a user name and triggers only if the user matches. Up
|
||||
to version 1.2 only WikiNames were supported, as of version 1.3,
|
||||
any name can be used in acl lines, including name with spaces
|
||||
using esoteric languages.
|
||||
|
||||
"SomeGroup" is a page name matching cfg.page_group_regex with
|
||||
some lines in the form " * Member", defining the group members.
|
||||
|
||||
"Known" is a group containing all valid / known users.
|
||||
|
||||
"All" is a group containing all users (Known and Anonymous users).
|
||||
|
||||
"right" may be an arbitrary word like read, write, delete, admin.
|
||||
Only words in cfg.acl_validrights are accepted, others are
|
||||
ignored. It is allowed to specify no rights, which means that no
|
||||
rights are given.
|
||||
|
||||
How ACL is processed
|
||||
|
||||
When some user is trying to access some ACL-protected resource,
|
||||
the ACLs will be processed in the order they are found. The first
|
||||
matching ACL will tell if the user has access to that resource
|
||||
or not.
|
||||
|
||||
For example, the following ACL tells that SomeUser is able to
|
||||
read and write the resources protected by that ACL, while any
|
||||
member of SomeGroup (besides SomeUser, if part of that group)
|
||||
may also admin that, and every other user is able to read it.
|
||||
|
||||
SomeUser:read,write SomeGroup:read,write,admin All:read
|
||||
|
||||
In this example, SomeUser can read and write but can not admin,
|
||||
revert or delete pages. Rights that are NOT specified on the
|
||||
right list are automatically set to NO.
|
||||
|
||||
Using Prefixes
|
||||
|
||||
To make the system more flexible, there are also two modifiers:
|
||||
the prefixes "+" and "-".
|
||||
|
||||
+SomeUser:read -OtherUser:write
|
||||
|
||||
The acl line above will grant SomeUser read right, and OtherUser
|
||||
write right, but will NOT block automatically all other rights
|
||||
for these users. For example, if SomeUser ask to write, the
|
||||
above acl line does not define if he can or can not write. He
|
||||
will be able to write if acl_rights_before or acl_rights_after
|
||||
allow this (see configuration options).
|
||||
|
||||
Using prefixes, this acl line:
|
||||
|
||||
SomeUser:read,write SomeGroup:read,write,admin All:read
|
||||
|
||||
Can be written as:
|
||||
|
||||
-SomeUser:admin SomeGroup:read,write,admin All:read
|
||||
|
||||
Or even:
|
||||
|
||||
+All:read -SomeUser:admin SomeGroup:read,write,admin
|
||||
|
||||
Notice that you probably will not want to use the second and
|
||||
third examples in ACL entries of some page. They are very
|
||||
useful on the moin configuration entries though.
|
||||
|
||||
Configuration options
|
||||
|
||||
cfg.acl_rights_default
|
||||
It is is ONLY used when no other ACLs are given.
|
||||
Default: "Known:read,write,delete All:read,write",
|
||||
|
||||
cfg.acl_rights_before
|
||||
When the page has ACL entries, this will be inserted BEFORE
|
||||
any page entries.
|
||||
Default: ""
|
||||
|
||||
cfg.acl_rights_after
|
||||
When the page has ACL entries, this will be inserted AFTER
|
||||
any page entries.
|
||||
Default: ""
|
||||
|
||||
cfg.acl_rights_valid
|
||||
These are the acceptable (known) rights (and the place to
|
||||
extend, if necessary).
|
||||
Default: ["read", "write", "delete", "admin"]
|
||||
'''
|
||||
|
||||
special_users = ["All", "Known", "Trusted"] # order is important
|
||||
|
||||
def __init__(self, cfg, lines=[]):
|
||||
"""Initialize an ACL, starting from <nothing>.
|
||||
"""
|
||||
if lines:
|
||||
self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
|
||||
self.acl_lines = []
|
||||
for line in lines:
|
||||
self._addLine(cfg, line)
|
||||
else:
|
||||
self.acl = None
|
||||
self.acl_lines = None
|
||||
|
||||
def _addLine(self, cfg, aclstring, remember=1):
|
||||
""" Add another ACL line
|
||||
|
||||
This can be used in multiple subsequent calls to process longer lists.
|
||||
|
||||
@param cfg: current config
|
||||
@param aclstring: acl string from page or cfg
|
||||
@param remember: should add the line to self.acl_lines
|
||||
"""
|
||||
|
||||
# Remember lines
|
||||
if remember:
|
||||
self.acl_lines.append(aclstring)
|
||||
|
||||
# Iterate over entries and rights, parsed by acl string iterator
|
||||
acliter = ACLStringIterator(cfg.acl_rights_valid, aclstring)
|
||||
for modifier, entries, rights in acliter:
|
||||
if entries == ['Default']:
|
||||
self._addLine(cfg, cfg.acl_rights_default, remember=0)
|
||||
else:
|
||||
for entry in entries:
|
||||
rightsdict = {}
|
||||
if modifier:
|
||||
# Only user rights are added to the right dict.
|
||||
# + add rights with value of 1
|
||||
# - add right with value of 0
|
||||
for right in rights:
|
||||
rightsdict[right] = (modifier == '+')
|
||||
else:
|
||||
# All rights from acl_rights_valid are added to the
|
||||
# dict, user rights with value of 1, and other with
|
||||
# value of 0
|
||||
for right in cfg.acl_rights_valid:
|
||||
rightsdict[right] = (right in rights)
|
||||
self.acl.append((entry, rightsdict))
|
||||
|
||||
def may(self, request, name, dowhat):
|
||||
""" May <name> <dowhat>? Returns boolean answer.
|
||||
|
||||
Note: this check does NOT include the acl_rights_before / _after ACL,
|
||||
but it WILL use acl_rights_default if there is no (page) ACL.
|
||||
"""
|
||||
if self.acl is None: # no #acl used on Page
|
||||
acl = request.cfg.cache.acl_rights_default.acl
|
||||
else: # we have a #acl on the page (self.acl can be [] if #acl is empty!)
|
||||
acl = self.acl
|
||||
is_group_member = request.dicts.has_member
|
||||
group_re = request.cfg.cache.page_group_regexact
|
||||
allowed = None
|
||||
for entry, rightsdict in acl:
|
||||
if entry in self.special_users:
|
||||
handler = getattr(self, "_special_"+entry, None)
|
||||
allowed = handler(request, name, dowhat, rightsdict)
|
||||
elif group_re.search(entry):
|
||||
if is_group_member(entry, name):
|
||||
allowed = rightsdict.get(dowhat)
|
||||
else:
|
||||
for special in self.special_users:
|
||||
if is_group_member(entry, special):
|
||||
handler = getattr(self, "_special_" + special, None)
|
||||
allowed = handler(request, name, dowhat, rightsdict)
|
||||
break # order of self.special_users is important
|
||||
elif entry == name:
|
||||
allowed = rightsdict.get(dowhat)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
return allowed # should be None
|
||||
|
||||
def getString(self, b='#acl ', e='\n'):
|
||||
"""print the acl strings we were fed with"""
|
||||
if self.acl_lines:
|
||||
acl_lines = ''.join(["%s%s%s" % (b, l, e) for l in self.acl_lines])
|
||||
else:
|
||||
acl_lines = ''
|
||||
return acl_lines
|
||||
|
||||
def _special_All(self, request, name, dowhat, rightsdict):
|
||||
return rightsdict.get(dowhat)
|
||||
|
||||
def _special_Known(self, request, name, dowhat, rightsdict):
|
||||
""" check if user <name> is known to us,
|
||||
that means that there is a valid user account present.
|
||||
works for subscription emails.
|
||||
"""
|
||||
if user.getUserId(request, name): # is a user with this name known?
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
|
||||
def _special_Trusted(self, request, name, dowhat, rightsdict):
|
||||
""" check if user <name> is known AND has logged in using a trusted
|
||||
authentication method.
|
||||
Does not work for subsription emails that should be sent to <user>,
|
||||
as he is not logged in in that case.
|
||||
"""
|
||||
if (request.user.name == name and
|
||||
request.user.auth_method in request.cfg.auth_methods_trusted):
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.acl_lines == other.acl_lines
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.acl_lines != other.acl_lines
|
||||
|
||||
|
||||
class ACLStringIterator:
|
||||
""" Iterator for acl string
|
||||
|
||||
Parse acl string and return the next entry on each call to
|
||||
next. Implement the Iterator protocol.
|
||||
|
||||
Usage:
|
||||
iter = ACLStringIterator(cfg.acl_rights_valid, 'user name:right')
|
||||
for modifier, entries, rights in iter:
|
||||
# process data
|
||||
"""
|
||||
|
||||
def __init__(self, rights, aclstring):
|
||||
""" Initialize acl iterator
|
||||
|
||||
@param rights: the acl rights to consider when parsing
|
||||
@param aclstring: string to parse
|
||||
"""
|
||||
self.rights = rights
|
||||
self.rest = aclstring.strip()
|
||||
self.finished = 0
|
||||
|
||||
def __iter__(self):
|
||||
""" Required by the Iterator protocol """
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
""" Return the next values from the acl string
|
||||
|
||||
When the iterator is finished and you try to call next, it
|
||||
raises a StopIteration. The iterator finish as soon as the
|
||||
string is fully parsed or can not be parsed any more.
|
||||
|
||||
@rtype: 3 tuple - (modifier, [entry, ...], [right, ...])
|
||||
@return: values for one item in an acl string
|
||||
"""
|
||||
# Handle finished state, required by iterator protocol
|
||||
if self.rest == '':
|
||||
self.finished = 1
|
||||
if self.finished:
|
||||
raise StopIteration
|
||||
|
||||
# Get optional modifier [+|-]entries:rights
|
||||
modifier = ''
|
||||
if self.rest[0] in ('+', '-'):
|
||||
modifier, self.rest = self.rest[0], self.rest[1:]
|
||||
|
||||
# Handle the Default meta acl
|
||||
if self.rest.startswith('Default ') or self.rest == 'Default':
|
||||
self.rest = self.rest[8:]
|
||||
entries, rights = ['Default'], []
|
||||
|
||||
# Handle entries:rights pairs
|
||||
else:
|
||||
# Get entries
|
||||
try:
|
||||
entries, self.rest = self.rest.split(':', 1)
|
||||
except ValueError:
|
||||
self.finished = 1
|
||||
raise StopIteration("Can't parse rest of string")
|
||||
if entries == '':
|
||||
entries = []
|
||||
else:
|
||||
# TODO strip each entry from blanks?
|
||||
entries = entries.split(',')
|
||||
|
||||
# Get rights
|
||||
try:
|
||||
rights, self.rest = self.rest.split(' ', 1)
|
||||
# Remove extra white space after rights fragment,
|
||||
# allowing using multiple spaces between items.
|
||||
self.rest = self.rest.lstrip()
|
||||
except ValueError:
|
||||
rights, self.rest = self.rest, ''
|
||||
rights = [r for r in rights.split(',') if r in self.rights]
|
||||
|
||||
return modifier, entries, rights
|
||||
|
||||
|
||||
def parseACL(request, text):
|
||||
""" Parse acl lines from text and return ACL object """
|
||||
pi, dummy = wikiutil.get_processing_instructions(text)
|
||||
acl_lines = [args for verb, args in pi if verb == 'acl']
|
||||
return AccessControlList(request.cfg, acl_lines)
|
||||
|
|
@ -1,501 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
MoinMoin - Wiki Security Interface and Access Control Lists
|
||||
|
||||
|
||||
This implements the basic interface for user permissions and
|
||||
system policy. If you want to define your own policy, inherit
|
||||
from the base class 'Permissions', so that when new permissions
|
||||
are defined, you get the defaults.
|
||||
|
||||
Then assign your new class to "SecurityPolicy" in wikiconfig;
|
||||
and I mean the class, not an instance of it!
|
||||
|
||||
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
|
||||
2003-2008 MoinMoin:ThomasWaldmann,
|
||||
2003 Gustavo Niemeyer,
|
||||
2005 Oliver Graf,
|
||||
2007 Alexander Schremmer
|
||||
@license: GNU GPL, see COPYING for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from MoinMoin import wikiutil, user
|
||||
from MoinMoin.Page import Page
|
||||
|
||||
### HACK SAUVAGE 1/5
|
||||
import sys
|
||||
sys.path.append('/usr/scripts/gestion/')
|
||||
from iptools import is_crans
|
||||
### FIN HACK 1/5
|
||||
|
||||
#############################################################################
|
||||
### Basic Permissions Interface -- most features enabled by default
|
||||
#############################################################################
|
||||
|
||||
def _check(request, pagename, username, right):
|
||||
""" Check <right> access permission for user <username> on page <pagename>
|
||||
|
||||
For cfg.acl_hierarchic=False we just check the page in question.
|
||||
|
||||
For cfg.acl_hierarchic=True we, we check each page in the hierarchy. We
|
||||
start with the deepest page and recurse to the top of the tree.
|
||||
If one of those permits, True is returned.
|
||||
|
||||
For both configurations, we check acl_rights_before before the page/default
|
||||
acl and acl_rights_after after the page/default acl, of course.
|
||||
|
||||
This method should not be called by users, use __getattr__ instead.
|
||||
|
||||
@param request: the current request object
|
||||
@param pagename: pagename to get permissions from
|
||||
@param username: the user name
|
||||
@param right: the right to check
|
||||
|
||||
@rtype: bool
|
||||
@return: True if you have permission or False
|
||||
"""
|
||||
cache = request.cfg.cache
|
||||
allowed = cache.acl_rights_before.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
if request.cfg.acl_hierarchic:
|
||||
pages = pagename.split('/') # create page hierarchy list
|
||||
some_acl = False
|
||||
for i in range(len(pages), 0, -1):
|
||||
# Create the next pagename in the hierarchy
|
||||
# starting at the leaf, going to the root
|
||||
name = '/'.join(pages[:i])
|
||||
# Get page acl and ask for permission
|
||||
acl = Page(request, name).getACL(request)
|
||||
if acl.acl:
|
||||
some_acl = True
|
||||
allowed = acl.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
if not some_acl:
|
||||
allowed = cache.acl_rights_default.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
else:
|
||||
if request.page is not None and pagename == request.page.page_name:
|
||||
p = request.page # reuse is good
|
||||
else:
|
||||
p = Page(request, pagename)
|
||||
acl = p.getACL(request) # this will be fast in a reused page obj
|
||||
allowed = acl.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
allowed = cache.acl_rights_after.may(request, username, right)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Permissions:
|
||||
""" Basic interface for user permissions and system policy.
|
||||
|
||||
Note that you still need to allow some of the related actions, this
|
||||
just controls their behavior, not their activation.
|
||||
|
||||
When sub classing this class, you must extend the class methods, not
|
||||
replace them, or you might break the acl in the wiki. Correct sub
|
||||
classing looks like this:
|
||||
|
||||
def read(self, pagename):
|
||||
# Your special security rule
|
||||
if something:
|
||||
return false
|
||||
|
||||
# Do not return True or you break acl!
|
||||
# This call will use the default acl rules
|
||||
return Permissions.read(pagename)
|
||||
"""
|
||||
|
||||
def __init__(self, user):
|
||||
self.name = user.name
|
||||
self.request = user._request
|
||||
|
||||
def save(self, editor, newtext, rev, **kw):
|
||||
""" Check whether user may save a page.
|
||||
|
||||
`editor` is the PageEditor instance, the other arguments are
|
||||
those of the `PageEditor.saveText` method.
|
||||
|
||||
@param editor: PageEditor instance.
|
||||
@param newtext: new page text, you can enable of disable saving according
|
||||
to the content of the text, e.g. prevent link spam.
|
||||
@param rev: new revision number? XXX
|
||||
@param kw: XXX
|
||||
@rtype: bool
|
||||
@return: True if you can save or False
|
||||
"""
|
||||
return self.write(editor.page_name)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
""" Shortcut to export getPermission function for all known ACL rights
|
||||
|
||||
if attr is one of the rights in acl_rights_valid, then return a
|
||||
checking function for it. Else raise an AttributeError.
|
||||
|
||||
@param attr: one of ACL rights as defined in acl_rights_valid
|
||||
@rtype: function
|
||||
@return: checking function for that right, accepting a pagename
|
||||
"""
|
||||
request = self.request
|
||||
if attr not in request.cfg.acl_rights_valid:
|
||||
raise AttributeError, attr
|
||||
return lambda pagename: _check(self.request, pagename, self.name, attr)
|
||||
|
||||
|
||||
# make an alias for the default policy
|
||||
Default = Permissions
|
||||
|
||||
|
||||
class AccessControlList:
|
||||
''' Access Control List
|
||||
|
||||
Control who may do what on or with a wiki page.
|
||||
|
||||
Syntax of an ACL string:
|
||||
|
||||
[+|-]User[,User,...]:[right[,right,...]] [[+|-]SomeGroup:...] ...
|
||||
... [[+|-]Known:...] [[+|-]All:...]
|
||||
|
||||
"User" is a user name and triggers only if the user matches. Up
|
||||
to version 1.2 only WikiNames were supported, as of version 1.3,
|
||||
any name can be used in acl lines, including name with spaces
|
||||
using esoteric languages.
|
||||
|
||||
"SomeGroup" is a page name matching cfg.page_group_regex with
|
||||
some lines in the form " * Member", defining the group members.
|
||||
|
||||
"Known" is a group containing all valid / known users.
|
||||
|
||||
"All" is a group containing all users (Known and Anonymous users).
|
||||
|
||||
"right" may be an arbitrary word like read, write, delete, admin.
|
||||
Only words in cfg.acl_validrights are accepted, others are
|
||||
ignored. It is allowed to specify no rights, which means that no
|
||||
rights are given.
|
||||
|
||||
How ACL is processed
|
||||
|
||||
When some user is trying to access some ACL-protected resource,
|
||||
the ACLs will be processed in the order they are found. The first
|
||||
matching ACL will tell if the user has access to that resource
|
||||
or not.
|
||||
|
||||
For example, the following ACL tells that SomeUser is able to
|
||||
read and write the resources protected by that ACL, while any
|
||||
member of SomeGroup (besides SomeUser, if part of that group)
|
||||
may also admin that, and every other user is able to read it.
|
||||
|
||||
SomeUser:read,write SomeGroup:read,write,admin All:read
|
||||
|
||||
In this example, SomeUser can read and write but can not admin,
|
||||
revert or delete pages. Rights that are NOT specified on the
|
||||
right list are automatically set to NO.
|
||||
|
||||
Using Prefixes
|
||||
|
||||
To make the system more flexible, there are also two modifiers:
|
||||
the prefixes "+" and "-".
|
||||
|
||||
+SomeUser:read -OtherUser:write
|
||||
|
||||
The acl line above will grant SomeUser read right, and OtherUser
|
||||
write right, but will NOT block automatically all other rights
|
||||
for these users. For example, if SomeUser ask to write, the
|
||||
above acl line does not define if he can or can not write. He
|
||||
will be able to write if acl_rights_before or acl_rights_after
|
||||
allow this (see configuration options).
|
||||
|
||||
Using prefixes, this acl line:
|
||||
|
||||
SomeUser:read,write SomeGroup:read,write,admin All:read
|
||||
|
||||
Can be written as:
|
||||
|
||||
-SomeUser:admin SomeGroup:read,write,admin All:read
|
||||
|
||||
Or even:
|
||||
|
||||
+All:read -SomeUser:admin SomeGroup:read,write,admin
|
||||
|
||||
Notice that you probably will not want to use the second and
|
||||
third examples in ACL entries of some page. They are very
|
||||
useful on the moin configuration entries though.
|
||||
|
||||
Configuration options
|
||||
|
||||
cfg.acl_rights_default
|
||||
It is is ONLY used when no other ACLs are given.
|
||||
Default: "Known:read,write,delete All:read,write",
|
||||
|
||||
cfg.acl_rights_before
|
||||
When the page has ACL entries, this will be inserted BEFORE
|
||||
any page entries.
|
||||
Default: ""
|
||||
|
||||
cfg.acl_rights_after
|
||||
When the page has ACL entries, this will be inserted AFTER
|
||||
any page entries.
|
||||
Default: ""
|
||||
|
||||
cfg.acl_rights_valid
|
||||
These are the acceptable (known) rights (and the place to
|
||||
extend, if necessary).
|
||||
Default: ["read", "write", "delete", "admin"]
|
||||
'''
|
||||
|
||||
#special_users = ["All", "Known", "Trusted"] # order is important
|
||||
### HACK SAUVAGE 2/5
|
||||
special_users = ["All", "Known", "Trusted", "Crans", "NoCrans"] # order is important
|
||||
### FIN HACK 2/5
|
||||
|
||||
def __init__(self, cfg, lines=[]):
|
||||
"""Initialize an ACL, starting from <nothing>.
|
||||
"""
|
||||
if lines:
|
||||
self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
|
||||
self.acl_lines = []
|
||||
for line in lines:
|
||||
self._addLine(cfg, line)
|
||||
else:
|
||||
self.acl = None
|
||||
self.acl_lines = None
|
||||
|
||||
def _addLine(self, cfg, aclstring, remember=1):
|
||||
""" Add another ACL line
|
||||
|
||||
This can be used in multiple subsequent calls to process longer lists.
|
||||
|
||||
@param cfg: current config
|
||||
@param aclstring: acl string from page or cfg
|
||||
@param remember: should add the line to self.acl_lines
|
||||
"""
|
||||
|
||||
# Remember lines
|
||||
if remember:
|
||||
self.acl_lines.append(aclstring)
|
||||
|
||||
# Iterate over entries and rights, parsed by acl string iterator
|
||||
acliter = ACLStringIterator(cfg.acl_rights_valid, aclstring)
|
||||
for modifier, entries, rights in acliter:
|
||||
if entries == ['Default']:
|
||||
self._addLine(cfg, cfg.acl_rights_default, remember=0)
|
||||
else:
|
||||
for entry in entries:
|
||||
rightsdict = {}
|
||||
if modifier:
|
||||
# Only user rights are added to the right dict.
|
||||
# + add rights with value of 1
|
||||
# - add right with value of 0
|
||||
for right in rights:
|
||||
rightsdict[right] = (modifier == '+')
|
||||
else:
|
||||
# All rights from acl_rights_valid are added to the
|
||||
# dict, user rights with value of 1, and other with
|
||||
# value of 0
|
||||
for right in cfg.acl_rights_valid:
|
||||
rightsdict[right] = (right in rights)
|
||||
self.acl.append((entry, rightsdict))
|
||||
|
||||
def may(self, request, name, dowhat):
|
||||
""" May <name> <dowhat>? Returns boolean answer.
|
||||
|
||||
Note: this check does NOT include the acl_rights_before / _after ACL,
|
||||
but it WILL use acl_rights_default if there is no (page) ACL.
|
||||
"""
|
||||
if self.acl is None: # no #acl used on Page
|
||||
acl = request.cfg.cache.acl_rights_default.acl
|
||||
else: # we have a #acl on the page (self.acl can be [] if #acl is empty!)
|
||||
acl = self.acl
|
||||
is_group_member = request.dicts.has_member
|
||||
group_re = request.cfg.cache.page_group_regexact
|
||||
allowed = None
|
||||
for entry, rightsdict in acl:
|
||||
if entry in self.special_users:
|
||||
handler = getattr(self, "_special_"+entry, None)
|
||||
allowed = handler(request, name, dowhat, rightsdict)
|
||||
elif group_re.search(entry):
|
||||
if is_group_member(entry, name):
|
||||
allowed = rightsdict.get(dowhat)
|
||||
else:
|
||||
for special in self.special_users:
|
||||
if is_group_member(entry, special):
|
||||
handler = getattr(self, "_special_" + special, None)
|
||||
allowed = handler(request, name, dowhat, rightsdict)
|
||||
break # order of self.special_users is important
|
||||
elif entry == name:
|
||||
allowed = rightsdict.get(dowhat)
|
||||
if allowed is not None:
|
||||
return allowed
|
||||
return allowed # should be None
|
||||
|
||||
def getString(self, b='#acl ', e='\n'):
|
||||
"""print the acl strings we were fed with"""
|
||||
if self.acl_lines:
|
||||
acl_lines = ''.join(["%s%s%s" % (b, l, e) for l in self.acl_lines])
|
||||
else:
|
||||
acl_lines = ''
|
||||
return acl_lines
|
||||
|
||||
def _special_All(self, request, name, dowhat, rightsdict):
|
||||
### HACK SAUVAGE 3/5
|
||||
if dowhat == "read" and is_page_public(request):
|
||||
return True
|
||||
### FIN HACK 3/5
|
||||
return rightsdict.get(dowhat)
|
||||
|
||||
def _special_Known(self, request, name, dowhat, rightsdict):
|
||||
""" check if user <name> is known to us,
|
||||
that means that there is a valid user account present.
|
||||
works for subscription emails.
|
||||
"""
|
||||
if user.getUserId(request, name): # is a user with this name known?
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
|
||||
def _special_Trusted(self, request, name, dowhat, rightsdict):
|
||||
""" check if user <name> is known AND has logged in using a trusted
|
||||
authentication method.
|
||||
Does not work for subsription emails that should be sent to <user>,
|
||||
as he is not logged in in that case.
|
||||
"""
|
||||
if (request.user.name == name and
|
||||
request.user.auth_method in request.cfg.auth_methods_trusted):
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
|
||||
### HACK SAUVAGE 4/5
|
||||
def _requete_interne(self, request):
|
||||
try:
|
||||
if is_crans(request.remote_addr):
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def _special_Crans(self, request, name, dowhat, rightsdict):
|
||||
if self._requete_interne(request):
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
|
||||
def _special_NoCrans(self, request, name, dowhat, rightsdict):
|
||||
if dowhat == "read" and is_page_public(request):
|
||||
return True
|
||||
if not self._requete_interne(request):
|
||||
return rightsdict.get(dowhat)
|
||||
return None
|
||||
### FIN HACK 4/5
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.acl_lines == other.acl_lines
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.acl_lines != other.acl_lines
|
||||
|
||||
|
||||
class ACLStringIterator:
|
||||
""" Iterator for acl string
|
||||
|
||||
Parse acl string and return the next entry on each call to
|
||||
next. Implement the Iterator protocol.
|
||||
|
||||
Usage:
|
||||
iter = ACLStringIterator(cfg.acl_rights_valid, 'user name:right')
|
||||
for modifier, entries, rights in iter:
|
||||
# process data
|
||||
"""
|
||||
|
||||
def __init__(self, rights, aclstring):
|
||||
""" Initialize acl iterator
|
||||
|
||||
@param rights: the acl rights to consider when parsing
|
||||
@param aclstring: string to parse
|
||||
"""
|
||||
self.rights = rights
|
||||
self.rest = aclstring.strip()
|
||||
self.finished = 0
|
||||
|
||||
def __iter__(self):
|
||||
""" Required by the Iterator protocol """
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
""" Return the next values from the acl string
|
||||
|
||||
When the iterator is finished and you try to call next, it
|
||||
raises a StopIteration. The iterator finish as soon as the
|
||||
string is fully parsed or can not be parsed any more.
|
||||
|
||||
@rtype: 3 tuple - (modifier, [entry, ...], [right, ...])
|
||||
@return: values for one item in an acl string
|
||||
"""
|
||||
# Handle finished state, required by iterator protocol
|
||||
if self.rest == '':
|
||||
self.finished = 1
|
||||
if self.finished:
|
||||
raise StopIteration
|
||||
|
||||
# Get optional modifier [+|-]entries:rights
|
||||
modifier = ''
|
||||
if self.rest[0] in ('+', '-'):
|
||||
modifier, self.rest = self.rest[0], self.rest[1:]
|
||||
|
||||
# Handle the Default meta acl
|
||||
if self.rest.startswith('Default ') or self.rest == 'Default':
|
||||
self.rest = self.rest[8:]
|
||||
entries, rights = ['Default'], []
|
||||
|
||||
# Handle entries:rights pairs
|
||||
else:
|
||||
# Get entries
|
||||
try:
|
||||
entries, self.rest = self.rest.split(':', 1)
|
||||
except ValueError:
|
||||
self.finished = 1
|
||||
raise StopIteration("Can't parse rest of string")
|
||||
if entries == '':
|
||||
entries = []
|
||||
else:
|
||||
# TODO strip each entry from blanks?
|
||||
entries = entries.split(',')
|
||||
|
||||
# Get rights
|
||||
try:
|
||||
rights, self.rest = self.rest.split(' ', 1)
|
||||
# Remove extra white space after rights fragment,
|
||||
# allowing using multiple spaces between items.
|
||||
self.rest = self.rest.lstrip()
|
||||
except ValueError:
|
||||
rights, self.rest = self.rest, ''
|
||||
rights = [r for r in rights.split(',') if r in self.rights]
|
||||
|
||||
return modifier, entries, rights
|
||||
|
||||
|
||||
def parseACL(request, text):
|
||||
""" Parse acl lines from text and return ACL object """
|
||||
pi, dummy = wikiutil.get_processing_instructions(text)
|
||||
acl_lines = [args for verb, args in pi if verb == 'acl']
|
||||
return AccessControlList(request.cfg, acl_lines)
|
||||
|
||||
### HACK SAUVAGE 5/5
|
||||
def is_page_public(request):
|
||||
#return True
|
||||
## On recherche si la page est publique
|
||||
this_page = request.page.page_name
|
||||
categories = request.page.getCategories(request)
|
||||
if u'CatégoriePagePublique' in categories:
|
||||
return True
|
||||
else:
|
||||
return None
|
||||
### FIN HACK 5/5
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue