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@21: import time terom@7: terom@8: # for ResponseError terom@8: import http terom@8: terom@8: # for TemplatePage terom@8: import template terom@8: terom@16: from page_tree import page_tree terom@26: import config terom@16: terom@8: # path to directory containing the page heirarcy terom@8: PAGE_DIR = "pages" terom@8: 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@11: # XXX: should inherit from PageInfo 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@21: def __init__ (self, url, path, basename, url_tail, charset='utf8') : 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@21: @param charset file charset 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@21: self.charset = charset 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@11: page_info = page_tree.get_page(self.url) terom@9: terom@9: # fallback to titlecase terom@11: if page_info : terom@11: title = page_info.title terom@11: terom@11: else : 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@21: terom@21: @property terom@21: def modified (self) : terom@21: """ terom@21: Returns the page modification timestamp terom@21: """ terom@21: terom@21: # stat terom@21: timestamp = os.stat(self.path).st_mtime terom@21: terom@26: return time.strftime(config.DATETIME_FMT, time.gmtime(timestamp)) 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@21: return open(self.path, 'rb').read().decode(self.charset) 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@11: page_tree = page_tree 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: