terom@5: terom@7: """ terom@7: Handling page requests terom@7: """ terom@7: terom@8: # for filesystem ops terom@8: import os, os.path terom@7: terom@8: # for ResponseError terom@8: import http terom@8: terom@8: # for TemplatePage terom@8: import template terom@8: terom@8: # path to directory containing the page heirarcy terom@8: PAGE_DIR = "pages" terom@8: terom@9: # path to directory containing the list of visible pages terom@9: PAGE_LIST_FILE = os.path.join(PAGE_DIR, "list") terom@9: terom@8: class PageError (http.ResponseError) : terom@8: """ terom@8: Error looking up/handling a page terom@8: """ terom@8: terom@8: pass terom@8: terom@9: class PageList (object) : terom@9: """ terom@9: The list of pages terom@9: """ terom@9: terom@9: def __init__ (self) : terom@9: """ terom@9: Loads the page list from the list file terom@9: """ terom@9: terom@9: # initialize list of pages terom@9: self.pages = [] terom@9: terom@9: # load from file terom@9: self._load(PAGE_LIST_FILE) terom@9: terom@9: def _load (self, path) : terom@9: """ terom@9: Processes the lines in the given file terom@9: """ terom@9: terom@9: for line in open(path, 'rb') : terom@9: # ignore whitespace terom@9: line = line.strip() terom@9: terom@9: # ignore empty lines terom@9: if not line : terom@9: continue terom@9: terom@9: # parse line terom@9: url, title = line.split(':') terom@9: terom@9: # add terom@9: self._add_item(url.strip(), title.strip()) terom@9: terom@9: def _add_item (self, url, title) : terom@9: """ terom@9: Add item to pages list terom@9: """ terom@9: terom@10: # remove index from URL terom@10: if url.endswith('/index') or url == 'index' : terom@10: url = url[:-len('/index')] terom@10: terom@9: self.pages.append((url, title)) terom@9: terom@9: def get_title (self, page) : terom@9: """ terom@9: Gets the title for the given page, or None if not found terom@9: """ terom@9: terom@9: return dict(self.pages).get(page) terom@9: terom@9: def get_siblings (self, page) : terom@9: """ terom@9: Gets the (url, title) tuple list of siblings (including the given page itself) for the given page terom@9: """ terom@9: terom@9: siblings = [] terom@9: terom@9: # parent url terom@9: parent = os.path.split(page.url)[0] terom@9: terom@9: # how many segments in the page name terom@9: segment_count = len(page.url.split('/')) terom@9: terom@9: # go through all pages terom@9: for url, title in self.pages : terom@9: # it's a sibling if the parent is the same, and the number of segments it the same terom@9: if url.startswith(parent) and len(url.split('/')) == segment_count : terom@9: siblings.append((url, title)) terom@9: terom@9: # return terom@9: return siblings terom@9: terom@9: # global singleton instance terom@9: page_list = PageList() terom@9: terom@8: class Page (object) : terom@8: """ terom@8: This object represents the information about our attempt to render some specific page terom@8: """ terom@8: terom@8: def __init__ (self, url, path, basename, url_tail) : terom@8: """ terom@8: Initialize the page at the given location terom@8: terom@8: @param url the URL leading to this page terom@8: @param path the filesystem path to this page's file terom@8: @param basename the filesystem name of this page's file, without the file extension terom@8: @param url_trail trailing URL for this page terom@8: """ terom@8: terom@8: # store terom@8: self.url = url terom@8: self.path = path terom@8: self.basename = basename terom@8: self.url_tail = url_tail terom@8: terom@10: # unbound terom@10: self.request = None terom@10: terom@8: # sub-init terom@8: self._init() terom@8: terom@8: def _init (self) : terom@8: """ terom@8: Do initial data loading, etc terom@8: """ terom@8: terom@8: pass terom@8: terom@10: def bind_request (self, request) : terom@10: """ terom@10: Bind this page-render to the given request terom@10: """ terom@10: terom@10: self.request = request terom@10: terom@9: @property terom@9: def title (self) : terom@8: """ terom@8: Return the page's title terom@8: terom@9: Defaults to the retreiving the page title from page_list, or basename in Titlecase. terom@8: """ terom@9: terom@9: # lookup in page_list terom@9: title = page_list.get_title(self.url) terom@9: terom@9: # fallback to titlecase terom@9: if not title : terom@9: title = self.basename.title() terom@8: terom@9: return title terom@9: terom@9: @property terom@9: def content (self) : terom@8: """ terom@8: Return the page content as a string terom@8: """ terom@8: terom@8: abstract terom@8: terom@8: class HTMLPage (Page) : terom@8: """ terom@8: A simple .html page that's just passed through directly terom@8: """ terom@8: terom@9: @property terom@9: def content (self) : terom@8: """ terom@8: Opens the .html file, reads and returns contents terom@8: """ terom@8: terom@8: return open(self.path, 'rb').read() terom@8: terom@8: class TemplatePage (Page) : terom@8: """ terom@8: A template that's rendered using our template library terom@8: """ terom@9: terom@9: @property terom@9: def content (self) : terom@8: """ terom@8: Loads the .tmpl file, and renders it terom@8: """ terom@8: terom@10: return template.render_file(self.path, terom@10: request = self.request, terom@10: ) terom@7: terom@7: # list of page handlers, by type terom@8: TYPE_HANDLERS = [ terom@8: ('html', HTMLPage ), terom@8: (template.TEMPLATE_EXT, TemplatePage ), terom@7: ] terom@7: terom@8: def _lookup_handler (url, path, filename, basename, extension, tail) : terom@5: """ terom@8: We found the file that we looked for, now get its handler terom@5: """ terom@5: terom@8: # find appropriate handler terom@8: for handler_ext, handler in TYPE_HANDLERS : terom@8: # match against file extension? terom@8: if handler_ext == extension : terom@8: # found handler, return instance terom@8: return handler(url, path, basename, tail) terom@5: terom@8: # no handler found terom@8: raise PageError("No handler found for page %r of type %r" % (url, extension)) terom@8: terom@8: def lookup (name) : terom@8: """ terom@8: Look up and return a Page object for the given page, or raise an error terom@8: """ terom@8: terom@8: # inital path terom@8: path = PAGE_DIR terom@8: url_segments = [] terom@8: terom@8: # name segments terom@8: segments = name.split('/') terom@8: terom@8: # iterate through the parts of the page segments terom@10: while True : terom@10: segment = None terom@10: terom@8: # pop segment terom@10: if segments : terom@10: segment = segments.pop(0) terom@8: terom@10: url_segments.append(segment) terom@8: terom@8: # translate empty -> index terom@8: if not segment : terom@8: segment = 'index' terom@10: terom@8: # look for it in the dir terom@8: for filename in os.listdir(path) : terom@8: # build full file path terom@8: file_path = os.path.join(path, filename) terom@8: terom@8: # stat, recurse into subdirectory? terom@8: if os.path.isdir(file_path) and filename == segment : terom@8: # use new dir terom@8: path = file_path terom@8: terom@8: # break for-loop to look at next segment terom@8: break terom@8: terom@8: # split into basename + extension terom@8: basename, extension = os.path.splitext(filename) terom@8: terom@8: # ...remove that dot terom@8: extension = extension.lstrip('.') terom@8: terom@8: # match against requested page name? terom@8: if basename == segment : terom@8: # found the file we wanted terom@8: return _lookup_handler('/'.join(url_segments), file_path, filename, basename, extension, '/'.join(segments)) terom@8: terom@8: else : terom@8: # inspect next file in dir terom@8: continue terom@8: terom@10: else : terom@10: # did not find any dir or file, break out of while loop terom@10: break terom@10: terom@8: # did not find the filename we were looking for in os.listdir terom@8: raise PageError("Page not found: %s" % name, status='404 Not Found') terom@8: