Writing Python extensions for deki-wiki

From $1

The amazing thing about deki-wiki is that it has a WYSIWYG interface and front end - WYSIWYG means "what you see is what you get " . SO unlike most wikis where you need to learn some "markup"  ( formatting guidelines). In dek-wiki you pretty much just compose your wiki post like you would compose an email in gmail .

The other great thing about deki-wiki is the ability to extend deki-wiki using extensions that call web-services. So say you had all your data sitting on a custom database and you wanted to have it easily embeddable on deki-wiki , you can write a custom extension which talks  to your webservice.

We have our custom database --i.e the database of all videos and associated data in our database and we decided to make these videos easily embeddable inside any wiki at bioscreencastwiki. SO to embed a video from our site you would just have to grab the video code ( from the section wiki-embed) and then embedded it with the custom tag given below. You can control the size of the video with the two numbers in the tag . SO given here is video with id f404c3f31514978ff064080d5b0e6dd5 displayed at its native size of 896 by 736.

{{bioscreencast.embed("f404c3f31514978ff064080d5b0e6dd5",896,736)}}

/content/body/p[4]/span, reference to undefined name 'bioscreencast': line 1, column 1 (click for details)

 

SO Now onto

What gets sent back and forth

The wiki learns about the extension based on the "GET" information the extension sends back.

Our extension returns the following GET ( check out http://www.bioscreencast.com:82) . On safari you may have to select view-source to see the xml.

Or the  xml message which is the "manifest" for the web service

 

<extension>
  <title> My extension </title>
<namespace>bioscreencast</namespace>
<function>
 <name>embed</name>
 <uri>http://67.207.145.173:82</uri>
 <description>Returns an embedded video from the Bioscreencast  site</description>
 <param name="video" type = "uri">video uri</param>
 <param name="width" type ="int">video width</param>
 <param name="height" type ="int">video height</param>
  <return>
  <html xmlns:eval="http://mindtouch.com/2007/dekiscript">
  <body>
  <embed src="http://www.code-itch.com/flashplayer/mediaplayer.swf"
  eval:width="args.width"
  eval:height="args.height"
  allowscriptaccess="always"
  allowfullscreen="true"
  eval:flashvars="'height='..args.height..'&amp;width='..args.width..'&amp;file='..args.video"     />
 </body>
 </html>
 </return>
 </function>
 </extension>

The wiki talks to a webservice registered at a particular URL in the control panel. So as a wiki administrator you have to setup a service in the control panel. The language in which the two communicate is XML over HTTP . So the way a wiki understands the extension is by the answer the extension delivers to a GET request . The actual extension information interchange is handled by a POST message. 

SO for a link like the one above , here is what gets posted from the wiki to the web service..

 <value type="list"><value key="#" type="str">1c80e8d95bc7861a755b4fad76b29a32</value><value key="#" type="num">800</value><value key="#" type="num">600</value></value>

And the web service then processes this information , formats the output in dekiscript or another flavor of xml or even simple html and returns this "POST".

 <html> <body>

      <embed

        src="http://www.code-itch.com/flashplayer/mediaplayer.swf"

  width="672"

    height="552"

      allowscriptaccess="always"

        allowfullscreen="true"

   flashvars="height=552&amp;width=672&amp;file=http://bioscreencast.s3.amazonaws.com/243/1c80e8d95bc7861a755b4fad76b29a32.mov"      />

   </body>

    </html>
 

SO together the GET just establishes the messages that you can send ..and the post handles the actual information sent to the web-service , whereupon it chews through that formats the wiki markup , wraps it in xml that dekiwiki understands and returns it back to the wiki for display accordingly.

 

The extension pseudocode:

The extension is base don writing a custom BaseHTTPRequestHandler in Python running on an HTTPServer

All the code does is

  • Connect to database
  • Handle the GET request by describing the extension in dekiscript xml
  • Handle the POST request by parsing input values using python minidom API and then  constructing the return xml which describes the embed tag for the video

 

The extension code:


The code follows here

 #!/usr/bin/env python

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

import CGIHTTPServer,cgi,re,MySQLdb

class MyHandler(CGIHTTPServer.CGIHTTPRequestHandler):

def get_metadata(self,video_metadata_id):

import MySQLdb
connection = MySQLdb.connect ( host= "localhost" ,user = "craptastic",passwd = "crapulous",db = "the right database")
cursor = connection.cursor()
cursor.execute("select all the stuff required- SQL goes here)
rows = cursor.fetchall()
file_nm = "%s" % rows[0][0]
ext = file_nm[-3:]
uid = "%s" % rows[0][1]
video_dim_full = "%s" % rows[0][2]
video_dim = video_dim_full.split("x")
val_array = [ext,video_dim[0],video_dim[1],uid]
return val_array

def process_string_xml(self,dekixml):
import xml.dom.minidom
from xml.dom.minidom import Node
width ,height ,fin_width , fin_height ,ext,arg = 0,0,0 , 0 , "flv",""
dat = []
doc = xml.dom.minidom.parseString(dekixml)
dekistring = ''
text = doc.getElementsByTagName("value")
video_id = doc.getElementsByTagName("value")[1].lastChild.data
# Handle case where API call has full video_link text 
rex = "http\:\/\/www\.mywebsite\.com\/bsc\_movwin\.html\?var1\=(.*)\&var2\=(.*)\&var3=(. *)\&var4\=(.*)"
matched = re.search(rex,video_id)
if (matched != None):
arg = "%s" % matched.group(2)
dat = self.get_metadata(arg)
else:
arg = "%s" % video_id
dat = self.get_metadata(arg)
ext = dat[0]
try:
width = doc.getElementsByTagName("value")[2].lastChild.data
height = doc.getElementsByTagName("value")[3].lastChild.data
except:
pass
if width != 0 and height != 0 :
fin_width = min(int(width) , int(dat[1]))
fin_height = min(int(height),int(dat[2]))
vals = [dat[3],arg,fin_width,fin_height,ext]
else:
fin_width = int(dat[1])
fin_height = int(dat[2])
vals = [dat[3],arg,fin_width,fin_height,ext]
return vals

def do_GET(self):
self.send_response(200)
self.send_header('Content-type','application/xml')
self.end_headers()
str = '''<extension>
<title> My extension </title>
<namespace>bioscreencast</namespace>
<function>
<name>embed</name>
<description>Returns an embedded video from the mywebsite.com site</description>
<param name="video" type = "uri">video uri</param>
<param name="width" type ="int">video width</param>
<param name="height" type ="int">video height</param>
<return>
<html xmlns:eval="http://mindtouch.com/2007/dekiscript">
<body>
<embed
src="mediaplayer.swf"
eval:width="args.width"
eval:height="args.height"
allowscriptaccess="always"
allowfullscreen="true"
eval:flashvars="'height='..args.height..'&amp;widt h='..args.width..'&amp;file='..args.video" />
</body>
</html>
</return>
</function>
</extension>
'''
print str
self.wfile.write(str)

def do_POST(self):
data = self.rfile.read(int(self.headers["content-length"]))
print data
self.send_response(200)
self.send_header('Content-type',"application/xml")
# Actually parse data and get string that has to be wrapped in dekiscript
# response = '''<value type="list"><value type="str">%s</value></value>''' % dekistring
vals = self.process_string_xml(data)
response = '''<value type="list"><value type="xml">
<html xmlns:eval="http://mindtouch.com/2007/dekiscript">
<body>
<embed
src="http://www.mywebsite.com/flashplayer/mediaplayer.swf"
width="%s"
height="%s"
allowscriptaccess="always"
allowfullscreen="true"
flashvars="height=%s&amp;width=%s&amp;file=http://mywebsite.s3.amazonaws.com/%s/%s.%s" />
</body>
</html></value></value>''' % (vals[2],vals[3],vals[3],vals[2],vals[0],vals[1],vals[4])
print response
self.send_header("Content-length", str(len(response)))
self.end_headers()
self.wfile.write(response)
self.wfile.flush()
self.connection.shutdown(1)

def main():
try:
server = HTTPServer(('', 82), MyHandler)
print 'Welcome to the machine...',
print 'Press ^C once or twice to quit.'
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down server'
server.socket.close()

if __name__ == '__main__'
main()

Tags:
 
Images (0)
 
Comments (0)
You must login to post a comment.