Source code for ferenda.sources.general.static

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os

from rdflib import URIRef, Graph, Literal
import pkg_resources

from docutils.core import publish_doctree

from ferenda import DocumentRepository
from ferenda import DocumentStore
from ferenda import util
from ferenda.decorators import managedparsing
from ferenda import elements


class StaticStore(DocumentStore):

    """Customized DocumentStore that looks for all "downloaded" resources
    from the specified ``staticdir``. If ``staticdir`` isn't provided
    or doesn't exist, falls back to a collection of package resources
    (under ferenda/res/static-content). Parsed, generated etc files
    are handled like normal, ie stored under
    ``[datadir]/static/{parsed,distilled,generated,...}/``
    """

    def __init__(self, datadir, downloaded_suffix=".rst", storage_policy="file", staticdir="static"):
        super(StaticStore, self).__init__(datadir, downloaded_suffix, storage_policy)
        if os.path.exists(staticdir):
            self.staticdir = staticdir
        else:
            # find out the path of our resourcedir
            p = pkg_resources.resource_filename('ferenda',
                                                'res/static-content/README')
            self.staticdir = os.path.dirname(p)

    def downloaded_path(self, basefile, version=None, attachment=None):
        segments = [self.staticdir,
                    self.basefile_to_pathfrag(basefile) + self.downloaded_suffix]
        return "/".join(segments).replace("/", os.sep)

    def list_basefiles_for(self, action, basedir=None):
        if action == "parse":
            for x in util.list_dirs(self.staticdir, self.downloaded_suffix):
                pathfrag = x[len(self.staticdir) + 1:-len(self.downloaded_suffix)]
                yield self.pathfrag_to_basefile(pathfrag)
        else:
            for x in super(StaticStore, self).list_basefiles_for(action, basedir):
                yield x


[docs]class Static(DocumentRepository): """Generates documents from your own ``.rst`` files The primary purpose of this docrepo is to provide a small set of static pages for a complete ferenda-based web site, like "About us", "Contact information", "Terms of service" or whatever else you need. The ``download`` step of this docrepo does not do anything, and it's ``parse`` step reads ReStructuredText (``.rst``) files from a local directory and converts them into XHTML+RDFa. From that point on, it works just like any other docrepo. After enabling this, you should set the configuration parameter ``staticdir`` to the path of a directory where you keep your ``.rst`` files:: [static] class = ferenda.sources.general.Static staticdir = /var/www/mysite/static/rst .. note:: If this configuration parameter is not set, this docrepo will use a small set of generic static pages, stored under ``ferenda/res/static-pages`` in the distribution. To get started, you can just copy this directory and set ``staticdir`` to point at your copy. Every file present in ``staticdir`` results in a link in the site footer. The link text will be the title of the document, i.e. the first header in the ``.rst`` file. """ alias = "static" downloaded_suffix = ".rst" documentstore_class = StaticStore # urls become on the form "http://localhost:8000/static/about" def download(self): pass @managedparsing def parse(self, doc): source = util.readfile(self.store.downloaded_path(doc.basefile)) doctree = publish_doctree(source=source) stack = [] root = self._transform(doctree, stack) if isinstance(root[0], elements.Title): doc.meta.add((URIRef(doc.uri), self.ns['dcterms'].title, Literal(str(root[0]), doc.lang))) root.pop(0) doc.body = root return True # converts a tree of docutils.nodes into ferenda.elements def _transform(self, node, stack): cls = {'document': elements.Body, 'title': elements.Title, 'paragraph': elements.Paragraph, '#text': str }.get(node.tagname, elements.CompoundElement) if hasattr(node, 'attributes'): attrs = dict((k, v) for (k, v) in node.attributes.items() if v) el = cls(**attrs) else: el = cls(node) # ! if len(stack) > 0: top = stack[-1] top.append(el) if hasattr(node, 'attributes'): stack.append(el) for childnode in node: self._transform(childnode, stack) return stack.pop() def toc(self, otherrepos=[]): pass def news(self, otherrepos=[]): pass def frontpage_content(self, primary=False): pass def tabs(self): if os.path.exists(self.store.parsed_path("about")): return [("About", self.canonical_uri("about"))] else: return[] def footer(self): # FIXME: ordering? res = [] for basefile in self.store.list_basefiles_for("generate"): uri = self.canonical_uri(basefile) g = Graph() g.parse(self.store.distilled_path(basefile)) title = g.value(URIRef(uri), self.ns['dcterms'].title).toPython() if not title: title = basefile res.append((title, uri)) return res