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&test=1">http://example.com/test?test&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('&', '&').replace('<', '<').replace('>', '>')
179+
180+ def attr_escape(self, text):
181+ return self.html_escape(text).replace('"', '"')
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>>' $REQUEST_cmd '</b>'
5.11+ bash -c "$REQUEST_cmd" | sed 's/&/\&/g;s/</\</g;s/>/\>/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>