Divmod : Mantissa : Wiki Tutorial
Contents
Please let me know if you know how to solve some of the steps more elegantly. Other comments are also welcome, of course.
-- Karl Bartel <karlb@gmx.net>
Note: this walkthrough relies on axiomatic project. However, axiomatic project is currently broken. See #911 for progress on this issue.
-- Jean-Paul Calderone <exarkun@divmod.com>
Things to learn in this Tutorial:
- Setting up a Mantissa server
- Serving simple dynamic pages
- Using public pages
- Manipulating your database from the python shell
- Reading parameters passed from the browser
Part 1: Creating an Offering and Running it in a Mantissa Server
Create Project Skeletion (warning: this doesn't work with current trunk of Axiom/Mantissa)
$ axiomatic project -n wiki
Set up Database and Mantissa Server
$ cd Wiki $ axiomatic -d wikitut.axiom mantissa
Add a public page
The project skeleton's start page is designed to be viewed only by users who have a login for our mantissa server. We don't want to deal with the login system in this tutorial, so let's change the WikiStart object to a PublicPage in wiki/wiki_model.py.
- Change ixmantissa.INavigableElement to ixmantissa.IPublicPage both in the "implements" call and in the "installOn" method
- Remove the "getTabs" and "explode" methods and the registerAdapter line below the class
- add the following method:
def getResource(self): return WikiView(self)
Because WikiStart is defined as powerup in our Offering(xmantissa/wikioff.py), an instance of this class will be added to our application store, when our app is added to the mantissa server.
Add Project to Mantissa Server
- Run the mantissa server
$ axiomatic -d wikitut.axiom start -n
- Go to http://localhost:8080/login and log in as admin
- Navigate to Home->Offerings and click on the "Wiki" offering to install it. If you can't see an offering named "Wiki", your probably need to get your project dir onto your PYTHONPATH.
From now on our WikiStart object can be viewed publicly at http://localhost:8080/Wiki. Unfortunately, we haven't written a nice view for our object, yet. This makes the Mantissa server display a nice backtrace.
Part 2: Adding a View using a Template
Add a View
Replace your WikiView in wiki/wiki_view.py with
from nevow import loaders
from nevow.rend import Page
class WikiView(Page):
docFactory = loaders.xmlfile('wiki/themes/base/wiki-start.html')
def __init__(self, original):
Page.__init__(self)
self.original = original
def customizeFor(self, avatar):
return self
def render_page(self, context, date):
return 'test'
Now you can view the rendered version of the wiki/themes/base/wiki-start.html template when you visit http://localhost:8080/Wiki. Well, at least if you restart your mantissa server, so that it can have a look at the changed python files.
Write a useful Template
Replace wiki-start.html with a template which let's our view do something. For instance
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<body>
Welcome to the Wiki Application
<hr/>
<nevow:invisible nevow:render="page" />
</body>
</html>
This template call out view's render_page function. Currently, it only returns 'test', but that will change soon.
Part 3: Storing and Retrieving Wiki Pages
Create a New Item Subclass
Add this to your wiki_model.py
class WikiPage(Item):
implements(ixmantissa.INavigableElement)
schemaVersion = 1 # First version of this object.
typeName = 'wiki_page' # Database table name.
page_name = text()
page_content = text()
Change our Application Store from the Python Shell
- Open application store and do some imports
>>> from axiom.store import Store >>> s = Store('wikitut.axiom/files/app/Wiki.axiom') >>> from wiki.wiki_model import WikiPage - Create FrontPage and PageTemplate
>>> WikiPage(store = s, page_name=u'FrontPage', page_content=u'This is the FrontPage.\nMantissa is *cool*. There might also be OtherPages and MoreStuff.') >>> WikiPage(store = s, page_name=u'PageTemplate', page_content=u'This page does not exist, yet. Use the edit link below to create it.')
Retrieve a Wiki Page and Display its Content
Replace the render_page method of our WikiView class with somthing more advanced:
from wiki.wiki_model import WikiPage
args = inevow.IRequest(context).args
page_name = unicode(args.get('page', ['FrontPage'])[0])
page = self.original.store.findFirst(WikiPage, WikiPage.page_name==page_name)
if page == None:
page = self.original.store.findFirst(WikiPage, WikiPage.page_name==u"PageTemplate")
return page.page_content
For this you'll need to import inevow from nevow. Now have a look at our application again:
- http://localhost:8080/Wiki?page=FrontPage (Displays the FrontPage)
- http://localhost:8080/Wiki (Displays the FrontPage, too, because it's the default)
- http://localhost:8080/Wiki?page=OtherPage (Displays the PageTemplate, because OtherPage doesn't exist)
Part 4: Rendering the WikiPage as a Fragment
Create a New View
Add the following to your wiki_view.py (NOTE: You may need to install python-docutils module)
import re
re_wiki_word = re.compile(r'\b([A-Z]\w+[A-Z]+\w+)')
from nevow import tags
from nevow.rend import Fragment
base_url = 'Wiki?page='
class WikiPageView(Fragment):
implements(ixmantissa.INavigableFragment)
docFactory = loaders.xmlfile('wiki/themes/base/wiki-page.html')
def wikified(self):
# add some nice formatting
from docutils.core import publish_parts
text = publish_parts(self.original.page_content, writer_name="html")["html_body"]
# place links on all WikiWords
text = re_wiki_word.sub(r'<a href="%s\1">\1</a>' % base_url, text)
return tags.xml(text)
def render_page_content(self, context, data):
return self.wikified()
def render_page_name(self, context, data):
return self.original.page_name
def render_page_link(self, context, data):
args = inevow.IRequest(context).args
return base_url + args.get('page', ['FrontPage'])[0]
Add a New Template
In the new WikiPageView, we are using the template 'wiki/themes/base/wiki-page.html'. Create the file with the following content:
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
<h1><span nevow:render="page_name" /></h1>
<hr/>
<p><span nevow:render="page_content" /></p>
<hr/>
<a>
<nevow:attr name="href">
<nevow:invisible nevow:render="page_link" />&edit=1
</nevow:attr>
Edit this page
</a>
</div>
Putting the Fragment in the WikiView
- Register WikiPageView as an adapter for WikiPage by adding the following lines to the end of your wiki_model.py:
from wiki.wiki_view import WikiPageView registerAdapter(WikiPageView, WikiPage, ixmantissa.INavigableFragment)
- Replace the return statement in your WikiView?'s render_page method with
return ixmantissa.INavigableFragment(page)
This adapts the WikiPage to the INavigableFragment interface by creating a WikiPageView instance, which implements this interface. This new instance will have a reference to the WikiPage in it's "original" attribute.
And again, it's time to see what our app can do now. It has some nice formatting possibilities and it automatically links WikiWords. Great!
Part 5: Editing
Change WikiPageView?'s render_page method to this:
def render_page_content(self, context, data):
args = inevow.IRequest(context).args
if args.get('page_content', [0])[0]:
if self.original.page_name != args['page'][0]:
from wiki.wiki_model import WikiPage
self.original = WikiPage(
store = self.original.store,
page_name = unicode(args['page'][0]),
page_content = unicode(args['page_content'][0]),
)
else:
self.original.page_content = unicode(args['page_content'][0])
if args.get('edit', [0])[0]:
link = base_url + args['page'][0]
return [
tags.form(action=link, method='post')[
tags.textarea(name='page_content', cols='80', rows=50)[
self.original.page_content
],
tags.input(type='submit', value='save changed page')
]
]
else:
return self.wikified()
Voila!
