X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=geekigeeki.py;h=7695228a895cb33c7d9720980022aaae891a9b8a;hb=46efe19f353d79e0be12da32980ef1457a97d749;hp=57992d288b93eb1774a2a82ce051dba49cfea5ab;hpb=10a9870db7d40c7375470061274902fc9f38f3a6;p=geekigeeki.git
diff --git a/geekigeeki.py b/geekigeeki.py
index 57992d2..79dda63 100755
--- a/geekigeeki.py
+++ b/geekigeeki.py
@@ -1,173 +1,258 @@
-#! /usr/bin/env python
-"""Quick-quick implementation of WikiWikiWeb in Python
-"""
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
#
-# Copyright (C) 1999, 2000 Martin Pool
-# This version includes additional changes by Gerardo Poggiali (2002)
-# This version includes additional changes by Bernardo Innocenti (2007)
+# Copyright 1999, 2000 Martin Pool
+# Copyright 2002 Gerardo Poggiali
+# Copyright 2007, 2008, 2009 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
# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# 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]
-
-import cgi, sys, string, os, re, errno, time, stat
-from os import path, environ
+# (at your option) any later version. You should have received a copy
+# of the GNU General Public License along with this program.
+# If not, see .
-# Regular expression defining a WikiWord
-# (but this definition is also assumed in other places)
-file_re = re.compile(r"^\b([A-Za-z0-9_\.\-]+)\b$")
-word_re = re.compile(r"^\b([A-Z][a-z]+){2,}\b$")
-img_re = re.compile(r"^.*\.(png|gif|jpg|jpeg)$", re.IGNORECASE)
-url_re = re.compile(r"^[a-z]{3,8}://[^\s'\"]+\S$")
+__version__ = '4.0-' + '$Id$'[4:11]
+from time import clock, localtime, gmtime, strftime
+start_time = clock()
title_done = False
+import cgi, sys, os, re, errno, stat, glob
+
+image_ext = 'png|gif|jpg|jpeg|bmp|ico'
+video_ext = "ogg|ogv|oga" # Not supported by Firefox 3.5: mkv|mpg|mpeg|mp4|avi|asf|flv|wmv|qt
+image_re = re.compile(r".*\.(" + image_ext + "|" + video_ext + ")", re.IGNORECASE)
+video_re = re.compile(r".*\.(" + video_ext + ")", re.IGNORECASE)
+# FIXME: we accept stuff like foo/../bar and we shouldn't
+file_re = re.compile(r"([A-Za-z0-9_\-][A-Za-z0-9_\.\-/]*)")
+url_re = re.compile(r"[a-z]{3,8}://[^\s'\"]+\S")
+ext_re = re.compile(r"\.([^\./]+)$")
# CGI stuff ---------------------------------------------------------
+def config_get(key, default=None):
+ return globals().get(key, default)
def script_name():
- return environ.get('SCRIPT_NAME', '')
+ return os.environ.get('SCRIPT_NAME', '')
+
+def script_path():
+ return os.path.split(os.environ.get('SCRIPT_FILENAME', ''))[0]
+
+def query_string():
+ path_info = os.environ.get('PATH_INFO', '')
+ if len(path_info) and path_info[0] == '/':
+ return path_info[1:] or 'FrontPage'
+ else:
+ return os.environ.get('QUERY_STRING', '') or 'FrontPage'
def privileged_path():
- return privileged_url or script_name()
+ return config_get('privileged_url') or script_name()
def remote_user():
- user = environ.get('REMOTE_USER', '')
+ user = os.environ.get('REMOTE_USER', '')
if user is None or user == '' or user == 'anonymous':
user = 'AnonymousCoward'
return user
def remote_host():
- return environ.get('REMOTE_ADDR', '')
+ return os.environ.get('REMOTE_ADDR', '')
def get_hostname(addr):
try:
from socket import gethostbyaddr
return gethostbyaddr(addr)[0] + ' (' + addr + ')'
- except:
+ except Exception:
return addr
-# Formatting stuff --------------------------------------------------
+def is_external_url(pathname):
+ return (url_re.match(pathname) or pathname.startswith('/'))
-def emit_header(type="text/html"):
- print "Content-type: " + type + "; charset=utf-8"
- print
+def relative_url(pathname, privileged=False):
+ if not is_external_url(pathname):
+ if privileged:
+ url = privileged_path()
+ else:
+ url = script_name()
+ pathname = url + '/' + pathname
+ return cgi.escape(pathname, quote=True)
+
+def permalink(s):
+ return re.sub(' ', '-', re.sub('[^a-z0-9_ ]', '', s.lower()).strip())
+
+def humanlink(s):
+ return re.sub(r'(?:.*[/:]|)([^:/\.]+)(?:\.[^/:]+|)$', r'\1', s.replace('_', ' '))
+
+# Split arg lists like "blah|blah blah| width=100 | align = center",
+# return a list containing anonymous arguments and a map containing the named arguments
+def parse_args(s):
+ args = []
+ kvargs = {}
+ for arg in s.strip('<[{}]>').split('|'):
+ m = re.match('\s*(\w+)\s*=\s*(.+)\s*', arg)
+ if m is not None:
+ kvargs[m.group(1)] = m.group(2)
+ else:
+ args.append(arg.strip())
+ return (args, kvargs)
-def send_guru(msg, msg_type):
- if msg is None or msg == '': return
- print '
' % (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():
- s = make_index_key()
- pages = list(page_list())
- pages.sort()
- current_letter = None
- for name in pages:
- letter = string.lower(name[0])
- if letter <> current_letter:
- s = s + '
\s*\|\|(=|)\s*)
+
+ # TODO: highlight search words (look at referrer)
+ )""", re.VERBOSE)
+ pre_re = re.compile("""(?:
+ (?P
\s*\}\}\})
+ | (?P[<>&])"
+ )""", re.VERBOSE)
blank_re = re.compile(r"^\s*$")
- indent_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
+ # For each line, we scan through looking for magic strings, outputting verbatim any intervening text
+ #3.0: for self.line in eol_re.split(str(self.raw.expandtabs(), 'utf-8')):
+ for self.line in eol_re.split(str(self.raw.expandtabs())):
+ # Skip pragmas
if self.in_header:
- if line.startswith('#'):
- continue
+ 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 '
'
+ print('
')
- if blank_re.match(line):
- print '
'
+ if blank_re.match(self.line):
+ print('
')
else:
- indent = indent_re.match(line)
- print self._indent_to(len(indent.group(0)))
- print re.sub(scan_re, self.replace, line)
+ indent = indent_re.match(self.line)
+ print(self._indent_to(len(indent.group(1)), indent.group(2)))
+ # Stand back! Here we apply the monster regex that does all the parsing
+ print(re.sub(scan_re, self.replace, self.line))
- if self.in_pre: print ''
- if self.in_table: print '
'
- print self._undent()
- print "
"
+ if self.in_pre: print('')
+ if self.in_table: print('
')
+ print(self._indent_to(0))
+ print('
')
-# ----------------------------------------------------------
class Page:
def __init__(self, page_name):
self.page_name = page_name
- self.msg = ''
+ self.msg_text = ''
self.msg_type = 'error'
- self.attrs = {}
def split_title(self):
- # look for the end of words and the start of a new word,
- # and insert a space there
+ # 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):
- return path.join(text_dir, self.page_name)
+ def _filename(self):
+ return self.page_name
def _tmp_filename(self):
- return path.join(text_dir, ('#' + self.page_name + '.' + `os.getpid()` + '#'))
+ return self.page_name + '.tmp' + str(os.getpid()) + '#'
- def exists(self):
+ def _mtime(self):
try:
- os.stat(self._text_filename())
- return 1
- except OSError, er:
- if er.errno == errno.ENOENT:
- return 0
- else:
- raise er
-
- def link_to(self):
- word = self.page_name
- 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 os.stat(self._filename()).st_mtime
+ except OSError, err:
+ if err.errno == errno.ENOENT:
+ return None
+ raise err
+ def exists(self):
+ if self._mtime():
+ return True
+ return False
- def get_raw_body(self):
+ def get_raw_body(self, default=None):
try:
- return open(self._text_filename(), 'rt').read()
- except IOError, er:
- if er.errno == errno.ENOENT:
- # just doesn't exist, use default
- return 'Describe %s here.' % self.page_name
+ return open(self._filename(), 'rb').read()
+ except IOError, err:
+ if err.errno == errno.ENOENT:
+ if default is None:
+ default = '//[[?a=edit&q=%s|Describe %s]]//' % (self.page_name, self.page_name)
+ return default
+ if err.errno == errno.EISDIR:
+ return self.format_dir()
+ raise err
+
+ def format_dir(self):
+ out = '== '
+ pathname = ''
+ for dirname in self.page_name.strip('/').split('/'):
+ pathname = (pathname and pathname + '/' ) + dirname
+ out += '[[' + pathname + '|' + dirname + ']]/'
+ out += ' ==\n'
+
+ for filename in page_list(self._filename(), file_re):
+ if image_re.match(filename):
+ maxwidth = config_get('image_maxwidth', '400')
+ if maxwidth:
+ maxwidth = ' | maxwidth=' + str(maxwidth)
+ out += '{{' + self.page_name + '/' + filename + ' | ' + humanlink(filename) + maxwidth + ' | class=thumbleft}}\n'
else:
- raise er
+ out += ' * [[' + self.page_name + '/' + filename + ']]\n'
+ return out
- def get_attrs(self):
- if self.attrs:
- return self.attrs
- try:
- file = open(self._text_filename(), 'rt')
- attr_re = re.compile(r"^#(\S*)(.*)$")
- for line in file:
- m = attr_re.match(line)
- if not m:
- break
- self.attrs[m.group(1)] = m.group(2).strip()
- #print "bernie: attrs[" + m.group(1) + "] = " + m.group(2) + " \n"
- except IOError, er:
- if er.errno != errno.ENOENT:
- raise er
- return self.attrs
-
- def can_edit(self):
- attrs = self.get_attrs()
+ def pragmas(self):
+ if not '_pragmas' in self.__dict__:
+ self._pragmas = {}
+ try:
+ file = open(self._filename(), 'rt')
+ attr_re = re.compile(r"^#(\S*)(.*)$")
+ for line in file:
+ m = attr_re.match(line)
+ if not m:
+ break
+ self._pragmas[m.group(1)] = m.group(2).strip()
+ #print "bernie: pragmas[" + m.group(1) + "] = " + m.group(2) + " \n"
+ except IOError, err:
+ if err.errno != errno.ENOENT and err.errno != errno.EISDIR:
+ raise er
+ return self._pragmas
+
+ def pragma(self, name, default):
+ return self.pragmas().get(name, default)
+
+ def can(self, action, default=True):
+ acl = None
try:
- # SomeUser:read,write All:read
- acl = attrs["acl"]
+ #acl SomeUser:read,write All:read
+ acl = self.pragma("acl", None)
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(','):
- return True
+ return action in perms.split(',')
return False
- except:
- pass
- return True
-
- def send_page(self):
- page_name = None
- if self.can_edit():
- 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()
- send_footer(page_name, self._last_modified())
-
- def _last_modified(self):
- if not self.exists():
- return None
- from time import localtime, strftime
- modtime = localtime(os.stat(self._text_filename())[stat.ST_MTIME])
- return strftime(datetime_fmt, modtime)
+ except Exception:
+ if acl:
+ self.msg_text = 'Illegal acl line: ' + acl
+ return default
+
+ def can_write(self):
+ return self.can("write", True)
+
+ def can_read(self):
+ return self.can("read", True)
+
+ def send_naked(self, kvargs=None):
+ if self.can_read():
+ WikiFormatter(self.get_raw_body(), kvargs).print_html()
+ else:
+ send_guru("Read access denied by ACLs", "notice")
+
+ def format(self):
+ #css foo.css
+ value = self.pragma("css", None)
+ if value:
+ global link_urls
+ link_urls += [ [ "stylesheet", value ] ]
+
+ send_title(self.page_name, self.split_title(),
+ msg_text=self.msg_text, msg_type=self.msg_type, writable=self.can_write(), mtime=self._mtime())
+ self.send_naked()
+ send_footer(mtime=self._mtime())
def send_editor(self, preview=None):
- send_title(None, 'Edit ' + self.split_title(), msg=self.msg, msg_type=self.msg_type)
-
- print ('