X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=geekigeeki.py;h=e6cfeca70ef5501177e27af3711418b1636cb81c;hb=05e3e729c0e20765f418c5efd35ab4426afb2fb7;hp=116b03b480911485162d8c6bacc49b3cddd1f523;hpb=1c33760fee962c9aa3b77ac6b354485b377e3d9e;p=geekigeeki.git diff --git a/geekigeeki.py b/geekigeeki.py index 116b03b..e6cfeca 100755 --- a/geekigeeki.py +++ b/geekigeeki.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__version__ = '$Revision: 1.63+gerry+bernie $'[11:-2] +__version__ = '$Id$'[3:-2] import cgi, sys, string, os, re, errno, time, stat from os import path, environ @@ -107,7 +107,7 @@ def send_title(name, text="Limbo", msg=None, msg_type='error'): """ print "%s: %s" % (site_name, text) - print ' ' + print ' ' if not name: print ' ' if css_url: @@ -154,8 +154,7 @@ def link_tag(params, text=None, ss_class=None, authentication=False): classattr = '' if ss_class: classattr += 'class="%s" ' % ss_class - # Prevent crawlers from following links to generated pages - # and links added by potential spammers + # Prevent crawlers from following links potentially added by spammers or to generated pages if ss_class == 'external' or ss_class == 'navlink': classattr += 'rel="nofollow" ' if authentication: @@ -220,9 +219,6 @@ def do_raw(pagename): Page(pagename).send_raw() def do_savepage(pagename): - if privileged_url is None: - raise 'editing disallowed for ' + pagename - global form pg = Page(pagename) if 'preview' in form: @@ -231,7 +227,7 @@ def do_savepage(pagename): pg.save_text(form['savetext'].value) pg.send_page() elif 'cancel' in form: - pg.msg = 'Editing cancelled' + pg.msg = 'Editing canceled' pg.msg_type = 'notice' pg.send_page() else: @@ -255,8 +251,11 @@ def send_footer(name, mod_string=None): cgi.print_arguments() cgi.print_form(cgi.FieldStorage()) cgi.print_environ() + global __version__ print '' @@ -264,10 +263,10 @@ def send_footer(name, mod_string=None): # ---------------------------------------------------------- # Macros -def _macro_TitleSearch(): +def _macro_TitleSearch(*vargs): return _macro_search("titlesearch") -def _macro_FullSearch(): +def _macro_FullSearch(*vargs): return _macro_search("fullsearch") def _macro_search(type): @@ -275,9 +274,9 @@ def _macro_search(type): default = form["value"].value else: default = '' - return """
""" % (type, default) + return """
""" % (type, default) -def _macro_WordIndex(): +def _macro_WordIndex(*vargs): s = make_index_key() pages = list(page_list()) map = {} @@ -310,7 +309,7 @@ def _macro_WordIndex(): return s -def _macro_TitleIndex(): +def _macro_TitleIndex(*vargs): s = make_index_key() pages = list(page_list()) pages.sort() @@ -337,98 +336,124 @@ class PageFormatter: self.raw = raw self.is_em = self.is_b = 0 self.h_level = 0 + self.h_count = 0 self.list_indents = [] - self.in_pre = 0 - self.in_var = 0 + self.in_pre = False + self.in_table = False + self.tr_cnt = 0 + self.in_var = False self.in_header = True def _emph_repl(self, word): if len(word) == 3: self.is_b = not self.is_b - return ['', ''][self.is_b] + return ['', ''][self.is_b] else: self.is_em = not self.is_em return ['', ''][self.is_em] def _tit_repl(self, word): if self.h_level: - result = "" % self.h_level + result = '' % self.h_level self.h_level = 0 else: self.h_level = len(word) - 1 - result = "" % self.h_level + self.h_count += 1 + result = '* ' % (self.h_level, self.h_count, self.h_count) return result def _rule_repl(self, word): - s = self._undent() - if len(word) <= 3: - s = s + "\n
\n" - else: - s = s + "\n
\n" % (len(word) - 2 ) - return s + return self._undent() + '\n
\n' % (len(word) - 2) def _word_repl(self, word): return Page(word).link_to() def _img_repl(self, word): - return '' % (script_name(), word) + path = script_name() + '/' + word; + return '' % (path, path) def _url_repl(self, word): if img_re.match(word): - return '' % word + return '' % (word, word) else: return '%s' % (word, word) def _hurl_repl(self, word): - m = re.compile("\[\[(\S+)\ (.+)\]\]").match(word) - anchor = m.group(1) - descr = m.group(2) - if img_re.match(anchor): - return '%s' % (anchor, descr) - elif url_re.match(anchor): - return '%s' % (anchor, descr) - elif anchor.startswith('/'): - return '%s' % (anchor, descr) + m = re.compile("\[\[(\S+)([^\]]*)\]\]").match(word) + name = m.group(1) + descr = m.group(2).strip() or name + + macro = globals().get('_macro_' + name) + if macro: + return apply(macro, (name, descr)) + elif img_re.match(name): + return '%s' % (name, name, descr) + elif url_re.match(name): + return '%s' % (name, descr) + elif name.startswith('/'): + return '%s' % (name, descr) else: - return link_tag(anchor, descr) + return link_tag(name, descr) def _email_repl(self, word): return '%s' % (word, word) + def _html_repl(self, word): + return word; # Pass through def _ent_repl(self, s): return {'&': '&', '<': '<', '>': '>'}[s] - def _li_repl(self, match): return '
  • ' - def _pre_repl(self, word): if word == '{{{' and not self.in_pre: - self.in_pre = 1 + self.in_pre = True return '
    '
             elif self.in_pre:
    -            self.in_pre = 0
    +            self.in_pre = False
                 return '
    ' - else: - return '' + return '' + + def _hi_repl(self, word): + if word == 'FIXME': + cl = 'error' + elif word == 'DONE': + cl = 'success' + elif word == 'TODO': + cl = 'notice' + return '' + word + '' def _var_repl(self, word): if word == '{{' and not self.in_var: - self.in_var = 1 + self.in_var = True return '' elif self.in_var: - self.in_var = 0 + self.in_var = False return '' - else: - return '' - def _macro_repl(self, word): - macro_name = word[2:-2] - # TODO: Somehow get the default value into the search field - return apply(globals()['_macro_' + macro_name], ()) + return '' + + def _tr_repl(self, word): + out = '' + if not self.in_table: + self.in_table = True + self.tr_cnt = 0 + out = '

    \n' + self.tr_cnt += 1 + return out + '' + return '' + + def _td_repl(self, word): + if self.in_table: + return '^\s*\|\|\s*)" + + r"|(?P\s*\|\|\s*$)" + + r"|(?P
    ' + + def _tre_repl(self, word): + if self.in_table: + return '
    ' + return '' def _indent_level(self): return len(self.list_indents) and self.list_indents[-1] @@ -461,33 +486,46 @@ class PageFormatter: raise "Can't handle match " + `match` def print_html(self): - print "

    " + print '

    ' # For each line, we scan through looking for magic # strings, outputting verbatim any intervening text + # TODO: highlight search words (look at referer) scan_re = re.compile( r"(?:" + # Formatting + r"(?P'{2,3})" + r"|(?P\={2,6})" + + 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^-{3,})" - + r"|(?P\[\[\S+\s+.+\]\])" - + r"|(?P(http|ftp|nntp|news|mailto)\:[^\s'\"]+\S)" + + r"|(?P\[\[\S+[^\]]*\]\])" + + r"|(?P(http|https|ftp|mailto)\:[^\s'\"]+\S)" + r"|(?P[-\w._+]+\@[\w.-]+)" + + # Lists, divs, spans + r"|(?P

  • ^\s+\*)" + r"|(?P
    (\{\{\{|\s*\}\}\}))"
                 + r"|(?P(\{\{|\}\}))"
    -            + r"|(?P\[\[(TitleSearch|FullSearch|WordIndex|TitleIndex)\]\])"
    +
    +            # Tables
    +            + r"|(?P
  • \s*\|\|\s*)" + r")") pre_re = re.compile( r"(?:" + r"(?P
    \s*\}\}\})"
                 + r")")
    -        blank_re = re.compile("^\s*$")
    -        indent_re = re.compile("^\s*")
    -        eol_re = re.compile(r'\r?\n')
    +        blank_re = re.compile(r"^\s*$")
    +        indent_re = re.compile(r"^\s*")
    +        tr_re = re.compile(r"^\s*\|\|")
    +        eol_re = re.compile(r"\r?\n")
             raw = string.expandtabs(self.raw)
             for line in eol_re.split(raw):
                 # Skip ACLs
    @@ -495,19 +533,25 @@ class PageFormatter:
                     if line.startswith('#'):
                        continue
                     self.in_header = False
    +
                 if self.in_pre:
                     print re.sub(pre_re, self.replace, line)
                 else:
    -                # XXX: Should we check these conditions in this order?
    +                if self.in_table and not tr_re.match(line):
    +                    self.in_table = False
    +                    print '

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

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

    ' print self._undent() - print "

    " + print '

    ' # ---------------------------------------------------------- class Page: @@ -543,11 +587,7 @@ class Page: if self.exists(): return link_tag(word, word, 'wikilink') else: - if nonexist_qm: - return link_tag(word, '?', 'nonexistent') + word - else: - return link_tag(word, word, 'nonexistent') - + return link_tag(word, nonexist_pfx + word, 'nonexistent') def get_raw_body(self): try: @@ -576,27 +616,38 @@ class Page: raise er return self.attrs - def can_edit(self): + def can(self, action, default=True): attrs = self.get_attrs() try: # SomeUser:read,write All:read acl = attrs["acl"] for rule in acl.split(): - (user,perms) = acl.split(':') + (user,perms) = rule.split(':') if user == remote_user() or user == "All": - if 'write' in perms.split(','): + if action in perms.split(','): return True + else: + return False return False - except: + except Exception, er: pass - return True + return default + + def can_write(self): + return self.can("write", True) + + def can_read(self): + return self.can("read", True) def send_page(self): page_name = None - if self.can_edit(): + if self.can_write(): page_name = self.page_name send_title(page_name, self.split_title(), msg=self.msg, msg_type=self.msg_type) - PageFormatter(self.get_raw_body()).print_html() + if self.can_read(): + PageFormatter(self.get_raw_body()).print_html() + else: + send_guru("Read access denied by ACLs", "notice") send_footer(page_name, self._last_modified()) def _last_modified(self): @@ -608,6 +659,9 @@ class Page: def send_editor(self, preview=None): send_title(None, 'Edit ' + self.split_title(), msg=self.msg, msg_type=self.msg_type) + if not self.can_write(): + send_guru("Write access denied by ACLs", "error") + return print ('

    Editing ' + self.page_name + ' for ' + cgi.escape(remote_user()) @@ -632,6 +686,9 @@ class Page: send_footer(self.page_name) def send_raw(self): + if not self.can_read(): + send_title(None, msg='Read access denied by ACLs', msg_type='notice') + return emit_header("text/plain") print self.get_raw_body() @@ -648,6 +705,11 @@ class Page: os.rename(tmp_filename, text) def save_text(self, newtext): + if not self.can_write(): + self.msg = 'Write access denied by ACLs' + self.msg_type = 'error' + return + self._write_file(newtext) rc = 0 if post_edit_hook: @@ -666,7 +728,7 @@ class Page: if msg: self.msg += 'Output follows:\n' + msg else: - self.msg = 'Thankyou for your contribution. Your attention to detail is appreciated.' + self.msg = 'Thank you for your contribution. Your attention to detail is appreciated.' self.msg_type = 'success' def send_verbatim(filename, mime_type='application/octet-stream'): @@ -680,8 +742,7 @@ try: # Configuration values site_name = 'Codewiz' - # set to None for read-only sites - # leave empty ('') to allow anonymous edits + # set to None for read-only sites, leave empty ('') to allow anonymous edits # otherwise, set to a URL that requires authentication privileged_url = 'https://www.codewiz.org/~bernie/wiki' @@ -693,7 +754,7 @@ try: datetime_fmt = '%a %d %b %Y %I:%M %p' allow_edit = True # Is it possible to edit pages? show_hosts = True # show hostnames? - nonexist_qm = False # show '?' for nonexistent? + nonexist_pfx = '' # prefix before nonexistent link (usually '?') debug_cgi = False # Set to True for CGI var dump form = cgi.FieldStorage()