Fixup a few errors in the HTML output
[geekigeeki.git] / geekigeeki.py
index cc0373621ab6e4c11da560c253d9383cc40a9948..f6ba26c967419f5c219d98d313bdc19932630883 100755 (executable)
@@ -33,7 +33,8 @@ file_re = re.compile(r"^\b([A-Za-z0-9_\-][A-Za-z0-9_\.\-/]*)\b$")
 img_re = re.compile(r"^.*\.(png|gif|jpg|jpeg|bmp|ico|ogm|ogg|mkv|mpg|mpeg|mp4|avi|asf|flv|wmv|qt)$", re.IGNORECASE)
 video_re = re.compile(r"^.*\.(ogm|ogg|mkv|mpg|mpeg|mp4|avi|asf|flv|wmv|qt)$", re.IGNORECASE)
 url_re = re.compile(r"^[a-z]{3,8}://[^\s'\"]+\S$")
-link_re = re.compile("(?:\[\[|{{)([^\s\|]+)(?:\s*\|\s*([^\]]+)|)(?:\]\]|}})")
+link_re = re.compile(r"(?:\[\[|{{)([^\s\|]+)(?:\s*\|\s*([^\]]+)|)(?:\]\]|}})")
+ext_re = re.compile(r"\.([^\./]+)$")
 
 title_done = False
 
@@ -77,7 +78,7 @@ def relative_url(pathname, privileged=False):
         else:
             url = script_name()
         pathname = url + '/' + pathname
-    return pathname
+    return cgi.escape(pathname, quote=True)
 
 def permalink(s):
     return re.sub(' ', '-', re.sub('[^a-z0-9_ ]', '', s.lower()).strip())
@@ -86,13 +87,6 @@ def permalink(s):
 def emit_header(mime_type="text/html"):
     print "Content-type: " + mime_type + "; charset=utf-8\n"
 
-def sendfile(dest_file, src_file):
-    """Efficiently copy file data between file descriptors"""
-    while 1:
-        data = src_file.read(65536)
-        if not data: break
-        dest_file.write(data)
-
 def send_guru(msg_text, msg_type):
     if not msg_text: return
     print '<pre id="guru" onclick="this.style.display = \'none\'" class="' + msg_type + '">'
@@ -100,12 +94,10 @@ def send_guru(msg_text, msg_type):
         print '    Software Failure.  Press left mouse button to continue.\n'
     print msg_text
     if msg_type == 'error':
-        print '\n      Guru Meditation #DEADBEEF.ABADC0DE'
+        print '\n           Guru Meditation #DEADBEEF.ABADC0DE'
     print '</pre>'
-    try:
-        sendfile(sys.stdout, open('gurumeditation.js', 'rb'))
-    except IOError, err:
-        pass
+    print '<script language="JavaScript" type="text/javascript" src="%s" defer="defer"></script>' \
+        % relative_url('sys/GuruMeditation.js')
 
 def send_title(name, text="Limbo", msg_text=None, msg_type='error', writable=False):
     global title_done
@@ -151,7 +143,7 @@ def send_title(name, text="Limbo", msg_text=None, msg_type='error', writable=Fal
 
     # Navbar
     print '<div class="nav">'
-    print link_tag('FrontPage', relative_url(site_icon or 'Home'), 'navlink')
+    print link_tag('FrontPage', site_icon or 'Home', 'navlink')
     if name:
         print '  <b>' + link_tag('?fullsearch=' + name, text, 'navlink') + '</b> '
     else:
@@ -188,7 +180,7 @@ def link_tag(params, text=None, link_class=None, privileged=False):
     if text is None:
         text = params # default
     elif img_re.match(text):
-        text = '<img border="0" src="' + text + '" />'
+        text = '<img border="0" src="' + relative_url(text) + '" alt="' + text + '" />'
 
     if not link_class:
         if is_external_url(params):
@@ -208,10 +200,16 @@ def link_tag(params, text=None, link_class=None, privileged=False):
 
 def link_inline(name, descr=None, args=''):
     if not descr: descr = name
+    url = relative_url(name)
     if video_re.match(name):
-        return '<video src="%s">Your browser does not support the HTML5 video tag</video>' % name
+        return '<video src="%s">Your browser does not support the HTML5 video tag</video>' % url
+    elif img_re.match(name):
+        return '<a href="%s"><img border="0" src="%s" alt="%s" /></a>' % (url, url + args, descr)
+    elif file_re.match(name) and not ext_re.search(name): # FIXME: this guesses a wiki page
+        return Page(name).send_naked()
     else:
-        return '<a href="%s"><img border="0" src="%s" alt="%s" /></a>' % (name, name + args, descr)
+        return '<iframe width="100%%" scrolling="auto" frameborder="0" src="%s"><a href="%s">%s</a></iframe>' \
+            % (url, url, name)
 
 # Search ---------------------------------------------------
 
@@ -289,9 +287,10 @@ def handle_edit(pagename):
             text = form['savetext'].value
         pg.send_editor(text)
 
+# Used by macros/WordIndex and macros/TitleIndex
 def make_index_key():
     links = map(lambda ch: '<a href="#%s">%s</a>' % (ch, ch), 'abcdefghijklmnopqrstuvwxyz')
-    return '<p><center>'+ ' | '.join(links) + '</center></p>'
+    return '<p style="text-align: center">'+ ' | '.join(links) + '</p>'
 
 def page_list(dirname = None, re = word_re):
     return sorted(filter(re.match, os.listdir(dirname or data_dir)))
@@ -387,9 +386,7 @@ class WikiFormatter:
         return link_tag(m.group(1), m.group(2))
 
     def _inl_repl(self, word):
-        m = link_re.match(word)
-        name = relative_url(m.group(1))
-        descr = m.group(2)
+        (name, descr) = link_re.match(word).groups()
 
         if descr:
             argv = descr.split('|')
@@ -406,11 +403,13 @@ class WikiFormatter:
             return link_inline(name, name)
 
     def _html_repl(self, word):
+        if not self.in_html and word.startswith('<div'): word = '</p>' + word
         self.in_html += 1
         return word; # Pass through
 
     def _htmle_repl(self, word):
         self.in_html -= 1
+        if not self.in_html and word.startswith('</div'): word += '<p>'
         return word; # Pass through
 
     def _ent_repl(self, s):
@@ -606,12 +605,14 @@ class Page:
                 return False
             raise err
 
-    def get_raw_body(self):
+    def get_raw_body(self, default=None):
         try:
             return open(self._filename(), 'rb').read()
         except IOError, err:
             if err.errno == errno.ENOENT:
-                return '' # just doesn't exist, use default
+                if default is None:
+                    default = '//[[?edit=%s|Describe %s]]//' % (self.page_name, self.page_name)
+                return default
             if err.errno == errno.EISDIR:
                 return self.format_dir()
             raise err
@@ -719,7 +720,8 @@ class Page:
         print '<div class="editor"><form name="editform" method="post" enctype="multipart/form-data" action="%s">' % relative_url(self.page_name)
         print '<input type="hidden" name="edit" value="%s">' % (self.page_name)
         print '<input type="input" id="editor" name="changelog" value="Edit page %s" accesskey="c" /><br />' % (self.page_name)
-        print '<textarea wrap="off" spellcheck="true" id="editor" name="savetext" rows="17" cols="100" accesskey="e">%s</textarea>' % cgi.escape(preview or self.get_raw_body())
+        print '<textarea wrap="off" spellcheck="true" id="editor" name="savetext" rows="17" cols="100" accesskey="e">%s</textarea>' \
+            % cgi.escape(preview or self.get_raw_body(default=''))
         print '<label for="file" accesskey="u">Or Upload a file:</label> <input type="file" name="file" value="%s" />' % filename
         print """
             <br />
@@ -779,19 +781,13 @@ class Page:
         self._write_file(newdata)
         rc = 0
         if post_edit_hook:
-            # FIXME: what's the std way to perform shell quoting in python?
-            cmd = ( post_edit_hook
-                + " '" + data_dir + '/' + self.page_name
-                + "' '" + remote_user()
-                + "' '" + remote_host()
-               + "' '" + changelog + "'"
-            )
-            out = os.popen(cmd)
-            output = out.read()
-            rc = out.close()
+            import subprocess
+            cmd = [ post_edit_hook, data_dir + '/' + self.page_name, remote_user(), remote_host(), changelog]
+            child = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
+            output = child.stdout.read()
+            rc = child.wait()
         if rc:
-            self.msg_text += "Post-editing hook returned %d.\n" % rc
-            self.msg_text += 'Command was: ' + cmd + '\n'
+            self.msg_text += "Post-editing hook returned %d. Command was:\n'%s'\n" % (rc, "' '".join(cmd))
             if output:
                 self.msg_text += 'Output follows:\n' + output
         else: