A Patch for filterbased Zwiki Pages

OK, it's too late, i'll be adding information and code tomorrow! Stay tuned! ;) --BeWo

Back again! I really had to catch the bus yesterdy evening! So let's see what we have...

Situation

Zwiki as well as other wiki engines use variants of StructuredText (ST) for markup purposes. Usability of Wikis always are strongly connected with the ST concept they use. On the other hand there is no ST standard, which is ok as there are shortcomings in all ST concepts i used and further experimentation is needed before one could hope for a useful standard.

Sadly this means that you can't transfer or copy pages between wikiwebs using different wikiengines, unless they support each others ST style. Concerning zwiki the page_type property is used to determin which ST style will be used to render the page.

Idea

Rendering text content of a wiki page is a filtering process transforming text to www browsable html. The filter(s) to be applied should be separated from the basic wiki engine because:

Solution

For the moment i added a new rendering function in ZWikiPage.py. Setting page_type to filtered triggers this function called render_filtered when the page is displayed. The function grabs a list of filternames from a lines property named display_filters and passes the page text through them returning the result.

At the moment filters are zope methods or scripts taking one parameter text and returning filtered text.

Todos

Add sanity checks: has property display_filters, filter existence

Serve external filters from a filters directory

Rebuild zwiki ST using filters

Add more cool filters

Future

If the filtering concept proves useful, other rendering functions and page_type could be removed from ZWikiPage.py.

Filters supporting formgeneration

checkin and checkout filters

Code

Finally here is the meat. The following code chunk is the only modification to ZWikiPage.py itself. The rest is playing with filters. Oh, i lied, the function uses strip from the pythons string module, so you should import it. ZwikiPage.py line 76 changes to:

    from string           import split,join,find,strip   

So here we go with the first chunk.

render_filtered - the function providing the page_type filtered (lines 289 and following in my ZWikiPage.py):

    def render_filtered(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        # BeWo: apply display_filters
        t = self.xread()
        if self.hasProperty('display_filters'):
            for f in self.display_filters:
                f = strip(f)
                if f == '': continue
                try:
                    t = eval('self.'+f+'(t)')
                    t = t+"\n<DEBUG filter "+f+": ok>\n"
                except:
                    # log warning
                    t = t+"\n<DEBUG filter "+f+": missing>\n"
        else:
            t = "<h1>Warning: page_type is filtered but has no property display_filters!</h1><pre>\n" + html_quote(t) + "\n</pre>\n" + "\n<DEBUG property display_filters missing>\n"
        return t 

TextFilter - a nonsense filter for testing perposes (add as a PhytonScript with parameter text)

 #
 # State that Filter has been activated and return text unchanged
 #

 return "!! TestFilter activated!<br>\n\n"+text

tables - filter building tables (ExternalMethod with Id tables, Module Name Filter and Function Name tables)

Just an interface to the tables function in Filters.py

debug - filter building tables (ExternalMethod with Id debug, Module Name Filter and Function Name debug)

Just an interface to the debug function in Filters.py

Filter.py - to become Python text filter library (Python Script called 'Filter.py'; add to Zope's Extensions directory)

 #!/usr/bin/python
 #
 # Text Filter Library:
 #
 #  - tables: Build Tables from || markup
 # 

 import re, string, types

 ####
 # A Table Generator
 #
 # try things like: || A   || table || with
 #                  || two || rows
 #
 def tables(somelines):
   # split lines first if they come in one string
   if type(somelines)==types.StringType:
     returnmode='string'
     lines = map(string.strip,string.splitfields(somelines,'\n'))
   elif type(somelines)==types.ListType and  type(somelines[0])==types.StringType:
     returnmode='listofstring'
     lines = map(string.strip,somelines)
   else:
     returnmode='string'
     lines=["<DEBUG tables: input is neither string nor list of strings>\n"]
   # append one line so that tables get closed
   lines.append('\n')  
   # start line processing  
   outtext = []
   lastline = 'notable'                                        # last line no  tabline
   for line in lines:                                          # process lines
     if string.find(line,r"||") == -1:                         # tabline?
       if lastline == 'table': outtext.append("</table>\n")    # table ended?
       lastline = 'notable'                                    # last line no  tabline
       outtext.append(line+'\n')                               # notable lines  ausgeben 
       continue                                                # nextline
     if lastline == 'notable': outtext.append("<table border=1>\n") # start new  table?
     outtext.append("  <tr>\n")                                # begin tablerow
     tabledata = string.splitfields(line,r"||")                # split tablerow
     for data in tabledata:                                    # process  tablerow data
       outtext.append("    <td align=center>"+data+"</td>\n")  # write datafield
     outtext.append("  </tr>\n")                               # close tablerow
     lastline = 'table'                                        # lastline table
   # return outtext as string or list of strings
   if returnmode=='string':
     return string.join(outtext)
   else:
     return outtext 

 ####
 # Show debug messages
 #
 #  collects <DEBUG some message> tags, spacifies and adds them at the end
 #
 def debug(somelines):
   if type(somelines)==types.StringType:
     returnmode='string'
     lines = map(string.strip,string.splitfields(somelines,'\n'))
   elif type(somelines)==types.ListType and  type(somelines[0])==types.StringType: 
     returnmode='listofstring'
     lines = map(string.strip,somelines)
   else:
     returnmode='string'
     lines=["<DEBUG debug: input is neither string nor list of strings>\n"]
   outtext = []
   debugtext = []
   for line in lines:                                # start line processing
     if string.find(line,r"<DEBUG") != -1:           # find debug messages
       debugtext.append(line[0:1]+' '+line[1:]+'\n')
     else:  
       outtext.append(line+'\n')
   outtext.append('<hr><b>DEBUG MESSAGES FOLLOW</b><hr>\n<pre>\n')  
   outtext.extend(debugtext)     # include debug messages
   outtext.append('</pre>\n\n')
   # return outtext as string or list of strings
   if returnmode=='string':
     return string.join(outtext)
   else:
     return outtext                                                                       

That's it for now! Putting code in here, isn't really funny! I'll be providing tarballs next time! Well when i'm finished with the filtering stuff, i'll start campaining for WikiBasedLiterateProgramming ! --BeWo

This is a ZwikiModification