X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=geekigeeki.py;h=d7e1a5a9e0d28a466e8f40b5704df5d4ea77148f;hb=ff74de7a49cc2bfa936503a4b37fc5c8a650a5fc;hp=a6158a3122b41b184930bf0255664a428851858d;hpb=fef72da7c35a10fc92c098a9287683e9d71bea1d;p=geekigeeki.git diff --git a/geekigeeki.py b/geekigeeki.py index a6158a3..d7e1a5a 100755 --- a/geekigeeki.py +++ b/geekigeeki.py @@ -3,7 +3,7 @@ # # Copyright 1999, 2000 Martin Pool # Copyright 2002 Gerardo Poggiali -# Copyright 2007, 2008 Bernardo Innocenti +# Copyright 2007, 2008 Bernie Innocenti # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,11 +28,12 @@ from os import path, environ # Regular expression defining a WikiWord # (but this definition is also assumed in other places) -word_re = re.compile(r"^\b((([A-Z][a-z]+){2,}/)*([A-Z][a-z]+){2,})\b$") +word_re = re.compile(r"^\b((([A-Z][a-z0-9]+){2,}/)*([A-Z][a-z0-9]+){2,})\b$") # FIXME: we accept stuff like foo/../bar and we shouldn't file_re = re.compile(r"^\b([A-Za-z0-9_\-][A-Za-z0-9_\.\-/]*)\b$") img_re = re.compile(r"^.*\.(png|gif|jpg|jpeg)$", re.IGNORECASE) url_re = re.compile(r"^[a-z]{3,8}://[^\s'\"]+\S$") +link_re = re.compile("(?:\[\[|{{)([^\s\|]+)(?:\s*\|\s*([^\]]+)|)(?:\]\]|}})") title_done = False @@ -70,8 +71,10 @@ def relative_url(pathname, privileged=False): pathname = url + '/' + pathname return pathname -# Formatting stuff -------------------------------------------------- +def permalink(s): + return re.sub(' ', '-', re.sub('[^a-z0-9_ ]', '', s.lower()).strip()) +# Formatting stuff -------------------------------------------------- def emit_header(mime_type="text/html"): print "Content-type: " + mime_type + "; charset=utf-8\n" @@ -137,11 +140,10 @@ def send_title(name, text="Limbo", msg_text=None, msg_type='error'): # Navbar print '' -# ---------------------------------------------------------- -# Macros -def _macro_TitleSearch(*vargs): - return _macro_search("titlesearch") - -def _macro_FullSearch(*vargs): - return _macro_search("fullsearch") - -def _macro_search(type): - default = '' - if 'value' in form: - default = form['value'].value - return """
""" % (type, default) - -def _macro_WordIndex(*vargs): - s = make_index_key() - pages = list(page_list()) - map = {} - word_re = re.compile('[A-Z][a-z]+') - for name in pages: - for word in word_re.findall(name): - try: - map[word].append(name) - except KeyError: - map[word] = [name] - - all_words = map.keys() - all_words.sort() - last_letter = None - # set title - for word in all_words: - letter = word[0].lower() - if letter != last_letter: - s = s + ';

%s

' % (letter, letter) - last_letter = letter - - s = s + '%s
    ' % word - links = map[word] - links.sort() - last_page = None - for name in links: - if name == last_page: continue - s = s + '
  • ' + Page(name).link_to() - s = s + '
' - return s - - -def _macro_TitleIndex(*vargs): - s = make_index_key() - pages = list(page_list()) - pages.sort() - current_letter = None - for name in pages: - letter = name[0].lower() - if letter != current_letter: - s += '

%s

' % (letter, letter) - current_letter = letter - else: - s += '
' - s += Page(name).link_to() - return s - - -# ---------------------------------------------------------- -class PageFormatter: +class WikiFormatter: """Object that turns Wiki markup into HTML. All formatting commands can be parsed one line at a time, though @@ -336,10 +274,10 @@ class PageFormatter: def __init__(self, raw): self.raw = raw self.h_level = 0 - self.in_pre = self.in_table = False + self.in_pre = self.in_html = self.in_table = self.in_li = False self.in_header = True self.list_indents = [] - self.tr_cnt = self.h_cnt = 0 + self.tr_cnt = 0 self.styles = { #wiki html enabled? "//": ["em", False], @@ -360,13 +298,12 @@ class PageFormatter: def _tit_repl(self, word): if self.h_level: - result = '' % self.h_level + result = '

\n' % self.h_level self.h_level = 0 else: self.h_level = len(word) - 1 - self.h_cnt += 1 - #abridged = re.sub('[^a-z_]', '', word.lower().replace(' ', '_')) - result = '¶ ' % (self.h_level, self.h_cnt, self.h_cnt) + link = permalink(self.line) + result = '\n

¶ ' % (self.h_level, link, link) return result def _br_repl(self, word): @@ -388,37 +325,76 @@ class PageFormatter: else: return '%s' % (word, word) - def _hurl_repl(self, word): - m = re.compile("\[\[([^ \t\n\r\f\v\|]+)(?:\s*\|\s*([^\]]+)|)\]\]").match(word) + def _macro_repl(self, word): + m = re.compile("\<\<([^\s\|\>]+)(?:\s*\|\s*([^\>]+)|)\>\>").match(word) name = m.group(1) - descr = m.group(2) or name + argv = [name] + if m.group(2): + argv.extend(m.group(2).split('|')) + argv = map(str.strip, argv) macro = globals().get('_macro_' + name) + if not macro: + execfile("macros/" + name + ".py", globals()) + macro = globals().get('_macro_' + name) if macro: - return macro(name, descr) - elif img_re.match(name): - name = relative_url(name) - # The "extthumb" nonsense works around a limitation of the HTML block model - return '
%s
%s
' % (name, name, descr, descr) + return macro(argv) + else: + return '<<' + '|'.join(argv) + '>>' + + def _hurl_repl(self, word): + m = link_re.match(word) + name = m.group(1) + if m.group(2) is None: + descr = name + elif img_re.match(m.group(2)): + descr = '' + else: + descr = m.group(2) + + return link_tag(name, descr, 'wikilink') + + def _inl_repl(self, word): + m = link_re.match(word) + name = m.group(1) + descr = m.group(2) or name + name = relative_url(name) + argv = descr.split('|') + descr = argv.pop(0) + + if argv: + args = '?' + '&'.join(argv) else: - if img_re.match(descr): - descr = '' + args = '' - return link_tag(name, descr, 'wikilink') + if descr: + # The "extthumb" nonsense works around a limitation of the HTML block model + return '
%s
%s
' \ + % (name, name + args, descr, descr) + else: + return '' % (name, name + args) def _email_repl(self, word): return '%s' % (word, word) def _html_repl(self, word): + self.in_html += 1 return word; # Pass through def _ent_repl(self, s): + if self.in_html and s == '>': + self.in_html -= 1 + return '>' return {'&': '&', '<': '<', '>': '>'}[s] def _li_repl(self, match): - return '
  • ' + if self.in_li: + return '
  • ' + else: + self.in_li = True + return '
  • ' def _pre_repl(self, word): if word == '{{{' and not self.in_pre: @@ -461,6 +437,9 @@ class PageFormatter: s = '

    ' while self._indent_level() > new_level: del(self.list_indents[-1]) + if self.in_li: + s += '
  • ' + self.in_li = False # FIXME s += '\n' while self._indent_level() < new_level: self.list_indents.append(new_level) @@ -495,20 +474,26 @@ class PageFormatter: + r"|(?P\={2,6})" + r"|(?P
    \\\\)" + r"|(?P^-{3,})" - + r"|(?P<(/|)(div|span|iframe)[^<>]*>)" - + r"|(?P[<>&])" + r"|(?P\b(FIXME|TODO|DONE)\b)" # Links - + r"|(?P\b[a-zA-Z0-9_-]+\.(png|gif|jpg|jpeg|bmp))" - + r"|(?P\b(?:[A-Z][a-z]+){2,}\b)" - + r"|(?P\[\[([^ \t\n\r\f\v\|]+)(?:\s*\|\s*([^\]]+)|)\]\])" - + r"|(?P(http|https|ftp|mailto)\:[^\s'\"]+\S)" - + r"|(?P[-\w._+]+\@[\w.-]+)" + + r"|(?P\<\<([^\s\|\>]+)(?:\s*\|\s*([^\>]+)|)\>\>)" + + r"|(?P\[\[([^\s\|]+)(?:\s*\|\s*([^\]]+)|)\]\])" + + # Inline HTML + + r"|(?P<(/|)(br|hr|div|form|iframe|input|span))" + + r"|(?P[<>&])" + + # Auto links + + r"|(?P\b[a-zA-Z0-9_/-]+\.(png|gif|jpg|jpeg|bmp))" # LEGACY + + r"|(?P\b(?:[A-Z][a-z]+){2,}\b)" # LEGACY + + r"|(?P(http|https|ftp|mailto)\:[^\s'\"]+\S)" # LEGACY + + r"|(?P[-\w._+]+\@[\w.-]+)" # LEGACY # Lists, divs, spans + r"|(?P
  • ^\s+[\*#] +)" + r"|(?P
    \{\{\{|\s*\}\}\})"
    +            + r"|(?P\{\{([^\s\|]+)(?:\s*\|\s*([^\]]+)|)\}\})" #TODO
     
                 # Tables
                 + r"|(?P^\s*\|\|(=|)\s*)"
    @@ -524,33 +509,32 @@ class PageFormatter:
             indent_re = re.compile(r"^\s*")
             tr_re = re.compile(r"^\s*\|\|")
             eol_re = re.compile(r"\r?\n")
    -        for line in eol_re.split(self.raw.expandtabs()):
    +        for self.line in eol_re.split(self.raw.expandtabs()):
                 # Skip ACLs
                 if self.in_header:
    -                if line.startswith('#'):
    +                if self.line.startswith('#'):
                         continue
                     self.in_header = False
     
                 if self.in_pre:
    -                print re.sub(pre_re, self.replace, line)
    +                print re.sub(pre_re, self.replace, self.line)
                 else:
    -                if self.in_table and not tr_re.match(line):
    +                if self.in_table and not tr_re.match(self.line):
                         self.in_table = False
                         print '

    ' - if blank_re.match(line): + if blank_re.match(self.line): print '

    ' else: - indent = indent_re.match(line) + indent = indent_re.match(self.line) print self._indent_to(len(indent.group(0))) - print re.sub(scan_re, self.replace, line) + print re.sub(scan_re, self.replace, self.line) if self.in_pre: print '

    ' if self.in_table: print '

    ' print self._undent() print '

    ' -# ---------------------------------------------------------- class Page: def __init__(self, page_name): self.page_name = page_name @@ -561,7 +545,7 @@ class Page: # look for the end of words and the start of a new word and insert a space there return re.sub('([a-z])([A-Z])', r'\1 \2', self.page_name) - def _text_filename(self): + def _filename(self): return path.join(data_dir, self.page_name) def _tmp_filename(self): @@ -569,7 +553,7 @@ class Page: def exists(self): try: - os.stat(self._text_filename()) + os.stat(self._filename()) return True except OSError, er: if er.errno == errno.ENOENT: @@ -585,20 +569,36 @@ class Page: def get_raw_body(self): try: - return open(self._text_filename(), 'rb').read() + return open(self._filename(), 'rb').read() except IOError, er: if er.errno == errno.ENOENT: return '' # just doesn't exist, use default if er.errno == errno.EISDIR: - return 'DIR' + return self.format_dir() raise er + def format_dir(self): + out = '== ' + path = '' + for dir in self.page_name.split('/'): + path = (path + '/' + dir) if path else dir + out += '[[' + path + '|' + dir + ']]/' + out += ' ==\n' + + for file in page_list(self._filename(), file_re): + if img_re.match(file): + if image_maxwidth: + maxwidth_arg = '|maxwidth=' + str(image_maxwidth) + out += '{{' + self.page_name + '/' + file + '|' + file + maxwidth_arg + '}}\n' + else: + out += ' * [[' + self.page_name + '/' + file + ']]\n' + return out def get_attrs(self): if 'attrs' in self.__dict__: return self.attrs self.attrs = {} try: - file = open(self._text_filename(), 'rt') + file = open(self._filename(), 'rt') attr_re = re.compile(r"^#(\S*)(.*)$") for line in file: m = attr_re.match(line) @@ -615,6 +615,7 @@ class Page: return self.get_attrs().get(name, default) def can(self, action, default=True): + acl = None try: #acl SomeUser:read,write All:read acl = self.get_attr("acl", None) @@ -634,7 +635,13 @@ class Page: def can_read(self): return self.can("read", True) - def send_page(self): + def send_naked(self): + if self.can_read(): + WikiFormatter(self.get_raw_body()).print_html() + else: + send_guru("Read access denied by ACLs", "notice") + + def format(self): page_name = None if self.can_write(): page_name = self.page_name @@ -644,16 +651,13 @@ class Page: css_url = self.get_attr("css", "").split() + css_url send_title(page_name, self.split_title(), msg_text=self.msg_text, msg_type=self.msg_type) - if self.can_read(): - PageFormatter(self.get_raw_body()).print_html() - else: - send_guru("Read access denied by ACLs", "notice") + self.send_naked() send_footer(page_name, self._last_modified()) def _last_modified(self): try: from time import localtime, strftime - modtime = localtime(os.stat(self._text_filename())[stat.ST_MTIME]) + modtime = localtime(os.stat(self._filename())[stat.ST_MTIME]) except OSError, er: if er.errno != errno.ENOENT: raise er @@ -689,7 +693,7 @@ class Page: print "

    " + Page('EditingTips').link_to() + "

    " if preview: print "
    " - PageFormatter(preview).print_html() + WikiFormatter(preview).print_html() print "
    " send_footer(self.page_name) @@ -701,10 +705,20 @@ class Page: else: send_title(None, msg_text='Read access denied by ACLs', msg_type='notice') + def send_image(self, mimetype, args=[]): + if 'maxwidth' in args: + import subprocess + emit_header(mimetype) + sys.stdout.flush() + subprocess.check_call(['gm', 'convert', self._filename(), + '-scale', args['maxwidth'].value + ' >', '-']) + else: + self.send_raw(mimetype) + def _write_file(self, data): tmp_filename = self._tmp_filename() open(tmp_filename, 'wb').write(data) - name = self._text_filename() + name = self._filename() if os.name == 'nt': # Bad Bill! POSIX rename ought to replace. :-( try: @@ -759,12 +773,19 @@ try: if file_re.match(query): if word_re.match(query): - Page(query).send_page() + Page(query).format() else: from mimetypes import MimeTypes type, encoding = MimeTypes().guess_type(query) - type = type or 'text/plain' - Page(query).send_raw(mimetype=type) + #type = type or 'text/plain' + #Page(query).send_raw(mimetype=type) + if type: + if type.startswith('image/'): + Page(query).send_image(mimetype=type,args=form) + else: + Page(query).send_raw(mimetype=type) + else: + Page(query).format() else: print "Status: 404 Not Found" send_title(None, msg_text='Can\'t work out query: ' + query)