qmsk_www_pages/pages.py
author Tero Marttila <terom@paivola.fi>
Sun, 14 Sep 2014 02:15:25 +0300
changeset 202 da5de3a97efb
parent 201 edcbbc94f90d
child 68 023b9a9d6c76
permissions -rw-r--r--
pages: Site, footer with modified, basic css styles, page tree types
from django.conf import settings

import codecs
import datetime
import logging; log = logging.getLogger('qmsk_www_pages.pages')
import os, os.path

class Site (object):
    @classmethod
    def lookup (cls):
        return cls(
            root        = settings.QMSK_WWW_PAGES_DIR,
            name        = settings.QMSK_WWW_PAGES_SITE,
        )

    def __init__ (self, root, name):
        self.root = root
        self.name = name

class Tree (object):
    INDEX = 'index'

    @classmethod
    def lookup (cls, site, parts):
        path = site.root

        for part in parts:
            if part.startswith('.'):
                # evil
                return None
            
            if not part:
                continue
        
            path = os.path.join(path, part)

            if not os.path.exists(path):
                return None
            
            if not os.path.isdir(path):
                return None
        
        return cls(path, parts, site)

    def __init__ (self, path, parts, site):
        self.path = path
        self.parts = parts
        self.site = site

    def url (self, tree=None, page=None):
        path = '/'.join(self.parts)

        if path:
            path += '/'

        if tree:
            path = tree + '/'

        if page:
            path += page

        return path

    def breadcrumb (self):
        path = []

        yield '', self.site.name

        for part in self.parts:
            path.append(part)

            yield '/'.join(path) + '/', part

    def scan (self):
        """
            Scan for files in tree.
        """

        for filename in os.listdir(self.path):
            if filename.startswith('.'):
                continue
            
            if '.' in filename:
                file_name, file_type = filename.rsplit('.', 1)
            else:
                file_name = filename
                file_type = None

            if not file_name:
                continue

            path = os.path.join(self.path, filename)
            
            yield path, file_name, file_type

    def list (self):
        """
            Lists all Trees and Pages for this Tree
        """
        
        for path, file_name, file_type in self.scan():
            # trees
            if os.path.isdir(path):
                yield self.url(tree=file_name), file_name, None

            if file_name == self.INDEX:
                continue
            
            # pages
            if not file_type:
                continue

            if file_type not in TYPES:
                continue
            
            yield self.url(page=file_name), file_name, file_type

    def page (self, name):
        """
            Scans through tree looking for a matching page.
            
            Returns Page.
        """
        
        if not name:
            name = self.INDEX

        for path, file_name, file_type in self.scan():
            # match on name
            if file_name != name:
                continue
            
            # match on type
            if not file_type:
                continue

            page_type = TYPES.get(file_type)

            if not page_type:
                continue
            
            # out
            return page_type(
                path    = path,
                name    = name,
                tree    = self,
            )

class Page (object):
    ENCODING = 'utf-8'

    @classmethod
    def lookup (cls, site, page):
        """
            Lookup a Page from disk.

            Returns None if there is no such page.
        """
        
        log.info("page=%r", page)

        if page:
            parts = page.split('/')
        else:
            parts = [ ]
            
        if parts:
            page_name = parts.pop(-1)
            tree_parts = parts
        else:
            page_name = ''
            tree_parts = []

        # scan dir
        tree = Tree.lookup(site, tree_parts)

        if not tree:
            return None

        # scan page
        page = tree.page(page_name)

        if not page:
            return None

        return page

    def __init__ (self, path, name, tree, encoding=ENCODING):
        self.path = path
        self.name = name
        self.tree = tree
        self.encoding = encoding

    def url (self):
        return self.tree.url(page=self.name)

    def open (self):
        return codecs.open(self.path, encoding=self.encoding)

    def stat (self):
        return os.stat(self.path)

    def breadcrumb (self):
        for path, name in self.tree.breadcrumb():
            yield path, name
        
        if self.name != self.tree.INDEX:
            yield self.url(), self.name

    def modified (self):
        return datetime.datetime.utcfromtimestamp(self.stat().st_mtime)

    def render (self, request):
        raise NotImplementedError()

class HTML_Page (Page):
    def render (self, request):
        return self.open().read()

SITE = Site.lookup()

TYPES = {
    'html':         HTML_Page,
}

def page (page):
    return Page.lookup(SITE, page)