[Checkins] SVN: grokapps/gbewiki/REVIEW.txt added application review
Michael Haubenwallner
michael at d2m.at
Sat Aug 30 06:15:50 EDT 2008
Log message for revision 90609:
added application review
Changed:
A grokapps/gbewiki/REVIEW.txt
-=-
Added: grokapps/gbewiki/REVIEW.txt
===================================================================
--- grokapps/gbewiki/REVIEW.txt (rev 0)
+++ grokapps/gbewiki/REVIEW.txt 2008-08-30 10:15:48 UTC (rev 90609)
@@ -0,0 +1,228 @@
+Grok-by-Example: Guestbook
+==========================
+
+:Author: d2m (michael at d2m.at)
+
+.. contents::
+
+A basic 'Grok Wiki' application [source__] ported from a
+Google Appengine [GAE] example application [source__].
+
+__ http://svn.zope.org/grokapps/gbewiki/src/gbewiki/
+__ http://code.google.com/p/google-app-engine-samples/source/browse/trunk/cccwiki/wiki.py
+
+
+Overview
+~~~~~~~~
+
+A simple Grok wiki application.
+
+Editing is in a WYSIWYG editor (TinyMCE) rather than a text editor with special
+syntax. Users need to create an account and authenticate to edit pages.
+WikiName linking and auto-linking to plain text URLs is supported.
+
+
+Review
+~~~~~~
+
+This example app is a bit larger than the two we looked at before (gbeguestbook
+and gbe99bottles). It is also quite usable as an app on its own, as a HTML based
+wiki engine.
+
+The GAE wiki and the Grok port are - again - about the same size/lines of code.
+The Grok app is split into 4 code modules (app, page, interface, utils) with
+their related template folders.
+
+Include and use the JS library with the application
+---------------------------------------------------
+
+*gbewiki* utilizes the recently published megrok.tinymce__ package to include the
+TinyMCE library (there is no more need to distribute the app together with the
+JS package). megrok.tinymce is simply added to the 'include-requires'
+requirements list in the packages setup.py module::
+
+ install_requires=['setuptools',
+ 'grok',
+ ...
+ 'megrok.tinymce',
+ ],
+
+This adds the megrok.tinymce packages to the .buildout/eggs folder when
+zc.buildout is run and registers the TinyMCE folder as a resource library.
+Usage within a pagetemplate looks like so::
+
+ <script type="text/javascript"
+ tal:attributes="src context/++resource++TinyMCE/tiny_mce.js"></script>
+
+__ http://pypi.python.org/pypi/megrok.tinymce
+
+
+Application object
+------------------
+
+GAE uses the webapp.WSGIApplication and already configures the URL dispatching
+as a wildcard pattern::
+
+ application = webapp.WSGIApplication([('/(.*)', WikiPage)], debug=_DEBUG)
+
+WikiPage here is a Requesthandler class that handles GET/POST HTTP requests.
+
+Grok subclasses from both grok.Application and grok.Container and defines a
+'traverse' method to handle request paths::
+
+ class WikiPage(grok.Application, grok.Container):
+ ...
+ def traverse(self, page_name=default_page_name):
+ ...
+
+Groks application object tries to traverse to and return the requested Page
+object or a new default Page 'MainPage'. Requests are than handled by the Page
+object view classes (@@index, @@edit).
+
+
+Request methods
+---------------
+
+While GAE webapp.RequestHandler classes understand HTTP methods and dispatch
+accordingly, Grok uses grok.View classes that 'render' the representation of the
+current context object.
+
+
+Creating the Response
+---------------------
+
+Here both frameworks use templating to render the context object and
+return the result.
+
+GAE explicitly by defining a handler class that creates a dict of values, renders
+a template to this values and writes the result to the 'response.out' stream::
+
+ class BaseRequestHandler(webapp.RequestHandler):
+ def generate(self, template_name, template_values={}):
+ ...
+ self.response.out.write(template.render(path, values, debug=_DEBUG))
+
+Grok implicitly by following conventions. The requested view name is searched
+within the views registered directly for the current context object or more
+general registrations. ZPTs then are rendered to the methods and attributes
+provided by the calling view class.
+
+
+Loading and Storing
+-------------------
+
+In this example GAE uses the low-level 'datastore' API to store and retrieve
+Page objects to and from the appengine datastore::
+
+ def load(name):
+ query = datastore.Query('Page')
+ ...
+
+ def save(self):
+ datastore.Put(entity)
+
+
+Grok stores and retrieves the Page objects to and from the application container.
+New Pages are added by the application containers @@add view and saved by the
+Page objects @@save view.
+
+
+User management and Permissions
+--------------------------------
+
+GAE uses the appengine 'users' API to handle posting new or edited existing wiki
+pages. The user must be logged in with her google account::
+
+ def post(self, page_name):
+ if not users.get_current_user():
+ self.redirect(users.create_login_url(self.request.uri))
+ ...
+
+With Grok one needs to setup the authentication first. We use the
+PluggableAuthentication utility defined on the application object as
+LocalUtility::
+
+ class WikiPage(grok.Application, grok.Container):
+ grok.local_utility(PluggableAuthentication, IAuthentication,
+ setup=setup_pau_principal)
+
+Besides the authentication plugins the utility also defines a storage for
+Principal (user) objects::
+
+ pau['principals'] = PrincipalFolder()
+
+Two application wide permissions are created to rule adding and editing of Page
+objects::
+
+ class PermissionEditPage(grok.Permission):
+ """Permission to edit a Page """
+ grok.name('wiki.EditPage')
+
+ class PermissionAddPage(grok.Permission):
+ """Permission to add a Page """
+ grok.name('wiki.AddPage')
+
+During authentication the submitted login-name is searched within the
+PrincipalFolder. If found the user is logged in. If no user object is found a
+new principal object is created with the credentials provided by the login form.
+Both the 'wiki.AddPage' and 'wiki.EditPage' permissions are granted on the
+principal and the user is logged in immediately::
+
+ permission_mngr = IPrincipalPermissionManager(grok.getSite())
+ permission_mngr.grantPermissionToPrincipal(
+ 'wiki.AddPage', principals.prefix + login)
+ permission_mngr.grantPermissionToPrincipal(
+ 'wiki.EditPage', principals.prefix + login)
+
+This directly happens inside the @@login view update method::
+
+ class Login(Master):
+ def update(self, login_submit=None):
+ ...
+
+
+Wikification of Page content
+----------------------------
+
+Before returning the rendered Page (@@index view) to the user, the page content
+gets wikified. In our example a list of transformations is applied to the
+'content' by adapting the view object itself::
+
+ class Index(Master):
+
+ def wikified_content(self):
+ self.content = self.context.content
+ ...
+
+ transforms = [
+ 'wiki.AutoLink',
+ 'wiki.ListOfPages',
+ 'wiki.WikiWords',
+ ]
+
+ for transform in transforms:
+ self.content = getAdapter(self, ITransform, transform).run()
+ return self.content
+
+Each of the transforms is created as a named adapter, registered for
+grok.View classes::
+
+ class ITransform(Interface):
+ pass
+
+ class WikiWords(grok.Adapter):
+ grok.implements(ITransform)
+ grok.name('wiki.WikiWords')
+ grok.context(grok.View)
+ ...
+
+Overall
+-------
+
+Porting this application took quite some time (about 1 day), mostly because
+of implementing the user management (again) and getting megrok.tinymce working.
+The wiki app is usable as is and also works fine when used inside an existing
+grok application. Transformation/Wikification of page contents through adapters
+could easily be made into a plugin-like configuration (using a distinct admin
+form). Also the renderer (TinyMCE and HTML) could be pluggable replaced by a
+RestructuredText based engine.
More information about the Checkins
mailing list