I've done two previous Webware-based applications, and I'm slowly getting a sense of how to set up a good base to build upon. This uniform site behaviour is in a site class (which I unimaginatively call WebWarePage) inheriting from the Page class, and deals with site appearance and login and admin areas.

Site appearance I achieve by having at least two Cheetah templates. The first deals with the outer part of the page: the banner, title, navigation, and footer. It's what surrounds where the differentiating factor of this page is (aka content). By passing the page object to the Cheetah template (searchList = self), you can control how the navigation is handled without complication - pages can include related pages in the outer navigation just by providing a function that adds a new menu to the navigation.

The second template is the content template. This one changes depending on the page. It defaults to a basic content template, that simply displays the results of the contents() method on the page. This means informational pages are arbitrary.

We can make it simple for pages to require that the viewer are members of the site (ie, logged in), or have admin privileges. This is as simple as adding require_login and require_admin attributes to your page object.

And then a mostly-cosmetic thing. Webware makes it trivial to use path information after the page as input. But by default, it's an all or nothing thing. With the site page, I allow the page to inform whether it will handle additional path information. If not, give an error. No trainsmash if it just works as if the path info wasn't there, but it's one of those things that irritate some people.

Here's the base of my site page. Once I've announced the project (not hard to figure what it's named), I'll post the rest and the page template I'm using (using the skidoo_too style sheet).

from Cheetah.Template import Template
from WebKit.Page import Page
from WebKit.HTTPExceptions import *

import os.path
import string
import sys

from templates import *
from samoosa import user

class WebwarePage(Page):
    handleExtraPathInfo = False
    template = WebwareTemplate
    contentTemplate = BasicContentTemplate
    require_login = False
    require_admin = False

    def contents(self):
        return "Hey there!"

    def content_template(self):
        return self.contentTemplate(searchList = [self]).respond()

    def title(self):
        return "> Home"

    def banner(self):
        return "samoosa"

    def _respond(self, transaction):
        if self.request().extraURLPath() and not self.request().extraURLPath() == "/":
            if self.handleExtraPathInfo == False:
                raise HTTPNotFound
        Page._respond(self, transaction)

    def writeHTML(self):
        session = self.session()
        request = self.request()
        trans = self.transaction()
        app = self.application()

        # Get login id and clear it from the session
        #
        # Get it now, because we clear session values on login/logout below
        loginid = session.value('loginid', None)
        if loginid:
            session.delValue('loginid')

        if request.hasField('logout'):
            session.values().clear()
            del request.fields()['logout']
            request.fields()['extra'] = 'You have been logged out.'

        if request.hasField('login') and request.hasField('username') and request.hasField('password'):
            session.values().clear()

            # Make sure we never get back here with app.forward
            del request.fields()['login']

            reqloginid = request.field('loginid', 'nologin')

            # Check if this is a valid user/password
            username = request.field('username')
            password = request.field('password')

            if not (self.isValidUserAndPassword(username, password)):
                # Failed login attempt; have them try again
                request.fields()['extra'] = 'Login failed.  Please try again.'
                print "Login Failed"
                app.forward(trans, '/Login')
                return

            if reqloginid != loginid:
                request.fields()['extra'] = 'Login failed (loginid).  Please try again.'
                print "Login Failed"
                app.forward(trans, '/Login')
                return

            session.setValue('authenticated_user_admin', username)
            request.fields()['extra'] = 'You are now logged in.'
            if request.urlPath() in ['/Login', '/Logout']:
                self.sendRedirectAndEnd('/')

        if not session.value('authenticated_user_admin', None) and self.require_login:
            # They need to log in.
            session.values().clear()
            request.fields()['extra'] = 'Login required.'
            app.forward(trans, '/Login')
            return

        if self.require_admin:
            username = session.value('authenticated_user_admin')
            try:
                u = user.User.byUsername(username)
            except:
                app.forward(trans, '/AdminRequired')
                pass

            if not u.admin:
                app.forward(trans, '/AdminRequired')
                return

        return self.writeContents()

    def writeContents(self):
        t = self.template(searchList=[self])
        self.write(t.respond())

    def footer(self):
        return "Footer<br />Part two"

    def isValidUserAndPassword(self, username, password):
        try:
            u = user.User.byUsername(username)
        except:
            return 0

        if u.password == password:
            return 1

        return 0