changelog shortlog tags files raw

changeset: Initial import.

changeset 0: c3bc2c8ad67d
child 1:8f76c0b58e7a
author: Gregor Richards <Richards@codu.org>
date: Tue Nov 17 16:30:13 2009 +0000 (2 years ago)
files: bin/.wiki bin/index bin/list bin/medit bin/raw bin/runner bin/view bin/wikisyntax lib/python/creole.py lib/python/creole.pyc/empty lib/python/hackikilogin.py templates/edit.html templates/wiki.css templates/wiki.html
description: Initial import.
       1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       2+++ b/bin/.wiki	Tue Nov 17 16:30:13 2009 +0000
       3@@ -0,0 +1,329 @@
       4+#!/usr/bin/env python
       5+# -*- coding: utf-8 -*-
       6+
       7+import os
       8+import subprocess
       9+import sys
      10+sys.path.append("/hackiki/lib/python")
      11+
      12+ur"""
      13+WikiCreole to HTML converter
      14+This program is an example of how the creole.py WikiCreole parser
      15+can be used.
      16+Modified for use with Hackiki
      17+
      18+@copyright: 2007 MoinMoin:RadomirDopieralski
      19+@license: BSD, see COPYING for details.
      20+
      21+Test cases contributed by Jan Klopper (janklopper@underdark.nl),
      22+modified by Radomir Dopieralski (MoinMoin:RadomirDopieralski).
      23+
      24+>>> import lxml.html.usedoctest
      25+>>> def parse(text):
      26+...     print HtmlEmitter(Parser(text).parse()).emit()
      27+
      28+>>> parse(u'test')
      29+<p>test</p>
      30+
      31+>>> parse(u'test\ntest')
      32+<p>test test</p>
      33+
      34+>>> HtmlEmitter(Parser(u'test\ntest').parse()).emit()
      35+u'<p>test test</p>\n'
      36+
      37+>>> parse(u'test\n\ntest')
      38+<p>test</p><p>test</p>
      39+
      40+>>> parse(u'test\\\\test')
      41+<p>test<br>test</p>
      42+
      43+>>> parse(u'----')
      44+<hr>
      45+
      46+>>> parse(u'==test==')
      47+<h2>test</h2>
      48+
      49+>>> parse(u'== test')
      50+<h2>test</h2>
      51+
      52+>>> parse(u'==test====')
      53+<h2>test</h2>
      54+
      55+>>> parse(u'=====test')
      56+<h5>test</h5>
      57+
      58+>>> parse(u'==test==\ntest\n===test===')
      59+<h2>test</h2>
      60+<p>test</p>
      61+<h3>test</h3>
      62+
      63+>>> parse(u'test\n* test line one\n * test line two\ntest\n\ntest')
      64+<p>test</p>
      65+<ul>
      66+    <li>test line one</li>
      67+    <li>test line two test</li>
      68+</ul>
      69+<p>test</p>
      70+
      71+>>> parse(u'* test line one\n* test line two\n** Nested item')
      72+<ul>
      73+    <li>test line one</li>
      74+    <li>test line two<ul>
      75+        <li>Nested item</li>
      76+    </ul></li>
      77+</ul>
      78+
      79+>>> parse(u'* test line one\n* test line two\n# Nested item')
      80+<ul>
      81+    <li>test line one</li>
      82+    <li>test line two<ol>
      83+        <li>Nested item</li>
      84+    </ol></li>
      85+</ul>
      86+
      87+>>> parse(u'test //test test// test **test test** test')
      88+<p>test <i>test test</i> test <b>test test</b> test</p>
      89+
      90+>>> parse(u'test //test **test// test** test')
      91+<p>test <i>test <b>test<i> test<b> test</b></i></b></i></p>
      92+
      93+>>> parse(u'**test')
      94+<p><b>test</b></p>
      95+
      96+>>> parse(u'|x|y|z|\n|a|b|c|\n|d|e|f|\ntest')
      97+<table>
      98+    <tr><td>x</td><td>y</td><td>z</td></tr>
      99+    <tr><td>a</td><td>b</td><td>c</td></tr>
     100+    <tr><td>d</td><td>e</td><td>f</td></tr>
     101+</table>
     102+<p>test</p>
     103+
     104+>>> parse(u'|=x|y|=z=|\n|a|b|c|\n|d|e|f|')
     105+<table>
     106+    <tr><th>x</th><td>y</td><th>z</th></tr>
     107+    <tr><td>a</td><td>b</td><td>c</td></tr>
     108+    <tr><td>d</td><td>e</td><td>f</td></tr>
     109+</table>
     110+
     111+>>> parse(u'test http://example.com/test test')
     112+<p>test <a href="http://example.com/test">http://example.com/test</a> test</p>
     113+
     114+>>> parse(u'http://example.com/,test, test')
     115+<p><a href="http://example.com/,test">http://example.com/,test</a>, test</p>
     116+
     117+>>> parse(u'(http://example.com/test)')
     118+<p>(<a href="http://example.com/test">http://example.com/test</a>)</p>
     119+
     120+XXX This might be considered a bug, but it's impossible to detect in general.
     121+>>> parse(u'http://example.com/(test)')
     122+<p><a href="http://example.com/(test">http://example.com/(test</a>)</p>
     123+
     124+>>> parse(u'http://example.com/test?test&test=1')
     125+<p><a href="http://example.com/test?test&amp;test=1">http://example.com/test?test&amp;test=1</a></p>
     126+
     127+>>> parse(u'~http://example.com/test')
     128+<p>http://example.com/test</p>
     129+
     130+>>> parse(u'http://example.com/~test')
     131+<p><a href="http://example.com/~test">http://example.com/~test</a></p>
     132+
     133+>>> parse(u'[[test]] [[tset|test]]')
     134+<p><a href="test">test</a> <a href="tset">test</a></p>
     135+
     136+>>> parse(u'[[http://example.com|test]]')
     137+<p><a href="http://example.com">test</a></p>
     138+"""
     139+
     140+import re
     141+from creole import Parser
     142+from hackikilogin import loginInfo
     143+
     144+class Rules:
     145+    # For the link targets:
     146+    proto = r'http|https|ftp|nntp|news|mailto|telnet|file|irc'
     147+    extern = r'(?P<extern_addr>(?P<extern_proto>%s):.*)' % proto
     148+    interwiki = r'''
     149+            (?P<inter_wiki> [A-Z][a-zA-Z]+ ) :
     150+            (?P<inter_page> .* )
     151+        '''
     152+
     153+title = ""
     154+
     155+class HtmlEmitter:
     156+    """
     157+    Generate HTML output for the document
     158+    tree consisting of DocNodes.
     159+    """
     160+
     161+    addr_re = re.compile('|'.join([
     162+            Rules.extern,
     163+            Rules.interwiki,
     164+        ]), re.X | re.U) # for addresses
     165+
     166+    def __init__(self, root):
     167+        self.root = root
     168+
     169+    def get_text(self, node):
     170+        """Try to emit whatever text is in the node."""
     171+
     172+        try:
     173+            return node.children[0].content or ''
     174+        except:
     175+            return node.content or ''
     176+
     177+    def html_escape(self, text):
     178+        return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
     179+
     180+    def attr_escape(self, text):
     181+        return self.html_escape(text).replace('"', '&quot')
     182+
     183+    # *_emit methods for emitting nodes of the document:
     184+
     185+    def document_emit(self, node):
     186+        return self.emit_children(node)
     187+
     188+    def text_emit(self, node):
     189+        return node.content
     190+
     191+    def separator_emit(self, node):
     192+        return u'<hr>';
     193+
     194+    def paragraph_emit(self, node):
     195+        return u'<p>%s</p>\n' % self.emit_children(node)
     196+
     197+    def bullet_list_emit(self, node):
     198+        return u'<ul>\n%s</ul>\n' % self.emit_children(node)
     199+
     200+    def number_list_emit(self, node):
     201+        return u'<ol>\n%s</ol>\n' % self.emit_children(node)
     202+
     203+    def list_item_emit(self, node):
     204+        return u'<li>%s</li>\n' % self.emit_children(node)
     205+
     206+    def table_emit(self, node):
     207+        return u'<table>\n%s</table>\n' % self.emit_children(node)
     208+
     209+    def table_row_emit(self, node):
     210+        return u'<tr>%s</tr>\n' % self.emit_children(node)
     211+
     212+    def table_cell_emit(self, node):
     213+        return u'<td>%s</td>' % self.emit_children(node)
     214+
     215+    def table_head_emit(self, node):
     216+        return u'<th>%s</th>' % self.emit_children(node)
     217+
     218+    def emphasis_emit(self, node):
     219+        return u'<i>%s</i>' % self.emit_children(node)
     220+
     221+    def strong_emit(self, node):
     222+        return u'<b>%s</b>' % self.emit_children(node)
     223+
     224+    def header_emit(self, node):
     225+        global title
     226+        if node.level == 1:
     227+            title = node.content
     228+            return ""
     229+        return u'<h%d>%s</h%d>\n' % (
     230+            node.level, node.content, node.level)
     231+
     232+    def code_emit(self, node):
     233+        return u'<tt>%s</tt>' % node.content
     234+
     235+    def link_emit(self, node):
     236+        target = node.content
     237+        if node.children:
     238+            inside = self.emit_children(node)
     239+        else:
     240+            inside = target
     241+        m = self.addr_re.match(target)
     242+        if m:
     243+            if m.group('extern_addr'):
     244+                return u'<a href="%s">%s</a>' % (
     245+                    self.attr_escape(target), inside)
     246+            elif m.group('inter_wiki'):
     247+                raise NotImplementedError
     248+        return u'<a href="%s/%s">%s</a>' % (
     249+            os.environ["HACKIKI_BASE"], self.attr_escape(target), inside)
     250+
     251+    def image_emit(self, node):
     252+        target = node.content
     253+        text = self.get_text(node)
     254+        m = self.addr_re.match(target)
     255+        if m:
     256+            if m.group('extern_addr'):
     257+                return u'<img src="%s" alt="%s" />' % (
     258+                    self.attr_escape(target), self.attr_escape(text))
     259+            elif m.group('inter_wiki'):
     260+                raise NotImplementedError
     261+        return u'<img src="%s/%s" alt="%s" />' % (
     262+            os.environ["HACKIKI_BASE"], self.attr_escape(target), self.attr_escape(text))
     263+
     264+    def macro_emit(self, node):
     265+        raise NotImplementedError
     266+
     267+    def break_emit(self, node):
     268+        return u"<br>"
     269+
     270+    def preformatted_emit(self, node):
     271+        if node.content[:3] == "#!/":
     272+            # Hm, did they want me to RUN this code? Sure, sounds like fun!
     273+            nl = node.content.find('\n')
     274+            interp = node.content[2:nl]
     275+            code = node.content[nl+1:]
     276+
     277+            cmd = subprocess.Popen(interp, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     278+            cmd.stdin.write(code)
     279+            ret = cmd.communicate()[0]
     280+            cmd.stdin.close()
     281+            return ret
     282+        else:
     283+            return u"<pre>%s</pre>" % node.content
     284+
     285+    def default_emit(self, node):
     286+        """Fallback function for emitting unknown nodes."""
     287+
     288+        raise TypeError
     289+
     290+    def emit_children(self, node):
     291+        """Emit all the children of a node."""
     292+
     293+        return u''.join([self.emit_node(child) for child in node.children])
     294+
     295+    def emit_node(self, node):
     296+        """Emit a single node."""
     297+
     298+        emit = getattr(self, '%s_emit' % node.kind, self.default_emit)
     299+        return emit(node)
     300+
     301+    def emit(self):
     302+        """Emit the document represented by self.root DOM tree."""
     303+
     304+        return self.emit_node(self.root)
     305+
     306+if __name__=="__main__":
     307+    fn = sys.argv[1]
     308+    name = os.path.basename(fn)
     309+    fcont = open(sys.argv[1], "r").read()
     310+
     311+    # Remove a chebang line
     312+    if fcont[:3] == "#!/":
     313+        fcont = fcont[fcont.find('\n')+1:]
     314+
     315+    # parse
     316+    title = name
     317+    document = Parser(unicode(fcont, 'utf-8', 'ignore')).parse()
     318+    text = HtmlEmitter(document).emit().encode('utf-8', 'ignore')
     319+  
     320+    # get login info
     321+    login = loginInfo()
     322+
     323+    # now put it in the template
     324+    template = open("templates/wiki.html", "r").read()
     325+    sys.stdout.write(template % {
     326+        'hackikibase': os.environ["HACKIKI_BASE"],
     327+        'login': login,
     328+        'fn': fn,
     329+        'name': name,
     330+        'title': title,
     331+        'text': text
     332+    })
     1.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2+++ b/bin/index	Tue Nov 17 16:30:13 2009 +0000
     1.3@@ -0,0 +1,30 @@
     1.4+#!/usr/bin/env .wiki
     1.5+= Hackiki
     1.6+
     1.7+This is a wiki powered by [[http://hackiki.codu.org/|Hackiki]], the world's most hackable wiki!
     1.8+
     1.9+It also has [[wikisyntax]], although pages using this syntax are really just scripts for the ".wiki" program, which is really just an implementation of [[http://wikicreole.org/wiki/Creole1.0|WikiCreole]]. Like everything else, [[edit/bin/.wiki|.wiki]] can be edited by anyone!
    1.10+
    1.11+All content on this wiki is distributed under the (unmodifiable) license in [[license]] unless otherwise specified. By editing this wiki without specifying another license, you are agreeing to license your edits as such.
    1.12+
    1.13+
    1.14+== Editing
    1.15+
    1.16+The simple interface:
    1.17+{{{
    1.18+#!/bin/sh
    1.19+echo '<form action="'$HACKIKI_BASE'/medit" method="get">'
    1.20+}}}
    1.21+    <input type="hidden" name="arg1" value="bin" />
    1.22+    Create/edit a page: <input type="text" name="arg2" value="hello" />
    1.23+    <input type="submit" value="Edit" />
    1.24+</form>
    1.25+
    1.26+The more complicated (but more complete, robust) interface: [[edit|Builtin editor]]
    1.27+
    1.28+
    1.29+== Links
    1.30+
    1.31+* [[runner | An arbitrary command runner]]
    1.32+* [[list | A list of all pages]]
    1.33+* [[hg | Administration]] (available to all users)
     2.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2+++ b/bin/list	Tue Nov 17 16:30:13 2009 +0000
     2.3@@ -0,0 +1,19 @@
     2.4+#!/usr/bin/env .wiki
     2.5+= Hackiki page list
     2.6+
     2.7+<ul>
     2.8+{{{
     2.9+#!/bin/bash
    2.10+ls -1a bin | awk '/.+/ { print "<li><a href=\"" $0 "\">" $0 "</a> <a href=\"'$HACKIKI_BASE'/medit/bin/" $0 "\">[edit]</a></li>" }'
    2.11+}}}
    2.12+</ul>
    2.13+
    2.14+{{{
    2.15+#!/bin/sh
    2.16+echo '<form action="'$HACKIKI_BASE'/edit" method="get">'
    2.17+}}}
    2.18+    New page:
    2.19+    <input type="hidden" name="arg1" value="bin" />
    2.20+    <input type="text" name="arg2" />
    2.21+    <input type="submit" value="Create" />
    2.22+</form>
     3.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2+++ b/bin/medit	Tue Nov 17 16:30:13 2009 +0000
     3.3@@ -0,0 +1,77 @@
     3.4+#!/usr/bin/python
     3.5+# medit, the metaeditor
     3.6+
     3.7+import re
     3.8+import os
     3.9+import sys
    3.10+import cgi
    3.11+
    3.12+sys.path.append("/hackiki/lib/python")
    3.13+from hackikilogin import loginInfo
    3.14+
    3.15+# Get the file
    3.16+fn = ""
    3.17+if len(sys.argv) == 1 or not sys.argv[1]:
    3.18+  fn = "bin/index"
    3.19+else:
    3.20+  fn = '/'.join(sys.argv[1:])
    3.21+
    3.22+# And the template file
    3.23+template = "Failed to open templates/edit.html"
    3.24+try:
    3.25+  f = open("templates/edit.html", "r")
    3.26+  template = f.read()
    3.27+finally:
    3.28+  f.close()
    3.29+
    3.30+message = ""
    3.31+
    3.32+# Perhaps write data
    3.33+if ("REQUEST_chebang" in os.environ) and ("REQUEST_cont" in os.environ):
    3.34+  try:
    3.35+    f = open(fn,"w")
    3.36+    cont = re.sub(r'\r', '', os.environ["REQUEST_cont"])
    3.37+    f.write("%s\n%s"%(os.environ["REQUEST_chebang"], cont))
    3.38+  finally:
    3.39+    f.close()
    3.40+  os.chmod(fn, 0755)
    3.41+  message = "<br/>File %s written.<hr/>" % (fn)
    3.42+
    3.43+# Now read it in
    3.44+chebang = ""
    3.45+text = ""
    3.46+if os.path.isfile(fn):
    3.47+  try:
    3.48+    f = open(fn,"r")
    3.49+    text = f.read()
    3.50+  finally:
    3.51+    f.close()
    3.52+else:
    3.53+  chebang = "#!/usr/bin/env .wiki"
    3.54+
    3.55+if text[:3] == '#!/':
    3.56+  nl = text.find('\n')
    3.57+  chebang = text[:nl]
    3.58+  text = text[nl+1:]
    3.59+
    3.60+# If this isn't a supported type, send it to the regular editor
    3.61+if chebang != "#!/usr/bin/env .wiki":
    3.62+  print "headers\nLocation: %s/edit/%s\n\n" % (os.environ["HACKIKI_BASE"], fn)
    3.63+  exit(0)
    3.64+
    3.65+# Make them printable
    3.66+name = os.path.basename(fn)
    3.67+chebangh = cgi.escape(chebang)
    3.68+texth = cgi.escape(text)
    3.69+  
    3.70+# get the login info
    3.71+login = loginInfo()
    3.72+
    3.73+print template % {
    3.74+    'hackikibase': os.environ["HACKIKI_BASE"],
    3.75+    'login': login,
    3.76+    'name': name, 'fn': fn,
    3.77+    'message': message,
    3.78+    'chebang': chebang, 'chebangh': chebangh,
    3.79+    'text': text, 'texth': texth
    3.80+    }
     4.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2+++ b/bin/raw	Tue Nov 17 16:30:13 2009 +0000
     4.3@@ -0,0 +1,5 @@
     4.4+#!/bin/bash
     4.5+echo 'headers
     4.6+Content-type: text/plain
     4.7+'
     4.8+cat "$1"
     4.9\ No newline at end of file
     5.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2+++ b/bin/runner	Tue Nov 17 16:30:13 2009 +0000
     5.3@@ -0,0 +1,21 @@
     5.4+#!/usr/bin/env .wiki
     5.5+{{{
     5.6+#!/bin/bash
     5.7+if [ "$REQUEST_cmd" ]
     5.8+then
     5.9+    echo '<pre>'
    5.10+    echo '<b>&gt;' $REQUEST_cmd '</b>'
    5.11+    bash -c "$REQUEST_cmd" | sed 's/&/\&amp;/g;s/</\&lt;/g;s/>/\&gt;/g'
    5.12+    echo '</pre>'
    5.13+fi
    5.14+}}}
    5.15+
    5.16+{{{
    5.17+#!/bin/sh
    5.18+echo '<form action="'$HACKIKI_BASE'/runner" method="post">'
    5.19+}}}
    5.20+<input type="text" name="cmd" />
    5.21+<input type="submit" value="Run" />
    5.22+</form>
    5.23+
    5.24+[[index | Wiki index]]
     6.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2+++ b/bin/view	Tue Nov 17 16:30:13 2009 +0000
     6.3@@ -0,0 +1,6 @@
     6.4+#!/bin/bash
     6.5+echo -n 'headers
     6.6+Content-type: '
     6.7+file -bi "$1"
     6.8+echo ''
     6.9+cat "$1"
    6.10\ No newline at end of file
     7.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2+++ b/bin/wikisyntax	Tue Nov 17 16:30:13 2009 +0000
     7.3@@ -0,0 +1,34 @@
     7.4+#!/usr/bin/env .wiki
     7.5+= Wiki syntax
     7.6+
     7.7+This installation of Hackiki uses (roughly) the [[http://wikicreole.org/wiki/Creole1.0|WikiCreole]] wiki syntax, implemented in [[edit/bin/.wiki|.wiki]] and [[edit/lib/python/creole.py|creole.py]]. The only major difference is that code sections can have a chebang line and be interpreted code.
     7.8+
     7.9+
    7.10+== Basic WikiCreole syntax:
    7.11+
    7.12+|=Syntax|=Display|
    7.13+|~**bold~**|**bold**|
    7.14+|~//italics~//|//italics//|
    7.15+|~= Large heading| = Large heading|
    7.16+|~====== Small heading| ====== Small heading|
    7.17+|~[[link~]]|[[link]]|
    7.18+|line~\\break|line\\break|
    7.19+
    7.20+Note that the largest heading size will be interpreted as the title of the page. There can only be one element on the page of the largest heading size.
    7.21+
    7.22+For more syntax, see http://wikicreole.org/wiki/Creole1.0
    7.23+
    7.24+
    7.25+== Interpreted code
    7.26+
    7.27+~{{{\\
    7.28+~#!/usr/bin/python\\
    7.29+print "Hello!"\\
    7.30+~}}}
    7.31+
    7.32+will print this:
    7.33+
    7.34+{{{
    7.35+#!/usr/bin/python
    7.36+print "Hello!"
    7.37+}}}
     8.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2+++ b/lib/python/creole.py	Tue Nov 17 16:30:13 2009 +0000
     8.3@@ -0,0 +1,400 @@
     8.4+# -*- coding: iso-8859-1 -*-
     8.5+"""
     8.6+    Creole wiki markup parser
     8.7+
     8.8+    See http://wikicreole.org/ for latest specs.
     8.9+
    8.10+    This is from http://wiki.sheep.art.pl/Wiki%20Creole%20Parser%20in%20Python
    8.11+
    8.12+    Notes:
    8.13+    * No markup allowed in headings.
    8.14+      Creole 1.0 does not require us to support this.
    8.15+    * No markup allowed in table headings.
    8.16+      Creole 1.0 does not require us to support this.
    8.17+    * No (non-bracketed) generic url recognition: this is "mission impossible"
    8.18+      except if you want to risk lots of false positives. Only known protocols
    8.19+      are recognized.
    8.20+    * We do not allow ":" before "//" italic markup to avoid urls with
    8.21+      unrecognized schemes (like wtf://server/path) triggering italic rendering
    8.22+      for the rest of the paragraph.
    8.23+
    8.24+    @copyright: 2007 MoinMoin:RadomirDopieralski (creole 0.5 implementation),
    8.25+                2007 MoinMoin:ThomasWaldmann (updates)
    8.26+    @license: GNU GPL, see COPYING for details.
    8.27+    @license: BSD, see COPYING for details.
    8.28+"""
    8.29+
    8.30+import re
    8.31+
    8.32+__version__ = '1.0'
    8.33+
    8.34+
    8.35+# Whether the parser should convert \n into <br>.
    8.36+bloglike_lines = False
    8.37+
    8.38+class Rules:
    8.39+    """Hold all the rules for generating regular expressions."""
    8.40+
    8.41+    # For the inline elements:
    8.42+    proto = r'http|https|ftp|nntp|news|mailto|telnet|file|irc'
    8.43+    url =  r'''(?P<url>
    8.44+            (^ | (?<=\s | [.,:;!?()/=]))
    8.45+            (?P<escaped_url>~)?
    8.46+            (?P<url_target> (?P<url_proto> %s ):\S+? )
    8.47+            ($ | (?=\s | [,.:;!?()] (\s | $)))
    8.48+        )''' % proto
    8.49+    link = r'''(?P<link>
    8.50+            \[\[
    8.51+            (?P<link_target>.+?) \s*
    8.52+            ([|] \s* (?P<link_text>.+?) \s*)?
    8.53+            ]]
    8.54+        )'''
    8.55+    image = r'''(?P<image>
    8.56+            {{
    8.57+            (?P<image_target>.+?) \s*
    8.58+            ([|] \s* (?P<image_text>.+?) \s*)?
    8.59+            }}
    8.60+        )'''
    8.61+    macro = r'''(?P<macro>
    8.62+            <<
    8.63+            (?P<macro_name> \w+)
    8.64+            (\( (?P<macro_args> .*?) \))? \s*
    8.65+            ([|] \s* (?P<macro_text> .+?) \s* )?
    8.66+            >>
    8.67+        )'''
    8.68+    code = r'(?P<code> {{{ (?P<code_text>.*?) }}} )'
    8.69+    emph = r'(?P<emph> (?<!:)// )' # there must be no : in front of the //
    8.70+                                   # avoids italic rendering in urls with
    8.71+                                   # unknown protocols
    8.72+    strong = r'(?P<strong> \*\* )'
    8.73+    linebreak = r'(?P<break> \\\\ )'
    8.74+    escape = r'(?P<escape> ~ (?P<escaped_char>\S) )'
    8.75+    char =  r'(?P<char> . )'
    8.76+
    8.77+    # For the block elements:
    8.78+    separator = r'(?P<separator> ^ \s* ---- \s* $ )' # horizontal line
    8.79+    line = r'(?P<line> ^ \s* $ )' # empty line that separates paragraphs
    8.80+    head = r'''(?P<head>
    8.81+            ^ \s*
    8.82+            (?P<head_head>=+) \s*
    8.83+            (?P<head_text> .*? ) \s*
    8.84+            (?P<head_tail>=*) \s*
    8.85+            $
    8.86+        )'''
    8.87+    if bloglike_lines:
    8.88+        text = r'(?P<text> .+ ) (?P<break> (?<!\\)$\n(?!\s*$) )?'
    8.89+    else:
    8.90+        text = r'(?P<text> .+ )'
    8.91+    list = r'''(?P<list>
    8.92+            ^ [ \t]* ([*][^*\#]|[\#][^\#*]).* $
    8.93+            ( \n[ \t]* [*\#]+.* $ )*
    8.94+        )''' # Matches the whole list, separate items are parsed later. The
    8.95+             # list *must* start with a single bullet.
    8.96+    item = r'''(?P<item>
    8.97+            ^ \s*
    8.98+            (?P<item_head> [\#*]+) \s*
    8.99+            (?P<item_text> .*?)
   8.100+            $
   8.101+        )''' # Matches single list items
   8.102+    pre = r'''(?P<pre>
   8.103+            ^{{{ \s* $
   8.104+            (\n)?
   8.105+            (?P<pre_text>
   8.106+                ([\#]!(?P<pre_kind>\w*?)(\s+.*)?$)?
   8.107+                (.|\n)+?
   8.108+            )
   8.109+            (\n)?
   8.110+            ^}}} \s*$
   8.111+        )'''
   8.112+    pre_escape = r' ^(?P<indent>\s*) ~ (?P<rest> \}\}\} \s*) $'
   8.113+    table = r'''(?P<table>
   8.114+            ^ \s*
   8.115+            [|].*? \s*
   8.116+            [|]? \s*
   8.117+            $
   8.118+        )'''
   8.119+
   8.120+    # For splitting table cells:
   8.121+    cell = r'''
   8.122+            \| \s*
   8.123+            (
   8.124+                (?P<head> [=][^|]+ ) |
   8.125+                (?P<cell> (  %s | [^|])+ )
   8.126+            ) \s*
   8.127+        ''' % '|'.join([link, macro, image, code])
   8.128+
   8.129+class Parser:
   8.130+    """
   8.131+    Parse the raw text and create a document object
   8.132+    that can be converted into output using Emitter.
   8.133+    """
   8.134+
   8.135+    # For pre escaping, in creole 1.0 done with ~:
   8.136+    pre_escape_re = re.compile(Rules.pre_escape, re.M | re.X)
   8.137+    link_re = re.compile('|'.join([Rules.image, Rules.linebreak, Rules.char]), re.X | re.U) # for link descriptions
   8.138+    item_re = re.compile(Rules.item, re.X | re.U | re.M) # for list items
   8.139+    cell_re = re.compile(Rules.cell, re.X | re.U) # for table cells
   8.140+    # For block elements:
   8.141+    block_re = re.compile('|'.join([Rules.line, Rules.head, Rules.separator,
   8.142+        Rules.pre, Rules.list, Rules.table, Rules.text]), re.X | re.U | re.M)
   8.143+    # For inline elements:
   8.144+    inline_re = re.compile('|'.join([Rules.link, Rules.url, Rules.macro,
   8.145+        Rules.code, Rules.image, Rules.strong, Rules.emph, Rules.linebreak,
   8.146+        Rules.escape, Rules.char]), re.X | re.U)
   8.147+
   8.148+    def __init__(self, raw):
   8.149+        self.raw = raw
   8.150+        self.root = DocNode('document', None)
   8.151+        self.cur = self.root        # The most recent document node
   8.152+        self.text = None            # The node to add inline characters to
   8.153+
   8.154+    def _upto(self, node, kinds):
   8.155+        """
   8.156+        Look up the tree to the first occurence
   8.157+        of one of the listed kinds of nodes or root.
   8.158+        Start at the node node.
   8.159+        """
   8.160+        while node.parent is not None and not node.kind in kinds:
   8.161+            node = node.parent
   8.162+        return node
   8.163+
   8.164+    # The _*_repl methods called for matches in regexps. Sometimes the
   8.165+    # same method needs several names, because of group names in regexps.
   8.166+
   8.167+    def _url_repl(self, groups):
   8.168+        """Handle raw urls in text."""
   8.169+
   8.170+        if not groups.get('escaped_url'):
   8.171+            # this url is NOT escaped
   8.172+            target = groups.get('url_target', '')
   8.173+            node = DocNode('link', self.cur)
   8.174+            node.content = target
   8.175+            DocNode('text', node, node.content)
   8.176+            self.text = None
   8.177+        else:
   8.178+            # this url is escaped, we render it as text
   8.179+            if self.text is None:
   8.180+                self.text = DocNode('text', self.cur, u'')
   8.181+            self.text.content += groups.get('url_target')
   8.182+    _url_target_repl = _url_repl
   8.183+    _url_proto_repl = _url_repl
   8.184+    _escaped_url = _url_repl
   8.185+
   8.186+    def _link_repl(self, groups):
   8.187+        """Handle all kinds of links."""
   8.188+
   8.189+        target = groups.get('link_target', '')
   8.190+        text = (groups.get('link_text', '') or '').strip()
   8.191+        parent = self.cur
   8.192+        self.cur = DocNode('link', self.cur)
   8.193+        self.cur.content = target
   8.194+        self.text = None
   8.195+        re.sub(self.link_re, self._replace, text)
   8.196+        self.cur = parent
   8.197+        self.text = None
   8.198+    _link_target_repl = _link_repl
   8.199+    _link_text_repl = _link_repl
   8.200+
   8.201+    def _macro_repl(self, groups):
   8.202+        """Handles macros using the placeholder syntax."""
   8.203+
   8.204+        name = groups.get('macro_name', '')
   8.205+        text = (groups.get('macro_text', '') or '').strip()
   8.206+        node = DocNode('macro', self.cur, name)
   8.207+        node.args = groups.get('macro_args', '') or ''
   8.208+        DocNode('text', node, text or name)
   8.209+        self.text = None
   8.210+    _macro_name_repl = _macro_repl
   8.211+    _macro_args_repl = _macro_repl
   8.212+    _macro_text_repl = _macro_repl
   8.213+
   8.214+    def _image_repl(self, groups):
   8.215+        """Handles images and attachemnts included in the page."""
   8.216+
   8.217+        target = groups.get('image_target', '').strip()
   8.218+        text = (groups.get('image_text', '') or '').strip()
   8.219+        node = DocNode("image", self.cur, target)
   8.220+        DocNode('text', node, text or node.content)
   8.221+        self.text = None
   8.222+    _image_target_repl = _image_repl
   8.223+    _image_text_repl = _image_repl
   8.224+
   8.225+    def _separator_repl(self, groups):
   8.226+        self.cur = self._upto(self.cur, ('document', 'section', 'blockquote'))
   8.227+        DocNode('separator', self.cur)
   8.228+
   8.229+    def _item_repl(self, groups):
   8.230+        bullet = groups.get('item_head', u'')
   8.231+        text = groups.get('item_text', u'')
   8.232+        if bullet[-1] == '#':
   8.233+            kind = 'number_list'
   8.234+        else:
   8.235+            kind = 'bullet_list'
   8.236+        level = len(bullet)
   8.237+        lst = self.cur
   8.238+        # Find a list of the same kind and level up the tree
   8.239+        while (lst and
   8.240+                   not (lst.kind in ('number_list', 'bullet_list') and
   8.241+                        lst.level == level) and
   8.242+                    not lst.kind in ('document', 'section', 'blockquote')):
   8.243+            lst = lst.parent
   8.244+        if lst and lst.kind == kind:
   8.245+            self.cur = lst
   8.246+        else:
   8.247+            # Create a new level of list
   8.248+            self.cur = self._upto(self.cur,
   8.249+                ('list_item', 'document', 'section', 'blockquote'))
   8.250+            self.cur = DocNode(kind, self.cur)
   8.251+            self.cur.level = level
   8.252+        self.cur = DocNode('list_item', self.cur)
   8.253+        self.parse_inline(text)
   8.254+        self.text = None
   8.255+    _item_text_repl = _item_repl
   8.256+    _item_head_repl = _item_repl
   8.257+
   8.258+    def _list_repl(self, groups):
   8.259+        text = groups.get('list', u'')
   8.260+        self.item_re.sub(self._replace, text)
   8.261+
   8.262+    def _head_repl(self, groups):
   8.263+        self.cur = self._upto(self.cur, ('document', 'section', 'blockquote'))
   8.264+        node = DocNode('header', self.cur, groups.get('head_text', '').strip())
   8.265+        node.level = len(groups.get('head_head', ' '))
   8.266+    _head_head_repl = _head_repl
   8.267+    _head_text_repl = _head_repl
   8.268+
   8.269+    def _text_repl(self, groups):
   8.270+        text = groups.get('text', '')
   8.271+        if self.cur.kind in ('table', 'table_row', 'bullet_list',
   8.272+            'number_list'):
   8.273+            self.cur = self._upto(self.cur,
   8.274+                ('document', 'section', 'blockquote'))
   8.275+        if self.cur.kind in ('document', 'section', 'blockquote'):
   8.276+            self.cur = DocNode('paragraph', self.cur)
   8.277+        else:
   8.278+            text = u' ' + text
   8.279+        self.parse_inline(text)
   8.280+        if groups.get('break') and self.cur.kind in ('paragraph',
   8.281+            'emphasis', 'strong', 'code'):
   8.282+            DocNode('break', self.cur, '')
   8.283+        self.text = None
   8.284+    _break_repl = _text_repl
   8.285+
   8.286+    def _table_repl(self, groups):
   8.287+        row = groups.get('table', '|').strip()
   8.288+        self.cur = self._upto(self.cur, (
   8.289+            'table', 'document', 'section', 'blockquote'))
   8.290+        if self.cur.kind != 'table':
   8.291+            self.cur = DocNode('table', self.cur)
   8.292+        tb = self.cur
   8.293+        tr = DocNode('table_row', tb)
   8.294+
   8.295+        text = ''
   8.296+        for m in self.cell_re.finditer(row):
   8.297+            cell = m.group('cell')
   8.298+            if cell:
   8.299+                self.cur = DocNode('table_cell', tr)
   8.300+                self.text = None
   8.301+                self.parse_inline(cell)
   8.302+            else:
   8.303+                cell = m.group('head')
   8.304+                self.cur = DocNode('table_head', tr)
   8.305+                self.text = DocNode('text', self.cur, u'')
   8.306+                self.text.content = cell.strip('=')
   8.307+        self.cur = tb
   8.308+        self.text = None
   8.309+
   8.310+    def _pre_repl(self, groups):
   8.311+        self.cur = self._upto(self.cur, ('document', 'section', 'blockquote'))
   8.312+        kind = groups.get('pre_kind', None)
   8.313+        text = groups.get('pre_text', u'')
   8.314+        def remove_tilde(m):
   8.315+            return m.group('indent') + m.group('rest')
   8.316+        text = self.pre_escape_re.sub(remove_tilde, text)
   8.317+        node = DocNode('preformatted', self.cur, text)
   8.318+        node.sect = kind or ''
   8.319+        self.text = None
   8.320+    _pre_text_repl = _pre_repl
   8.321+    _pre_head_repl = _pre_repl
   8.322+    _pre_kind_repl = _pre_repl
   8.323+
   8.324+    def _line_repl(self, groups):
   8.325+        self.cur = self._upto(self.cur, ('document', 'section', 'blockquote'))
   8.326+
   8.327+    def _code_repl(self, groups):
   8.328+        DocNode('code', self.cur, groups.get('code_text', u'').strip())
   8.329+        self.text = None
   8.330+    _code_text_repl = _code_repl
   8.331+    _code_head_repl = _code_repl
   8.332+
   8.333+    def _emph_repl(self, groups):
   8.334+        if self.cur.kind != 'emphasis':
   8.335+            self.cur = DocNode('emphasis', self.cur)
   8.336+        else:
   8.337+            self.cur = self._upto(self.cur, ('emphasis', )).parent
   8.338+        self.text = None
   8.339+
   8.340+    def _strong_repl(self, groups):
   8.341+        if self.cur.kind != 'strong':
   8.342+            self.cur = DocNode('strong', self.cur)
   8.343+        else:
   8.344+            self.cur = self._upto(self.cur, ('strong', )).parent
   8.345+        self.text = None
   8.346+
   8.347+    def _break_repl(self, groups):
   8.348+        DocNode('break', self.cur, None)
   8.349+        self.text = None
   8.350+
   8.351+    def _escape_repl(self, groups):
   8.352+        if self.text is None:
   8.353+            self.text = DocNode('text', self.cur, u'')
   8.354+        self.text.content += groups.get('escaped_char', u'')
   8.355+
   8.356+    def _char_repl(self, groups):
   8.357+        if self.text is None:
   8.358+            self.text = DocNode('text', self.cur, u'')
   8.359+        self.text.content += groups.get('char', u'')
   8.360+
   8.361+    def _replace(self, match):
   8.362+        """Invoke appropriate _*_repl method. Called for every matched group."""
   8.363+
   8.364+        groups = match.groupdict()
   8.365+        for name, text in groups.iteritems():
   8.366+            if text is not None:
   8.367+                replace = getattr(self, '_%s_repl' % name)
   8.368+                replace(groups)
   8.369+                return
   8.370+
   8.371+    def parse_inline(self, raw):
   8.372+        """Recognize inline elements inside blocks."""
   8.373+
   8.374+        re.sub(self.inline_re, self._replace, raw)
   8.375+
   8.376+    def parse_block(self, raw):
   8.377+        """Recognize block elements."""
   8.378+
   8.379+        re.sub(self.block_re, self._replace, raw)
   8.380+
   8.381+    def parse(self):
   8.382+        """Parse the text given as self.raw and return DOM tree."""
   8.383+
   8.384+        self.parse_block(self.raw)
   8.385+        return self.root
   8.386+
   8.387+#################### Helper classes
   8.388+
   8.389+### The document model and emitter follow
   8.390+
   8.391+class DocNode:
   8.392+    """
   8.393+    A node in the document.
   8.394+    """
   8.395+
   8.396+    def __init__(self, kind='', parent=None, content=None):
   8.397+        self.children = []
   8.398+        self.parent = parent
   8.399+        self.kind = kind
   8.400+        self.content = content
   8.401+        if self.parent is not None:
   8.402+            self.parent.children.append(self)
   8.403+
    10.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2+++ b/lib/python/hackikilogin.py	Tue Nov 17 16:30:13 2009 +0000
    10.3@@ -0,0 +1,8 @@
    10.4+import os
    10.5+
    10.6+def loginInfo():
    10.7+    if os.environ.has_key("HACKIKI_AUTH_DISPLAY"):
    10.8+        login = "<span class=\"dark\">You are logged in as %s</span> - <a href=\"%s/?openidLogOff\">Log off</a> -" % (os.environ["HACKIKI_AUTH_DISPLAY"], os.environ["HACKIKI_BASE"])
    10.9+    else:
   10.10+        login = "<form action=\"%s/\" method=\"get\" style=\"display: inline\"><input type=\"hidden\" name=\"openidTryAuth\" /><input type=\"text\" name=\"login\" /><input type=\"submit\" value=\"Log in using OpenID\" /></form> -" % (os.environ["HACKIKI_BASE"])
   10.11+    return login
   10.12\ No newline at end of file
    11.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2+++ b/templates/edit.html	Tue Nov 17 16:30:13 2009 +0000
    11.3@@ -0,0 +1,38 @@
    11.4+<html>
    11.5+  <head>
    11.6+    <title>Edit: %(name)s</title>
    11.7+    <link rel="stylesheet" type="text/css" href="%(hackikibase)s/raw/templates/wiki.css">
    11.8+    <meta http-equiv="Content-type" value="text/html; charset=utf-8" />
    11.9+    <style type="text/css">
   11.10+      .edit { color: red }
   11.11+    </style>
   11.12+  </head>
   11.13+  <body>
   11.14+    <div id="sidebar">
   11.15+        <h1>Hackiki</h1>
   11.16+        <ul>
   11.17+            <li><a href="%(hackikibase)s/">Start page</a></li>
   11.18+            <li><a href="%(hackikibase)s/license">License</a></li>
   11.19+            <li><a href="%(hackikibase)s/wikisyntax">Wiki syntax</a></li>
   11.20+            <li><a href="%(hackikibase)s/edit/bin/medit">Edit the meta-editor</a> (Python code)</li>
   11.21+        </ul>
   11.22+    </div>
   11.23+
   11.24+    <div id="iebox">
   11.25+    <div id="topbar">
   11.26+        <a href="%(hackikibase)s/%(name)s">View</a> -
   11.27+        <a href="%(hackikibase)s/edit/bin/%(name)s">Builtin editor</a> -
   11.28+        <a href="/fs/log/tip/bin/%(name)s">History</a>
   11.29+    </div>
   11.30+    <div id="content">
   11.31+        %(message)s
   11.32+        <h1>%(name)s</h1>
   11.33+        <form action="%(hackikibase)s/medit/%(fn)s" method="post">
   11.34+            <input type="hidden" name="chebang" value="%(chebangh)s" />
   11.35+            <textarea name="cont" rows="25" cols="80">%(texth)s</textarea><br/>
   11.36+            <input type="submit" value="Edit" />
   11.37+        </form>
   11.38+    </div>
   11.39+    </div>
   11.40+  </body>
   11.41+</html>
    12.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2+++ b/templates/wiki.css	Tue Nov 17 16:30:13 2009 +0000
    12.3@@ -0,0 +1,72 @@
    12.4+body {
    12.5+    background-color: #FFFFFF;
    12.6+    color: #000000;
    12.7+    margin: 0px;
    12.8+}
    12.9+
   12.10+a:active {color: #990099}
   12.11+a:visited {color: #0000FF}
   12.12+a:link {color: #0000FF}
   12.13+a:hover {color: #990099}
   12.14+
   12.15+input, select, textarea {	
   12.16+    background-color: #FFFFFF;
   12.17+    border: solid 1px #000000;
   12.18+}
   12.19+
   12.20+h1, h2, h3, h4, h5, h6 { font-family: sans-serif }
   12.21+
   12.22+div#sidebar {
   12.23+    background-color: #000066;
   12.24+    color: #FFFFFF;
   12.25+    position: fixed;
   12.26+    left: 0px;
   12.27+    width: 10em;
   12.28+    top: 0px;
   12.29+    bottom: 0px;
   12.30+    z-index: 2;
   12.31+    overflow: auto;
   12.32+    padding: 0.5em;
   12.33+}
   12.34+div#sidebar a:active { color: #66FF66 }
   12.35+div#sidebar a:visited { color: #FFFF00 }
   12.36+div#sidebar a:link { color: #FFFF00 }
   12.37+div#sidebar a:hover { color: #66FF66 }
   12.38+
   12.39+div#iebox {
   12.40+    position: absolute;
   12.41+    left: 0px;
   12.42+    width: 100%;
   12.43+    top: 0px;
   12.44+}
   12.45+
   12.46+div#topbar {
   12.47+    position: fixed;
   12.48+    left: 0px;
   12.49+    right: 0px;
   12.50+    top: 0px;
   12.51+    background-color: #FFFFFF;
   12.52+    color: #CCCCCC;
   12.53+    margin-left: 11em;
   12.54+    border-bottom: 2px solid #000066;
   12.55+    padding: 0.2em 0.5em 0.5em 0.5em;
   12.56+    text-align: right;
   12.57+}
   12.58+div#topbar a {
   12.59+    color: #000099;
   12.60+    font-family: sans-serif;
   12.61+    text-decoration: none;
   12.62+}
   12.63+div#topbar a:hover {
   12.64+    font-family: sans-serif;
   12.65+    text-decoration: underline;
   12.66+}
   12.67+span.dark {
   12.68+    color: #000000;
   12.69+}
   12.70+
   12.71+div#content {
   12.72+    background-color: #FFFFFF;
   12.73+    margin-left: 11em;
   12.74+    padding: 2em 0.5em 0px 0.5em;
   12.75+}
   12.76\ No newline at end of file
    13.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2+++ b/templates/wiki.html	Tue Nov 17 16:30:13 2009 +0000
    13.3@@ -0,0 +1,33 @@
    13.4+<html>
    13.5+  <head>
    13.6+    <title>%(title)s</title>
    13.7+    <link rel="stylesheet" type="text/css" href="%(hackikibase)s/raw/templates/wiki.css">
    13.8+    <meta http-equiv="Content-type" value="text/html; charset=utf-8" />
    13.9+    <style type="text/css">
   13.10+      .edit { color: red }
   13.11+    </style>
   13.12+  </head>
   13.13+  <body>
   13.14+    <div id="sidebar">
   13.15+        <h1>Hackiki</h1>
   13.16+        <ul>
   13.17+            <li><a href="%(hackikibase)s/">Start page</a></li>
   13.18+            <li><a href="%(hackikibase)s/license">License</a></li>
   13.19+            <li><a href="%(hackikibase)s/raw/bin/%(name)s">Source of this page</a></li>
   13.20+        </ul>
   13.21+    </div>
   13.22+
   13.23+    <div id="iebox">
   13.24+    <div id="topbar">
   13.25+        <a href="%(hackikibase)s/medit/bin/%(name)s">Edit</a> -
   13.26+        <a href="/fs/log/tip/bin/%(name)s">History</a>
   13.27+    </div>
   13.28+    <div id="content">
   13.29+        <h1>%(title)s</h1>
   13.30+        <p>
   13.31+            %(text)s
   13.32+        </p>
   13.33+    </div>
   13.34+    </div>
   13.35+  </body>
   13.36+</html>