lib/page.py
author Tero Marttila <terom@fixme.fi>
Fri, 06 Feb 2009 22:48:00 +0200
changeset 8 0ce1f471e9d7
parent 7 d6a8258bd90e
child 9 2a47b00f60b0
permissions -rw-r--r--
and it works, a lot better than before

"""
    Handling page requests
"""

# for filesystem ops
import os, os.path

# for ResponseError
import http

# for TemplatePage
import template

# path to directory containing the page heirarcy
PAGE_DIR = "pages"

class PageError (http.ResponseError) :
    """
        Error looking up/handling a page
    """

    pass

class Page (object) :
    """
        This object represents the information about our attempt to render some specific page
    """

    def __init__ (self, url, path, basename, url_tail) :
        """
            Initialize the page at the given location

            @param url the URL leading to this page
            @param path the filesystem path to this page's file
            @param basename the filesystem name of this page's file, without the file extension
            @param url_trail trailing URL for this page
        """
        
        # store
        self.url = url
        self.path = path
        self.basename = basename
        self.url_tail = url_tail

        # sub-init
        self._init()

    def _init (self) :
        """
            Do initial data loading, etc
        """
        
        pass

    def get_title (self) :
        """
            Return the page's title

            Defaults to the Titlecase'd file basename
        """

        return self.basename.title()

    def get_content (self) :
        """
            Return the page content as a string
        """

        abstract

class HTMLPage (Page) :
    """
        A simple .html page that's just passed through directly
    """

    def get_content (self) :
        """
            Opens the .html file, reads and returns contents
        """

        return open(self.path, 'rb').read()

class TemplatePage (Page) :
    """
        A template that's rendered using our template library
    """

    def get_content (self) :
        """
            Loads the .tmpl file, and renders it
        """

        return template.render_file(self.path)

# list of page handlers, by type
TYPE_HANDLERS = [
    ('html',                    HTMLPage        ),
    (template.TEMPLATE_EXT,     TemplatePage    ),
]

def _lookup_handler (url, path, filename, basename, extension, tail) :
    """
        We found the file that we looked for, now get its handler
    """

    # find appropriate handler
    for handler_ext, handler in TYPE_HANDLERS :
        # match against file extension?
        if handler_ext == extension :
            # found handler, return instance
            return handler(url, path, basename, tail)

    # no handler found
    raise PageError("No handler found for page %r of type %r" % (url, extension))

def lookup (name) :
    """
        Look up and return a Page object for the given page, or raise an error
    """

    # inital path
    path = PAGE_DIR
    url_segments = []

    # name segments
    segments = name.split('/')

    # iterate through the parts of the page segments
    while segments :
        # pop segment
        segment = segments.pop(0)

        # add to page url
        url_segments.append(segment)

        # translate empty -> index
        if not segment :
            segment = 'index'
        
        # look for it in the dir
        for filename in os.listdir(path) :
            # build full file path
            file_path = os.path.join(path, filename)

            # stat, recurse into subdirectory?
            if os.path.isdir(file_path) and filename == segment :
                # use new dir
                path = file_path

                # break for-loop to look at next segment
                break
 
            # split into basename + extension
            basename, extension = os.path.splitext(filename)

            # ...remove that dot
            extension = extension.lstrip('.')
            
            # match against requested page name?
            if basename == segment :
                # found the file we wanted
                return _lookup_handler('/'.join(url_segments), file_path, filename, basename, extension, '/'.join(segments))
            
            else :
                # inspect next file in dir
                continue

    # did not find the filename we were looking for in os.listdir
    raise PageError("Page not found: %s" % name, status='404 Not Found')