From a6788f83c2304bd5e551cb9e8dd2daf998e5c48b Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 7 May 2012 17:11:57 +0200 Subject: [PATCH 1/5] integrate webassets --- pelican/__init__.py | 5 ++++- pelican/generators.py | 21 +++++++++++++++++---- pelican/settings.py | 9 +++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 6b3d12fb..97e21ccd 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -126,12 +126,15 @@ class Pelican(object): writer = self.get_writer() + generators[1].env.assets_environment = generators[0].assets_env + generators[2].env.assets_environment = generators[0].assets_env + for p in generators: if hasattr(p, 'generate_output'): p.generate_output(writer) def get_generator_classes(self): - generators = [ArticlesGenerator, PagesGenerator, StaticGenerator] + generators = [StaticGenerator, ArticlesGenerator, PagesGenerator] 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 9647d397..00701116 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -42,7 +42,7 @@ class Generator(object): simple_loader = FileSystemLoader(os.path.join(theme_path, "themes", "simple", "templates")) - self._env = Environment( + self.env = Environment( loader=ChoiceLoader([ FileSystemLoader(self._templates_path), simple_loader, # implicit inheritance @@ -51,11 +51,11 @@ class Generator(object): extensions=self.settings.get('JINJA_EXTENSIONS', []), ) - logger.debug('template list: {0}'.format(self._env.list_templates())) + logger.debug('template list: {0}'.format(self.env.list_templates())) # get custom Jinja filters from user settings custom_filters = self.settings.get('JINJA_FILTERS', {}) - self._env.filters.update(custom_filters) + self.env.filters.update(custom_filters) def get_template(self, name): """Return the template by name. @@ -64,7 +64,7 @@ class Generator(object): """ if name not in self._templates: try: - self._templates[name] = self._env.get_template(name + '.html') + self._templates[name] = self.env.get_template(name + '.html') except TemplateNotFound: raise Exception('[templates] unable to load %s.html from %s' \ % (name, self._templates_path)) @@ -364,7 +364,20 @@ class StaticGenerator(Generator): copy(path, source, os.path.join(output_path, destination), final_path, overwrite=True) + def generate_context(self): + + if self.settings['WEBASSETS']: + from webassets import Environment as AssetsEnvironment + + assets_url = self.settings['SITEURL'] + '/theme/' + assets_src = os.path.join(self.output_path, 'theme') + self.assets_env = AssetsEnvironment(assets_src, assets_url) + + if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG": + self.assets_env.debug = True + def generate_output(self, writer): + self._copy_paths(self.settings['STATIC_PATHS'], self.path, 'static', self.output_path) self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, diff --git a/pelican/settings.py b/pelican/settings.py index 4da66989..647d3e93 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -68,6 +68,7 @@ _DEFAULT_CONFIG = {'PATH': '.', 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, 'LESS_GENERATOR': False, + 'WEBASSETS': False, } @@ -150,4 +151,12 @@ def configure_settings(settings, default_settings=None, filename=None): "http://docs.notmyidea.org/alexis/pelican/settings.html#timezone " "for more information") + if settings['WEBASSETS']: + try: + from webassets.ext.jinja2 import AssetsExtension + settings['JINJA_EXTENSIONS'].append(AssetsExtension) + except ImportError: + logger.warn("You must install the webassets module to use WEBASSETS.") + settings['WEBASSETS'] = False + return settings From 252d00834fe81f9954df4ebc78b26edb0dbea2a2 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 7 May 2012 13:05:33 +0200 Subject: [PATCH 2/5] strip tags for feed titles --- pelican/writers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pelican/writers.py b/pelican/writers.py index 593879e2..75971ee9 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -8,8 +8,8 @@ import logging from codecs import open from functools import partial - from feedgenerator import Atom1Feed, Rss201rev2Feed +from jinja2 import Markup from pelican.paginator import Paginator from pelican.utils import get_relative_path, set_date_tzinfo @@ -25,8 +25,9 @@ class Writer(object): def _create_new_feed(self, feed_type, context): feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed + sitename = Markup(context['SITENAME']).striptags() feed = feed_class( - title=context['SITENAME'], + title=sitename, link=(self.site_url + '/'), feed_url=self.feed_url, description=context.get('SITESUBTITLE', '')) @@ -34,8 +35,9 @@ class Writer(object): def _add_item_to_the_feed(self, feed, item): + title = Markup(item.title).striptags() feed.add_item( - title=item.title, + title=title, link='%s/%s' % (self.site_url, item.url), unique_id='tag:%s,%s:%s' % (self.site_url.replace('http://', ''), item.date.date(), item.url), From ec707930ce247f762d7c1a580e3ef6ac974cb0de Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 7 May 2012 17:11:57 +0200 Subject: [PATCH 3/5] integrate webassets --- pelican/__init__.py | 5 ++++- pelican/generators.py | 21 +++++++++++++++++---- pelican/settings.py | 9 +++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 6b3d12fb..97e21ccd 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -126,12 +126,15 @@ class Pelican(object): writer = self.get_writer() + generators[1].env.assets_environment = generators[0].assets_env + generators[2].env.assets_environment = generators[0].assets_env + for p in generators: if hasattr(p, 'generate_output'): p.generate_output(writer) def get_generator_classes(self): - generators = [ArticlesGenerator, PagesGenerator, StaticGenerator] + generators = [StaticGenerator, ArticlesGenerator, PagesGenerator] 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 9647d397..00701116 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -42,7 +42,7 @@ class Generator(object): simple_loader = FileSystemLoader(os.path.join(theme_path, "themes", "simple", "templates")) - self._env = Environment( + self.env = Environment( loader=ChoiceLoader([ FileSystemLoader(self._templates_path), simple_loader, # implicit inheritance @@ -51,11 +51,11 @@ class Generator(object): extensions=self.settings.get('JINJA_EXTENSIONS', []), ) - logger.debug('template list: {0}'.format(self._env.list_templates())) + logger.debug('template list: {0}'.format(self.env.list_templates())) # get custom Jinja filters from user settings custom_filters = self.settings.get('JINJA_FILTERS', {}) - self._env.filters.update(custom_filters) + self.env.filters.update(custom_filters) def get_template(self, name): """Return the template by name. @@ -64,7 +64,7 @@ class Generator(object): """ if name not in self._templates: try: - self._templates[name] = self._env.get_template(name + '.html') + self._templates[name] = self.env.get_template(name + '.html') except TemplateNotFound: raise Exception('[templates] unable to load %s.html from %s' \ % (name, self._templates_path)) @@ -364,7 +364,20 @@ class StaticGenerator(Generator): copy(path, source, os.path.join(output_path, destination), final_path, overwrite=True) + def generate_context(self): + + if self.settings['WEBASSETS']: + from webassets import Environment as AssetsEnvironment + + assets_url = self.settings['SITEURL'] + '/theme/' + assets_src = os.path.join(self.output_path, 'theme') + self.assets_env = AssetsEnvironment(assets_src, assets_url) + + if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG": + self.assets_env.debug = True + def generate_output(self, writer): + self._copy_paths(self.settings['STATIC_PATHS'], self.path, 'static', self.output_path) self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, diff --git a/pelican/settings.py b/pelican/settings.py index 4da66989..647d3e93 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -68,6 +68,7 @@ _DEFAULT_CONFIG = {'PATH': '.', 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, 'LESS_GENERATOR': False, + 'WEBASSETS': False, } @@ -150,4 +151,12 @@ def configure_settings(settings, default_settings=None, filename=None): "http://docs.notmyidea.org/alexis/pelican/settings.html#timezone " "for more information") + if settings['WEBASSETS']: + try: + from webassets.ext.jinja2 import AssetsExtension + settings['JINJA_EXTENSIONS'].append(AssetsExtension) + except ImportError: + logger.warn("You must install the webassets module to use WEBASSETS.") + settings['WEBASSETS'] = False + return settings From e6448567a0a98d9c005c1ab955c45a657b814428 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 11 May 2012 22:19:03 +0200 Subject: [PATCH 4/5] add some doc in the code --- pelican/__init__.py | 6 ++++-- pelican/generators.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 97e21ccd..7e546b29 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -126,8 +126,10 @@ class Pelican(object): writer = self.get_writer() - generators[1].env.assets_environment = generators[0].assets_env - generators[2].env.assets_environment = generators[0].assets_env + # pass the assets environment to the generators + if self.settings['WEBASSETS']: + generators[1].env.assets_environment = generators[0].assets_env + generators[2].env.assets_environment = generators[0].assets_env for p in generators: if hasattr(p, 'generate_output'): diff --git a/pelican/generators.py b/pelican/generators.py index 30a463b4..f15db15d 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -394,6 +394,9 @@ class StaticGenerator(Generator): if self.settings['WEBASSETS']: from webassets import Environment as AssetsEnvironment + # Define the assets environment that will be passed to the + # generators. The StaticGenerator must then be run first to have + # the assets in the output_path before generating the templates. assets_url = self.settings['SITEURL'] + '/theme/' assets_src = os.path.join(self.output_path, 'theme') self.assets_env = AssetsEnvironment(assets_src, assets_url) From fed8e8b331668963d6d5f944211490541274eace Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 12 May 2012 23:48:57 +0200 Subject: [PATCH 5/5] documentation for webassets --- docs/settings.rst | 54 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 85e9f0c3..582cd9d4 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -369,6 +369,7 @@ Setting name (default value) What does it do? value is `static`, but if your theme has other static paths, you can put them here. `CSS_FILE` (``'main.css'``) Specify the CSS file you want to load. +`WEBASSETS` (``False``) Asset management with `webassets` (see below) ================================================ ===================================================== By default, two themes are available. You can specify them using the `-t` option: @@ -418,7 +419,58 @@ adding the following to your configuration:: CSS_FILE = "wide.css" -.. _pelican-themes: :doc:`pelican-themes` +Asset management +---------------- + +The `WEBASSETS` setting allows to use the `webassets`_ module to manage assets +(css, js). The module must first be installed:: + + pip install webassets + +`webassets` allows to concatenate your assets and to use almost all of the +hype tools of the moment (see the `documentation`_): + +* css minifier (`cssmin`, `yuicompressor`, ...) +* css compiler (`less`, `sass`, ...) +* js minifier (`uglifyjs`, `yuicompressor`, `closure`, ...) + +Others filters include gzip compression, integration of images in css with +`datauri` and more. Webassets also append a version identifier to your asset +url to convince browsers to download new versions of your assets when you use +far future expires headers. + +When using it with Pelican, `webassets` is configured to process assets in the +``OUTPUT_PATH/theme`` directory. You can use it in your templates with a +template tag, for example: + +.. code-block:: jinja + + {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %} + + {% endassets %} + +will produce a minified css file with the version identifier: + +.. code-block:: html + + + +Another example for javascript: + +.. code-block:: jinja + + {% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %} + + {% endassets %} + +will produce a minified and gzipped js file: + +.. code-block:: html + + + +.. _webassets: https://github.com/miracle2k/webassets +.. _documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html Example settings ================