remove plugins, update docs and update dependecies

This commit is contained in:
Deniz Turgut 2013-04-12 23:39:39 -04:00
commit c4b3ad58e8
20 changed files with 16 additions and 1193 deletions

View file

@ -4,12 +4,11 @@ python:
- "3.2"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq --no-install-recommends asciidoc ruby-sass
- sudo apt-get install -qq --no-install-recommends asciidoc
install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi
- pip install nose mock --use-mirrors
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors unittest2; else pip install --use-mirrors unittest2py3k; fi
- pip install . --use-mirrors
- pip install --use-mirrors Markdown
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors webassets cssmin; fi
script: nosetests -s pelican

View file

@ -6,4 +6,3 @@ Markdown
BeautifulSoup4
lxml
typogrify
webassets

View file

@ -6,9 +6,6 @@ Plugins
Beginning with version 3.0, Pelican supports plugins. Plugins are a way to add
features to Pelican without having to directly modify the Pelican core.
Pelican is shipped with a set of bundled plugins, but you can easily implement
your own. This page describes how to use and create plugins.
How to use plugins
==================
@ -16,12 +13,12 @@ To load plugins, you have to specify them in your settings file. There are two
ways to do so. The first method is to specify strings with the path to the
callables::
PLUGINS = ['pelican.plugins.gravatar',]
PLUGINS = ['package.myplugin',]
Alternatively, another method is to import them and add them to the list::
from pelican.plugins import gravatar
PLUGINS = [gravatar,]
from package import myplugin
PLUGINS = [myplugin,]
If your plugins are not in an importable path, you can specify a ``PLUGIN_PATH``
in the settings. ``PLUGIN_PATH`` can be an absolute path or a path relative to
@ -30,6 +27,17 @@ the settings file::
PLUGIN_PATH = "plugins"
PLUGINS = ["list", "of", "plugins"]
Where to find plugins
=====================
We maintain a separate repository of plugins for people to share and use.
Please see the `pelican-plugins`_ repository for the official place for plugins.
.. _pelican-plugins: https://github.com/getpelican/pelican-plugins
Please note that while we will give best effort, the submitted plugins are all
community driven and supported.
How to create plugins
=====================
@ -96,311 +104,3 @@ request if you need them!
def register():
signals.content_object_init.connect(test, sender=contents.Article)
List of plugins
===============
The following plugins are currently included with Pelican:
* `Asset management`_ ``pelican.plugins.assets``
* `GitHub activity`_ ``pelican.plugins.github_activity``
* `Global license`_ ``pelican.plugins.global_license``
* `Gravatar`_ ``pelican.plugins.gravatar``
* `Gzip cache`_ ``pelican.plugins.gzip_cache``
* `HTML tags for reStructuredText`_ ``pelican.plugins.html_rst_directive``
* `Related posts`_ ``pelican.plugins.related_posts``
* `Sitemap`_ ``pelican.plugins.sitemap``
* `Summary`_ ``pelican.plugins.summary``
Ideas for plugins that haven't been written yet:
* Tag cloud
* Translation
Plugin descriptions
===================
Asset management
----------------
This plugin allows you to use the `Webassets`_ module to manage assets such as
CSS and JS files. The module must first be installed::
pip install webassets
The Webassets module allows you to perform a number of useful asset management
functions, including:
* CSS minifier (``cssmin``, ``yui_css``, ...)
* CSS compiler (``less``, ``sass``, ...)
* JS minifier (``uglifyjs``, ``yui_js``, ``closure``, ...)
Others filters include gzip compression, integration of images in CSS via data
URIs, and more. Webassets can 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. Please refer to the `Webassets documentation`_ for
more information.
When used with Pelican, Webassets is configured to process assets in the
``OUTPUT_PATH/theme`` directory. You can use Webassets in your templates by
including one or more template tags. The Jinja variable ``{{ ASSET_URL }}`` can
be used in templates and is relative to the ``theme/`` url. The
``{{ ASSET_URL }}`` variable should be used in conjunction with the
``{{ SITEURL }}`` variable in order to generate URLs properly. For example:
.. code-block:: jinja
{% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %}
<link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
{% endassets %}
... will produce a minified css file with a version identifier that looks like:
.. code-block:: html
<link href="http://{SITEURL}/theme/css/style.min.css?b3a7c807" rel="stylesheet">
These filters can be combined. Here is an example that uses the SASS compiler
and minifies the output:
.. code-block:: jinja
{% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %}
<link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
{% endassets %}
Another example for Javascript:
.. code-block:: jinja
{% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
<script src="{{ SITEURL }}/{{ ASSET_URL }}"></script>
{% endassets %}
The above will produce a minified and gzipped JS file:
.. code-block:: html
<script src="http://{SITEURL}/theme/js/packed.js?00703b9d"></script>
Pelican's debug mode is propagated to Webassets to disable asset packaging
and instead work with the uncompressed assets.
Many of Webasset's available compilers have additional configuration options
(i.e. 'Less', 'Sass', 'Stylus', 'Closure_js'). You can pass these options to
Webassets using the ``ASSET_CONFIG`` in your settings file.
The following will handle Google Closure's compilation level and locate
LessCSS's binary:
.. code-block:: python
ASSET_CONFIG = (('closure_compressor_optimization', 'WHITESPACE_ONLY'),
('less_bin', 'lessc.cmd'), )
.. _Webassets: https://github.com/miracle2k/webassets
.. _Webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
GitHub activity
---------------
This plugin makes use of the `feedparser`_ library that you'll need to
install.
Set the ``GITHUB_ACTIVITY_FEED`` parameter to your GitHub activity feed.
For example, to track Pelican project activity, the setting would be::
GITHUB_ACTIVITY_FEED = 'https://github.com/getpelican.atom'
On the template side, you just have to iterate over the ``github_activity``
variable, as in this example::
{% if GITHUB_ACTIVITY_FEED %}
<div class="social">
<h2>Github Activity</h2>
<ul>
{% for entry in github_activity %}
<li><b>{{ entry[0] }}</b><br /> {{ entry[1] }}</li>
{% endfor %}
</ul>
</div><!-- /.github_activity -->
{% endif %}
``github_activity`` is a list of lists. The first element is the title,
and the second element is the raw HTML from GitHub.
.. _feedparser: https://crate.io/packages/feedparser/
Global license
--------------
This plugin allows you to define a ``LICENSE`` setting and adds the contents of that
license variable to the article's context, making that variable available to use
from within your theme's templates.
Gravatar
--------
This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
makes the variable available within the article's context. You can add
``AUTHOR_EMAIL`` to your settings file to define the default author's email
address. Obviously, that email address must be associated with a Gravatar
account.
Alternatively, you can provide an email address from within article metadata::
:email: john.doe@example.com
If the email address is defined via at least one of the two methods above,
the ``author_gravatar`` variable is added to the article's context.
Gzip cache
----------
Certain web servers (e.g., Nginx) can use a static cache of gzip-compressed
files to prevent the server from compressing files during an HTTP call. Since
compression occurs at another time, these compressed files can be compressed
at a higher compression level for increased optimization.
The ``gzip_cache`` plugin compresses all common text type files into a ``.gz``
file within the same directory as the original file.
HTML tags for reStructuredText
------------------------------
This plugin allows you to use HTML tags from within reST documents. Following
is a usage example, which is in this case a contact form::
.. html::
<form method="GET" action="mailto:some email">
<p>
<input type="text" placeholder="Subject" name="subject">
<br />
<textarea name="body" placeholder="Message">
</textarea>
<br />
<input type="reset"><input type="submit">
</p>
</form>
Related posts
-------------
This plugin adds the ``related_posts`` variable to the article's context.
By default, up to 5 articles are listed. You can customize this value by
defining ``RELATED_POSTS_MAX`` in your settings file::
RELATED_POSTS_MAX = 10
You can then use the ``article.related_posts`` variable in your templates.
For example::
{% if article.related_posts %}
<ul>
{% for related_post in article.related_posts %}
<li><a href="{{ SITEURL }}/{{ related_post.url }}">{{ related_post.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
Sitemap
-------
The sitemap plugin generates plain-text or XML sitemaps. You can use the
``SITEMAP`` variable in your settings file to configure the behavior of the
plugin.
The ``SITEMAP`` variable must be a Python dictionary and can contain three keys:
- ``format``, which sets the output format of the plugin (``xml`` or ``txt``)
- ``priorities``, which is a dictionary with three keys:
- ``articles``, the priority for the URLs of the articles and their
translations
- ``pages``, the priority for the URLs of the static pages
- ``indexes``, the priority for the URLs of the index pages, such as tags,
author pages, categories indexes, archives, etc...
All the values of this dictionary must be decimal numbers between ``0`` and ``1``.
- ``changefreqs``, which is a dictionary with three items:
- ``articles``, the update frequency of the articles
- ``pages``, the update frequency of the pages
- ``indexes``, the update frequency of the index pages
Valid frequency values are ``always``, ``hourly``, ``daily``, ``weekly``, ``monthly``,
``yearly`` and ``never``.
If a key is missing or a value is incorrect, it will be replaced with the
default value.
The sitemap is saved in ``<output_path>/sitemap.<format>``.
.. note::
``priorities`` and ``changefreqs`` are information for search engines.
They are only used in the XML sitemaps.
For more information: <http://www.sitemaps.org/protocol.html#xmlTagDefinitions>
**Example**
Here is an example configuration (it's also the default settings):
.. code-block:: python
PLUGINS=['pelican.plugins.sitemap',]
SITEMAP = {
'format': 'xml',
'priorities': {
'articles': 0.5,
'indexes': 0.5,
'pages': 0.5
},
'changefreqs': {
'articles': 'monthly',
'indexes': 'daily',
'pages': 'monthly'
}
}
.. _plugin-summary:
Summary
-------------
This plugin allows easy, variable length summaries directly embedded into the
body of your articles. It introduces two new settings: ``SUMMARY_BEGIN_MARKER``
and ``SUMMARY_END_MARKER``: strings which can be placed directly into an article
to mark the beginning and end of a summary. When found, the standard
``SUMMARY_MAX_LENGTH`` setting will be ignored. The markers themselves will also
be removed from your articles before they are published. The default values
are ``<!-- PELICAN_BEGIN_SUMMARY -->`` and ``<!-- PELICAN_END_SUMMARY -->``.
For example::
Title: My super title
Date: 2010-12-03 10:20
Tags: thats, awesome
Category: yeah
Slug: my-super-post
Author: Alexis Metaireau
This is the content of my super blog post.
<!-- PELICAN_END_SUMMARY -->
and this content occurs after the summary.
Here, the summary is taken to be the first line of the post. Because no
beginning marker was found, it starts at the top of the body. It is possible
to leave out the end marker instead, in which case the summary will start at the
beginning marker and continue to the end of the body.

View file

@ -1,53 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
Asset management plugin for Pelican
===================================
This plugin allows you to use the `webassets`_ module to manage assets such as
CSS and JS files.
The ASSET_URL is set to a relative url to honor Pelican's RELATIVE_URLS
setting. This requires the use of SITEURL in the templates::
<link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
.. _webassets: https://webassets.readthedocs.org/
"""
import os
import logging
from pelican import signals
from webassets import Environment
from webassets.ext.jinja2 import AssetsExtension
def add_jinja2_ext(pelican):
"""Add Webassets to Jinja2 extensions in Pelican settings."""
pelican.settings['JINJA_EXTENSIONS'].append(AssetsExtension)
def create_assets_env(generator):
"""Define the assets environment and pass it to the generator."""
assets_url = 'theme/'
assets_src = os.path.join(generator.output_path, 'theme')
generator.env.assets_environment = Environment(assets_src, assets_url)
if 'ASSET_CONFIG' in generator.settings:
for item in generator.settings['ASSET_CONFIG']:
generator.env.assets_environment.config[item[0]] = item[1]
logger = logging.getLogger(__name__)
if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG":
generator.env.assets_environment.debug = True
def register():
"""Plugin registration."""
signals.initialized.connect(add_jinja2_ext)
signals.generator_init.connect(create_assets_env)

View file

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
"""
Copyright (c) Marco Milanesi <kpanic@gnufunk.org>
A plugin to list your Github Activity
To enable it set in your pelican config file the GITHUB_ACTIVITY_FEED
parameter pointing to your github activity feed.
for example my personal activity feed is:
https://github.com/kpanic.atom
in your template just write a for in jinja2 syntax against the
github_activity variable.
i.e.
<div class="social">
<h2>Github Activity</h2>
<ul>
{% for entry in github_activity %}
<li><b>{{ entry[0] }}</b><br /> {{ entry[1] }}</li>
{% endfor %}
</ul>
</div><!-- /.github_activity -->
github_activity is a list containing a list. The first element is the title
and the second element is the raw html from github
"""
from pelican import signals
class GitHubActivity():
"""
A class created to fetch github activity with feedparser
"""
def __init__(self, generator):
try:
import feedparser
self.activities = feedparser.parse(
generator.settings['GITHUB_ACTIVITY_FEED'])
except ImportError:
raise Exception("Unable to find feedparser")
def fetch(self):
"""
returns a list of html snippets fetched from github actitivy feed
"""
entries = []
for activity in self.activities['entries']:
entries.append(
[element for element in [activity['title'],
activity['content'][0]['value']]])
return entries
def fetch_github_activity(gen, metadata):
"""
registered handler for the github activity plugin
it puts in generator.context the html needed to be displayed on a
template
"""
if 'GITHUB_ACTIVITY_FEED' in gen.settings.keys():
gen.context['github_activity'] = gen.plugin_instance.fetch()
def feed_parser_initialization(generator):
"""
Initialization of feed parser
"""
generator.plugin_instance = GitHubActivity(generator)
def register():
"""
Plugin registration
"""
signals.article_generator_init.connect(feed_parser_initialization)
signals.article_generate_context.connect(fetch_github_activity)

View file

@ -1,24 +0,0 @@
from pelican import signals
"""
License plugin for Pelican
==========================
This plugin allows you to define a LICENSE setting and adds the contents of that
license variable to the article's context, making that variable available to use
from within your theme's templates.
Settings:
---------
Define LICENSE in your settings file with the contents of your default license.
"""
def add_license(generator, metadata):
if 'license' not in metadata.keys()\
and 'LICENSE' in generator.settings.keys():
metadata['license'] = generator.settings['LICENSE']
def register():
signals.article_generate_context.connect(add_license)

View file

@ -1,44 +0,0 @@
import hashlib
import six
from pelican import signals
"""
Gravatar plugin for Pelican
===========================
This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
makes the variable available within the article's context.
Settings:
---------
Add AUTHOR_EMAIL to your settings file to define the default author's email
address. Obviously, that email address must be associated with a Gravatar
account.
Article metadata:
------------------
:email: article's author email
If one of them are defined, the author_gravatar variable is added to the
article's context.
"""
def add_gravatar(generator, metadata):
#first check email
if 'email' not in metadata.keys()\
and 'AUTHOR_EMAIL' in generator.settings.keys():
metadata['email'] = generator.settings['AUTHOR_EMAIL']
#then add gravatar url
if 'email' in metadata.keys():
email_bytes = six.b(metadata['email']).lower()
gravatar_url = "http://www.gravatar.com/avatar/" + \
hashlib.md5(email_bytes).hexdigest()
metadata["author_gravatar"] = gravatar_url
def register():
signals.article_generate_context.connect(add_gravatar)

View file

@ -1,78 +0,0 @@
# Copyright (c) 2012 Matt Layman
'''A plugin to create .gz cache files for optimization.'''
import gzip
import logging
import os
from pelican import signals
logger = logging.getLogger(__name__)
# A list of file types to exclude from possible compression
EXCLUDE_TYPES = [
# Compressed types
'.bz2',
'.gz',
# Audio types
'.aac',
'.flac',
'.mp3',
'.wma',
# Image types
'.gif',
'.jpg',
'.jpeg',
'.png',
# Video types
'.avi',
'.mov',
'.mp4',
]
def create_gzip_cache(pelican):
'''Create a gzip cache file for every file that a webserver would
reasonably want to cache (e.g., text type files).
:param pelican: The Pelican instance
'''
for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
for name in filenames:
if should_compress(name):
filepath = os.path.join(dirpath, name)
create_gzip_file(filepath)
def should_compress(filename):
'''Check if the filename is a type of file that should be compressed.
:param filename: A file name to check against
'''
for extension in EXCLUDE_TYPES:
if filename.endswith(extension):
return False
return True
def create_gzip_file(filepath):
'''Create a gzipped file in the same directory with a filepath.gz name.
:param filepath: A file to compress
'''
compressed_path = filepath + '.gz'
with open(filepath, 'rb') as uncompressed:
try:
logger.debug('Compressing: %s' % filepath)
compressed = gzip.open(compressed_path, 'wb')
compressed.writelines(uncompressed)
except Exception as ex:
logger.critical('Gzip compression failed: %s' % ex)
finally:
compressed.close()
def register():
signals.finalized.connect(create_gzip_cache)

View file

@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from docutils import nodes
from docutils.parsers.rst import directives, Directive
"""
HTML tags for reStructuredText
==============================
Directives
----------
.. html::
(HTML code)
Example
-------
A search engine:
.. html::
<form action="http://seeks.fr/search" method="GET">
<input type="text" value="Pelican v2" title="Search" maxlength="2048" name="q" autocomplete="on" />
<input type="hidden" name="lang" value="en" />
<input type="submit" value="Seeks !" id="search_button" />
</form>
A contact form:
.. html::
<form method="GET" action="mailto:some email">
<p>
<input type="text" placeholder="Subject" name="subject">
<br />
<textarea name="body" placeholder="Message">
</textarea>
<br />
<input type="reset"><input type="submit">
</p>
</form>
"""
class RawHtml(Directive):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = True
has_content = True
def run(self):
html = ' '.join(self.content)
node = nodes.raw('', html, format='html')
return [node]
def register():
directives.register_directive('html', RawHtml)

View file

@ -1,9 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
from pelican import signals
def test(sender):
print("%s initialized !!" % sender)
def register():
signals.initialized.connect(test)

View file

@ -1,35 +0,0 @@
"""
Related posts plugin for Pelican
================================
Adds related_posts variable to article's context
"""
from pelican import signals
from collections import Counter
def add_related_posts(generator):
# get the max number of entries from settings
# or fall back to default (5)
numentries = generator.settings.get('RELATED_POSTS_MAX', 5)
for article in generator.articles:
# no tag, no relation
if not hasattr(article, 'tags'):
continue
# score = number of common tags
scores = Counter()
for tag in article.tags:
scores += Counter(generator.tags[tag])
# remove itself
scores.pop(article)
article.related_posts = [other for other, count
in scores.most_common(numentries)]
def register():
signals.article_generator_finalized.connect(add_related_posts)

View file

@ -1,195 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import collections
import os.path
from datetime import datetime
from logging import warning, info
from codecs import open
from pelican import signals, contents
TXT_HEADER = """{0}/index.html
{0}/archives.html
{0}/tags.html
{0}/categories.html
"""
XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
"""
XML_URL = """
<url>
<loc>{0}/{1}</loc>
<lastmod>{2}</lastmod>
<changefreq>{3}</changefreq>
<priority>{4}</priority>
</url>
"""
XML_FOOTER = """
</urlset>
"""
def format_date(date):
if date.tzinfo:
tz = date.strftime('%s')
tz = tz[:-2] + ':' + tz[-2:]
else:
tz = "-00:00"
return date.strftime("%Y-%m-%dT%H:%M:%S") + tz
class SitemapGenerator(object):
def __init__(self, context, settings, path, theme, output_path, *null):
self.output_path = output_path
self.context = context
self.now = datetime.now()
self.siteurl = settings.get('SITEURL')
self.format = 'xml'
self.changefreqs = {
'articles': 'monthly',
'indexes': 'daily',
'pages': 'monthly'
}
self.priorities = {
'articles': 0.5,
'indexes': 0.5,
'pages': 0.5
}
config = settings.get('SITEMAP', {})
if not isinstance(config, dict):
warning("sitemap plugin: the SITEMAP setting must be a dict")
else:
fmt = config.get('format')
pris = config.get('priorities')
chfreqs = config.get('changefreqs')
if fmt not in ('xml', 'txt'):
warning("sitemap plugin: SITEMAP['format'] must be `txt' or `xml'")
warning("sitemap plugin: Setting SITEMAP['format'] on `xml'")
elif fmt == 'txt':
self.format = fmt
return
valid_keys = ('articles', 'indexes', 'pages')
valid_chfreqs = ('always', 'hourly', 'daily', 'weekly', 'monthly',
'yearly', 'never')
if isinstance(pris, dict):
# We use items for Py3k compat. .iteritems() otherwise
for k, v in pris.items():
if k in valid_keys and not isinstance(v, (int, float)):
default = self.priorities[k]
warning("sitemap plugin: priorities must be numbers")
warning("sitemap plugin: setting SITEMAP['priorities']"
"['{0}'] on {1}".format(k, default))
pris[k] = default
self.priorities.update(pris)
elif pris is not None:
warning("sitemap plugin: SITEMAP['priorities'] must be a dict")
warning("sitemap plugin: using the default values")
if isinstance(chfreqs, dict):
# .items() for py3k compat.
for k, v in chfreqs.items():
if k in valid_keys and v not in valid_chfreqs:
default = self.changefreqs[k]
warning("sitemap plugin: invalid changefreq `{0}'".format(v))
warning("sitemap plugin: setting SITEMAP['changefreqs']"
"['{0}'] on '{1}'".format(k, default))
chfreqs[k] = default
self.changefreqs.update(chfreqs)
elif chfreqs is not None:
warning("sitemap plugin: SITEMAP['changefreqs'] must be a dict")
warning("sitemap plugin: using the default values")
def write_url(self, page, fd):
if getattr(page, 'status', 'published') != 'published':
return
page_path = os.path.join(self.output_path, page.url)
if not os.path.exists(page_path):
return
lastmod = format_date(getattr(page, 'date', self.now))
if isinstance(page, contents.Article):
pri = self.priorities['articles']
chfreq = self.changefreqs['articles']
elif isinstance(page, contents.Page):
pri = self.priorities['pages']
chfreq = self.changefreqs['pages']
else:
pri = self.priorities['indexes']
chfreq = self.changefreqs['indexes']
if self.format == 'xml':
fd.write(XML_URL.format(self.siteurl, page.url, lastmod, chfreq, pri))
else:
fd.write(self.siteurl + '/' + loc + '\n')
def generate_output(self, writer):
path = os.path.join(self.output_path, 'sitemap.{0}'.format(self.format))
pages = self.context['pages'] + self.context['articles'] \
+ [ c for (c, a) in self.context['categories']] \
+ [ t for (t, a) in self.context['tags']] \
+ [ a for (a, b) in self.context['authors']]
for article in self.context['articles']:
pages += article.translations
info('writing {0}'.format(path))
with open(path, 'w', encoding='utf-8') as fd:
if self.format == 'xml':
fd.write(XML_HEADER)
else:
fd.write(TXT_HEADER.format(self.siteurl))
FakePage = collections.namedtuple('FakePage',
['status',
'date',
'url'])
for standard_page_url in ['index.html',
'archives.html',
'tags.html',
'categories.html']:
fake = FakePage(status='published',
date=self.now,
url=standard_page_url)
self.write_url(fake, fd)
for page in pages:
self.write_url(page, fd)
if self.format == 'xml':
fd.write(XML_FOOTER)
def get_generators(generators):
return SitemapGenerator
def register():
signals.get_generators.connect(get_generators)

View file

@ -1,53 +0,0 @@
import types
from pelican import signals
def initialized(pelican):
from pelican.settings import _DEFAULT_CONFIG
_DEFAULT_CONFIG.setdefault('SUMMARY_BEGIN_MARKER',
'<!-- PELICAN_BEGIN_SUMMARY -->')
_DEFAULT_CONFIG.setdefault('SUMMARY_END_MARKER',
'<!-- PELICAN_END_SUMMARY -->')
if pelican:
pelican.settings.setdefault('SUMMARY_BEGIN_MARKER',
'<!-- PELICAN_BEGIN_SUMMARY -->')
pelican.settings.setdefault('SUMMARY_END_MARKER',
'<!-- PELICAN_END_SUMMARY -->')
def content_object_init(instance):
# if summary is already specified, use it
if 'summary' in instance.metadata:
return
def _get_content(self):
content = self._content
if self.settings['SUMMARY_BEGIN_MARKER']:
content = content.replace(
self.settings['SUMMARY_BEGIN_MARKER'], '', 1)
if self.settings['SUMMARY_END_MARKER']:
content = content.replace(
self.settings['SUMMARY_END_MARKER'], '', 1)
return content
instance._get_content = types.MethodType(_get_content, instance)
# extract out our summary
if not hasattr(instance, '_summary') and instance._content is not None:
content = instance._content
begin_summary = -1
end_summary = -1
if instance.settings['SUMMARY_BEGIN_MARKER']:
begin_summary = content.find(instance.settings['SUMMARY_BEGIN_MARKER'])
if instance.settings['SUMMARY_END_MARKER']:
end_summary = content.find(instance.settings['SUMMARY_END_MARKER'])
if begin_summary != -1 or end_summary != -1:
# the beginning position has to take into account the length
# of the marker
begin_summary = (begin_summary +
len(instance.settings['SUMMARY_BEGIN_MARKER'])
if begin_summary != -1 else 0)
end_summary = end_summary if end_summary != -1 else None
instance._summary = content[begin_summary:end_summary]
def register():
signals.initialized.connect(initialized)
signals.content_object_init.connect(content_object_init)

View file

@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
'''Core plugins unit tests'''
import os
import tempfile
from pelican.contents import Page
from pelican.plugins import gzip_cache
from pelican.tests.test_contents import TEST_CONTENT, TEST_SUMMARY
from pelican.tests.support import unittest, temporary_folder
class TestGzipCache(unittest.TestCase):
def test_should_compress(self):
# Some filetypes should compress and others shouldn't.
self.assertTrue(gzip_cache.should_compress('foo.html'))
self.assertTrue(gzip_cache.should_compress('bar.css'))
self.assertTrue(gzip_cache.should_compress('baz.js'))
self.assertTrue(gzip_cache.should_compress('foo.txt'))
self.assertFalse(gzip_cache.should_compress('foo.gz'))
self.assertFalse(gzip_cache.should_compress('bar.png'))
self.assertFalse(gzip_cache.should_compress('baz.mp3'))
self.assertFalse(gzip_cache.should_compress('foo.mov'))
def test_creates_gzip_file(self):
# A file matching the input filename with a .gz extension is created.
# The plugin walks over the output content after the finalized signal
# so it is safe to assume that the file exists (otherwise walk would
# not report it). Therefore, create a dummy file to use.
with temporary_folder() as tempdir:
_, a_html_filename = tempfile.mkstemp(suffix='.html', dir=tempdir)
gzip_cache.create_gzip_file(a_html_filename)
self.assertTrue(os.path.exists(a_html_filename + '.gz'))
class TestSummary(unittest.TestCase):
def setUp(self):
super(TestSummary, self).setUp()
from pelican.plugins import summary
summary.register()
summary.initialized(None)
self.page_kwargs = {
'content': TEST_CONTENT,
'context': {
'localsiteurl': '',
},
'metadata': {
'summary': TEST_SUMMARY,
'title': 'foo bar',
'author': 'Blogger',
},
}
def _copy_page_kwargs(self):
# make a deep copy of page_kwargs
page_kwargs = dict([(key, self.page_kwargs[key]) for key in
self.page_kwargs])
for key in page_kwargs:
if not isinstance(page_kwargs[key], dict):
break
page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey])
for subkey in page_kwargs[key]])
return page_kwargs
def test_end_summary(self):
page_kwargs = self._copy_page_kwargs()
del page_kwargs['metadata']['summary']
page_kwargs['content'] = (
TEST_SUMMARY + '<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
page = Page(**page_kwargs)
# test both the summary and the marker removal
self.assertEqual(page.summary, TEST_SUMMARY)
self.assertEqual(page.content, TEST_SUMMARY + TEST_CONTENT)
def test_begin_summary(self):
page_kwargs = self._copy_page_kwargs()
del page_kwargs['metadata']['summary']
page_kwargs['content'] = (
'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_CONTENT)
page = Page(**page_kwargs)
# test both the summary and the marker removal
self.assertEqual(page.summary, TEST_CONTENT)
self.assertEqual(page.content, 'FOOBAR' + TEST_CONTENT)
def test_begin_end_summary(self):
page_kwargs = self._copy_page_kwargs()
del page_kwargs['metadata']['summary']
page_kwargs['content'] = (
'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_SUMMARY +
'<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
page = Page(**page_kwargs)
# test both the summary and the marker removal
self.assertEqual(page.summary, TEST_SUMMARY)
self.assertEqual(page.content, 'FOOBAR' + TEST_SUMMARY + TEST_CONTENT)

View file

@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
# from __future__ import unicode_literals
import hashlib
import locale
import os
from codecs import open
from tempfile import mkdtemp
from shutil import rmtree
from pelican import Pelican
from pelican.settings import read_settings
from .support import unittest, skipIfNoExecutable, module_exists
CUR_DIR = os.path.dirname(__file__)
THEME_DIR = os.path.join(CUR_DIR, 'themes', 'assets')
CSS_REF = open(os.path.join(THEME_DIR, 'static', 'css',
'style.min.css')).read()
CSS_HASH = hashlib.md5(CSS_REF).hexdigest()[0:8]
@unittest.skipUnless(module_exists('webassets'), "webassets isn't installed")
@skipIfNoExecutable(['scss', '-v'])
@skipIfNoExecutable(['cssmin', '--version'])
class TestWebAssets(unittest.TestCase):
"""Base class for testing webassets."""
def setUp(self, override=None):
self.temp_path = mkdtemp(prefix='pelicantests.')
settings = {
'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'),
'OUTPUT_PATH': self.temp_path,
'PLUGINS': ['pelican.plugins.assets', ],
'THEME': THEME_DIR,
'LOCALE': locale.normalize('en_US'),
}
if override:
settings.update(override)
self.settings = read_settings(override=settings)
pelican = Pelican(settings=self.settings)
pelican.run()
def tearDown(self):
rmtree(self.temp_path)
def check_link_tag(self, css_file, html_file):
"""Check the presence of `css_file` in `html_file`."""
link_tag = ('<link rel="stylesheet" href="{css_file}">'
.format(css_file=css_file))
html = open(html_file).read()
self.assertRegexpMatches(html, link_tag)
class TestWebAssetsRelativeURLS(TestWebAssets):
"""Test pelican with relative urls."""
def setUp(self):
TestWebAssets.setUp(self, override={'RELATIVE_URLS': True})
def test_jinja2_ext(self):
# Test that the Jinja2 extension was correctly added.
from webassets.ext.jinja2 import AssetsExtension
self.assertIn(AssetsExtension, self.settings['JINJA_EXTENSIONS'])
def test_compilation(self):
# Compare the compiled css with the reference.
gen_file = os.path.join(self.temp_path, 'theme', 'gen',
'style.{0}.min.css'.format(CSS_HASH))
self.assertTrue(os.path.isfile(gen_file))
css_new = open(gen_file).read()
self.assertEqual(css_new, CSS_REF)
def test_template(self):
# Look in the output files for the link tag.
css_file = './theme/gen/style.{0}.min.css'.format(CSS_HASH)
html_files = ['index.html', 'archives.html',
'this-is-an-article-with-category.html']
for f in html_files:
self.check_link_tag(css_file, os.path.join(self.temp_path, f))
self.check_link_tag(
'../theme/gen/style.{0}.min.css'.format(CSS_HASH),
os.path.join(self.temp_path, 'category/yeah.html'))
class TestWebAssetsAbsoluteURLS(TestWebAssets):
"""Test pelican with absolute urls."""
def setUp(self):
TestWebAssets.setUp(self, override={'SITEURL': 'http://localhost'})
def test_absolute_url(self):
# Look in the output files for the link tag with absolute url.
css_file = ('http://localhost/theme/gen/style.{0}.min.css'
.format(CSS_HASH))
html_files = ['index.html', 'archives.html',
'this-is-an-article-with-category.html']
for f in html_files:
self.check_link_tag(css_file, os.path.join(self.temp_path, f))

View file

@ -1 +0,0 @@
body{font:14px/1.5 "Droid Sans",sans-serif;background-color:#e4e4e4;color:#242424}a{color:red}a:hover{color:orange}

View file

@ -1,19 +0,0 @@
/* -*- scss-compile-at-save: nil -*- */
$baseFontFamily : "Droid Sans", sans-serif;
$textColor : #242424;
$bodyBackground : #e4e4e4;
body {
font: 14px/1.5 $baseFontFamily;
background-color: $bodyBackground;
color: $textColor;
}
a {
color: red;
&:hover {
color: orange;
}
}

View file

@ -1,7 +0,0 @@
{% extends "!simple/base.html" %}
{% block head %}
{% assets filters="scss,cssmin", output="gen/style.%(version)s.min.css", "css/style.scss" %}
<link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
{% endassets %}
{% endblock %}

View file

@ -32,7 +32,7 @@ setup(
description="A tool to generate a static blog from reStructuredText or "
"Markdown input files.",
long_description=README + '\n' + CHANGELOG,
packages=['pelican', 'pelican.tools', 'pelican.plugins'],
packages=['pelican', 'pelican.tools'],
include_package_data=True,
install_requires=requires,
entry_points=entry_points,