From 7127676f714195ff1cfa28fa3790c8bde02bcc8b Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Mon, 5 Mar 2012 15:52:14 +0100 Subject: [PATCH 1/6] added a static pages generator Conflicts: pelican/__init__.py pelican/generators.py pelican/settings.py --- pelican/__init__.py | 6 ++++-- pelican/generators.py | 33 +++++++++++++++++++++++++++++++-- pelican/settings.py | 1 + 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 9809b19b..2a8c0703 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -9,7 +9,8 @@ from pelican import signals from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, PdfGenerator, - LessCSSGenerator, SourceFileGenerator) + LessCSSGenerator, SourceFileGenerator, + StaticPageGenerator) from pelican.log import init from pelican.settings import read_settings from pelican.utils import (clean_output_dir, files_changed, file_changed, @@ -170,7 +171,8 @@ class Pelican(object): signals.finalized.send(self) def get_generator_classes(self): - generators = [StaticGenerator, ArticlesGenerator, PagesGenerator] + generators = [StaticGenerator, ArticlesGenerator, PagesGenerator, + StaticPageGenerator] if self.settings['PDF_GENERATOR']: generators.append(PdfGenerator) if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc diff --git a/pelican/generators.py b/pelican/generators.py index c38409a1..81bcbc97 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -5,6 +5,7 @@ import random import logging import datetime import subprocess +from os.path import exists, getmtime from codecs import open from collections import defaultdict @@ -12,8 +13,8 @@ from functools import partial from itertools import chain from operator import attrgetter, itemgetter -from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader -from jinja2.exceptions import TemplateNotFound +from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, + BaseLoader, TemplateNotFound) from pelican.contents import Article, Page, Category, is_valid_content from pelican.readers import read_file @@ -110,6 +111,34 @@ class Generator(object): self.context[item] = value +class _FileLoader(BaseLoader): + + def __init__(self, path): + self.path = path + + def get_source(self, environment, template): + path = template + if not exists(path): + raise TemplateNotFound(template) + mtime = getmtime(path) + with file(path) as f: + source = f.read().decode('utf-8') + return source, path, lambda: mtime == getmtime(path) + + +class StaticPageGenerator(Generator): + + def generate_output(self, writer): + for urlpath, source in self.settings['STATIC_PAGES'].items(): + self.env.loader.loaders.insert(0, _FileLoader(source)) + try: + template = self.env.get_template(source) + rurls = self.settings.get('RELATIVE_URLS') + writer.write_file(urlpath.strip('/'), template, self.context, rurls) + finally: + del self.env.loader.loaders[0] + + class ArticlesGenerator(Generator): """Generate blog articles""" diff --git a/pelican/settings.py b/pelican/settings.py index 610d0733..a3f94e24 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -80,6 +80,7 @@ _DEFAULT_CONFIG = {'PATH': '.', 'WEBASSETS': False, 'PLUGINS': [], 'MARKDOWN_EXTENSIONS': ['toc', ], + 'STATIC_PAGES': {} } From a91f1cab5d1ccb92cf33452f7f065b15825e20b8 Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Fri, 16 Mar 2012 21:12:41 +0100 Subject: [PATCH 2/6] activate the static page generator only if the option is used Conflicts: pelican/__init__.py --- pelican/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 2a8c0703..64a61e0c 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -171,8 +171,10 @@ class Pelican(object): signals.finalized.send(self) def get_generator_classes(self): - generators = [StaticGenerator, ArticlesGenerator, PagesGenerator, - StaticPageGenerator] + generators = [StaticGenerator, ArticlesGenerator, PagesGenerator] + + if self.settings['STATIC_PAGES']: + generators.append(StaticPageGenerator) if self.settings['PDF_GENERATOR']: generators.append(PdfGenerator) if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc From 083a8cc172c3b0832d2b2afe2b217e4d593eb7cf Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Sun, 18 Mar 2012 09:59:12 +0100 Subject: [PATCH 3/6] added some doc about STATIC_PAGES Conflicts: docs/settings.rst --- docs/settings.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index 1e3704d2..749e0431 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -92,6 +92,9 @@ Setting name (default value) What doe will not be generated with properly-formed URLs. You should include ``http://`` and your domain, with no trailing slash at the end. Example: ``SITEURL = 'http://mydomain.com'`` +`STATIC_PAGES` (``None``) A mapping containing static pages that will + be rendered with the blog entries. See + :ref:`static_pages`. `STATIC_PATHS` (``['images']``) The static paths you want to have accessible on the output path "static". By default, Pelican will copy the 'images' folder to the @@ -266,6 +269,25 @@ can get a list of available locales via the ``locale -a`` command; see manpage .. _locale(1): http://linux.die.net/man/1/locale + +.. _static_pages: + +Static pages +============ + +If you want to generate static pages besides your blog entries, you can point +any HTML or HTML template file with a path pointing to the file and the +URL it will match. + +For instance, if you have a blog with four static pages, for a list of +books, your resume and a contact page, you could have:: + + STATIC_PAGES = {'/books.html': 'static/books.html', + '/resume.html': 'static/resume.html', + '/contact.html': 'static/contact.html'} + + + Feed settings ============= From e0e1b3eecff7941324df065271af1f95af3855ad Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 30 Oct 2012 00:27:18 +0100 Subject: [PATCH 4/6] rename STATIC_PAGES to TEMPLATE_PAGES --- docs/settings.rst | 25 +++++++++++-------------- pelican/__init__.py | 6 +++--- pelican/generators.py | 7 ++++--- pelican/settings.py | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 749e0431..bf9ac2ac 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -92,9 +92,8 @@ Setting name (default value) What doe will not be generated with properly-formed URLs. You should include ``http://`` and your domain, with no trailing slash at the end. Example: ``SITEURL = 'http://mydomain.com'`` -`STATIC_PAGES` (``None``) A mapping containing static pages that will - be rendered with the blog entries. See - :ref:`static_pages`. +`TEMPLATE_PAGES` (``None``) A mapping containing template pages that will be rendered with + the blog entries. See :ref:`template_pages`. `STATIC_PATHS` (``['images']``) The static paths you want to have accessible on the output path "static". By default, Pelican will copy the 'images' folder to the @@ -270,24 +269,22 @@ can get a list of available locales via the ``locale -a`` command; see manpage .. _locale(1): http://linux.die.net/man/1/locale -.. _static_pages: +.. _template_pages: -Static pages -============ +Template pages +============== -If you want to generate static pages besides your blog entries, you can point -any HTML or HTML template file with a path pointing to the file and the -URL it will match. +If you want to generate custom pages besides your blog entries, you can point +any Jinja2 template file with a path pointing to the file and the URL it will +match. -For instance, if you have a blog with four static pages, for a list of -books, your resume and a contact page, you could have:: +For instance, if you have a blog with four static pages, for a list of books, +your resume and a contact page, you could have:: - STATIC_PAGES = {'/books.html': 'static/books.html', + TEMPLATE_PAGES = {'/books.html': 'static/books.html', '/resume.html': 'static/resume.html', '/contact.html': 'static/contact.html'} - - Feed settings ============= diff --git a/pelican/__init__.py b/pelican/__init__.py index 64a61e0c..804fe5b4 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -10,7 +10,7 @@ from pelican import signals from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, PdfGenerator, LessCSSGenerator, SourceFileGenerator, - StaticPageGenerator) + TemplatePagesGenerator) from pelican.log import init from pelican.settings import read_settings from pelican.utils import (clean_output_dir, files_changed, file_changed, @@ -173,8 +173,8 @@ class Pelican(object): def get_generator_classes(self): generators = [StaticGenerator, ArticlesGenerator, PagesGenerator] - if self.settings['STATIC_PAGES']: - generators.append(StaticPageGenerator) + if self.settings['TEMPLATE_PAGES']: + generators.append(TemplatePagesGenerator) if self.settings['PDF_GENERATOR']: generators.append(PdfGenerator) if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc diff --git a/pelican/generators.py b/pelican/generators.py index 81bcbc97..c7b29bab 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -126,15 +126,16 @@ class _FileLoader(BaseLoader): return source, path, lambda: mtime == getmtime(path) -class StaticPageGenerator(Generator): +class TemplatePagesGenerator(Generator): def generate_output(self, writer): - for urlpath, source in self.settings['STATIC_PAGES'].items(): + for urlpath, source in self.settings['TEMPLATE_PAGES'].items(): self.env.loader.loaders.insert(0, _FileLoader(source)) try: template = self.env.get_template(source) rurls = self.settings.get('RELATIVE_URLS') - writer.write_file(urlpath.strip('/'), template, self.context, rurls) + writer.write_file( + urlpath.strip('/'), template, self.context, rurls) finally: del self.env.loader.loaders[0] diff --git a/pelican/settings.py b/pelican/settings.py index a3f94e24..35752fee 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -80,7 +80,7 @@ _DEFAULT_CONFIG = {'PATH': '.', 'WEBASSETS': False, 'PLUGINS': [], 'MARKDOWN_EXTENSIONS': ['toc', ], - 'STATIC_PAGES': {} + 'TEMPLATE_PAGES': {} } From 54eee3f28abdc82d16cee3a5edfbb5e075dc9acd Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 30 Oct 2012 02:33:01 +0100 Subject: [PATCH 5/6] update TemplatePagesGenerator: * bugfix: now supports custom path for path where to find the content files * TEMPLATE_PAGES settings is now of the form: { 'jinja2/src/file.html': 'dest/file.html' } * update doc --- docs/settings.rst | 12 ++++++------ pelican/generators.py | 21 ++++++++++----------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index bf9ac2ac..55becc8b 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -275,15 +275,15 @@ Template pages ============== If you want to generate custom pages besides your blog entries, you can point -any Jinja2 template file with a path pointing to the file and the URL it will -match. +any Jinja2 template file with a path pointing to the file and the destination +path for the generated file. -For instance, if you have a blog with four static pages, for a list of books, +For instance, if you have a blog with three static pages, for a list of books, your resume and a contact page, you could have:: - TEMPLATE_PAGES = {'/books.html': 'static/books.html', - '/resume.html': 'static/resume.html', - '/contact.html': 'static/contact.html'} + TEMPLATE_PAGES = {'src/books.html': 'dest/books.html', + 'src/resume.html': 'dest/resume.html', + 'src/contact.html': 'dest/contact.html'} Feed settings ============= diff --git a/pelican/generators.py b/pelican/generators.py index c7b29bab..f056484a 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -5,7 +5,6 @@ import random import logging import datetime import subprocess -from os.path import exists, getmtime from codecs import open from collections import defaultdict @@ -113,29 +112,29 @@ class Generator(object): class _FileLoader(BaseLoader): - def __init__(self, path): + def __init__(self, path, basedir): self.path = path + self.fullpath = os.path.join(basedir, path) def get_source(self, environment, template): - path = template - if not exists(path): + if template != self.path or not os.path.exists(self.fullpath): raise TemplateNotFound(template) - mtime = getmtime(path) - with file(path) as f: + mtime = os.path.getmtime(self.fullpath) + with file(self.fullpath) as f: source = f.read().decode('utf-8') - return source, path, lambda: mtime == getmtime(path) + return source, self.fullpath, \ + lambda: mtime == os.path.getmtime(self.fullpath) class TemplatePagesGenerator(Generator): def generate_output(self, writer): - for urlpath, source in self.settings['TEMPLATE_PAGES'].items(): - self.env.loader.loaders.insert(0, _FileLoader(source)) + for source, dest in self.settings['TEMPLATE_PAGES'].items(): + self.env.loader.loaders.insert(0, _FileLoader(source, self.path)) try: template = self.env.get_template(source) rurls = self.settings.get('RELATIVE_URLS') - writer.write_file( - urlpath.strip('/'), template, self.context, rurls) + writer.write_file(dest, template, self.context, rurls) finally: del self.env.loader.loaders[0] From 3734c48489fc07cc57b61a462cb2ca0489a7746f Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 30 Oct 2012 02:33:22 +0100 Subject: [PATCH 6/6] add test for TemplatePagesGenerator --- tests/test_generators.py | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tests/test_generators.py b/tests/test_generators.py index 8caa7213..accdb699 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -6,7 +6,9 @@ import re from tempfile import mkdtemp from shutil import rmtree -from pelican.generators import ArticlesGenerator, LessCSSGenerator, PagesGenerator +from pelican.generators import ArticlesGenerator, LessCSSGenerator, \ + PagesGenerator, TemplatePagesGenerator +from pelican.writers import Writer from pelican.settings import _DEFAULT_CONFIG from .support import unittest, skipIfNoExecutable @@ -194,6 +196,50 @@ class TestPageGenerator(unittest.TestCase): self.assertItemsEqual(hidden_pages_expected,hidden_pages) +class TestTemplatePagesGenerator(unittest.TestCase): + + TEMPLATE_CONTENT = "foo: {{ foo }}" + + def setUp(self): + self.temp_content = mkdtemp() + self.temp_output = mkdtemp() + + def tearDown(self): + rmtree(self.temp_content) + rmtree(self.temp_output) + + def test_generate_output(self): + + settings = _DEFAULT_CONFIG.copy() + settings['STATIC_PATHS'] = ['static'] + settings['TEMPLATE_PAGES'] = { + 'template/source.html': 'generated/file.html' + } + + generator = TemplatePagesGenerator({'foo': 'bar'}, settings, + self.temp_content, '', self.temp_output, None) + + # create a dummy template file + template_dir = os.path.join(self.temp_content, 'template') + template_filename = os.path.join(template_dir, 'source.html') + os.makedirs(template_dir) + with open(template_filename, 'w') as template_file: + template_file.write(self.TEMPLATE_CONTENT) + + writer = Writer(self.temp_output, settings=settings) + generator.generate_output(writer) + + output_filename = os.path.join( + self.temp_output, 'generated', 'file.html') + + # output file has been generated + self.assertTrue(os.path.exists(output_filename)) + + # output content is correct + with open(output_filename, 'r') as output_file: + self.assertEquals(output_file.read(), 'foo: bar') + + class TestLessCSSGenerator(unittest.TestCase): LESS_CONTENT = """