1
0
Fork 0
forked from github/pelican

merge the plugin branch

This commit is contained in:
Alexis Metaireau 2012-06-10 01:14:30 +02:00
commit 6a0937a9e8
14 changed files with 356 additions and 3 deletions

View file

@ -61,6 +61,7 @@ A French version of the documentation is available at :doc:`fr/index`.
getting_started
settings
themes
plugins
internals
pelican-themes
importer

108
docs/plugins.rst Normal file
View file

@ -0,0 +1,108 @@
.. _plugins:
Plugins
#######
Since version 3.0, pelican manages plugins. Plugins are a way to add features
to pelican without having to directly hack pelican code.
Pelican is shipped with a set of core plugins, but you can easily implement
your own (and this page describes how).
How to use plugins?
====================
To load plugins, you have to specify them in your settings file. You have two
ways to do so.
Either by specifying strings with the path to the callables::
PLUGINS = ['pelican.plugins.gravatar',]
Or by importing them and adding them to the list::
from pelican.plugins import gravatar
PLUGINS = [gravatar, ]
If your plugins are not in an importable path, you can specify a `PLUGIN_PATH`
in the settings::
PLUGIN_PATH = "plugins"
PLUGINS = ["list", "of", "plugins"]
How to create plugins?
======================
Plugins are based on the concept of signals. Pelican sends signals and plugins
subscribe to those signals. The list of signals are defined in a following
section.
The only rule to follow for plugins is to define a `register` callable, in
which you map the signals to your plugin logic. Let's take a simple exemple::
from pelican import signals
def test(sender):
print "%s initialized !!" % sender
def register():
signals.initialized.connect(test)
List of signals
===============
Here is the list of currently implemented signals:
========================= ============================ =========================================
Signal Arguments Description
========================= ============================ =========================================
initialized pelican object
article_generate_context article_generator, metadata
article_generator_init article_generator invoked in the ArticlesGenerator.__init__
========================= ============================ =========================================
The list is currently small, don't hesitate to add signals and make a pull
request if you need them!
List of plugins
===============
Not all the list are described here, but a few of them have been extracted from
pelican core and provided in pelican.plugins. They are described here:
Tag cloud
---------
Translation
-----------
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, my setting would look like::
GITHUB_ACTIVITY_FEED = 'https://github.com/kpanic.atom'
On the templates side, you just have to iterate over the ``github_activity``
variable, as in the 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.

View file

@ -61,6 +61,7 @@ Setting name (default value) What doe
`rst2pdf`.
`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or
not.
`PLUGINS` (``[]``) The list of plugins to load. See :ref:`plugins`.
`SITENAME` (``'A Pelican Blog'``) Your site name
`SITEURL` Base URL of your website. Not defined by default,
which means the base URL is assumed to be "/" with a

View file

@ -5,6 +5,8 @@ import time
import logging
import argparse
from pelican import signals
from pelican.generators import (ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator, LessCSSGenerator)
from pelican.log import init
@ -22,7 +24,7 @@ logger = logging.getLogger(__name__)
class Pelican(object):
def __init__(self, settings=None, path=None, theme=None, output_path=None,
markup=None, delete_outputdir=False):
markup=None, delete_outputdir=False, plugin_path=None):
"""Read the settings, and performs some checks on the environment
before doing anything else.
"""
@ -58,6 +60,20 @@ class Pelican(object):
else:
raise Exception("Impossible to find the theme %s" % theme)
self.init_plugins()
signals.initialized.send(self)
def init_plugins(self):
self.plugins = self.settings['PLUGINS']
for plugin in self.plugins:
# if it's a string, then import it
if isinstance(plugin, basestring):
log.debug("Loading plugin `{0}' ...".format(plugin))
plugin = __import__(plugin, globals(), locals(), 'module')
log.debug("Registering plugin `{0}' ...".format(plugin.__name__))
plugin.register()
def _handle_deprecation(self):
if self.settings.get('CLEAN_URLS', False):

View file

@ -17,6 +17,7 @@ from jinja2.exceptions import TemplateNotFound
from pelican.contents import Article, Page, Category, is_valid_content
from pelican.readers import read_file
from pelican.utils import copy, process_translations, open
from pelican import signals
logger = logging.getLogger(__name__)
@ -118,6 +119,7 @@ class ArticlesGenerator(Generator):
self.authors = defaultdict(list)
super(ArticlesGenerator, self).__init__(*args, **kwargs)
self.drafts = []
signals.article_generator_init.send(self)
def generate_feeds(self, writer):
"""Generate the feeds from the current context, and output files."""
@ -274,6 +276,7 @@ class ArticlesGenerator(Generator):
metadata['date'] = datetime.datetime.fromtimestamp(
os.stat(f).st_ctime)
signals.article_generate_context.send(self, metadata=metadata)
article = Article(content, metadata, settings=self.settings,
filename=f)
if not is_valid_content(article, f):

View file

View file

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
"""
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

@ -0,0 +1,23 @@
from pelican import signals
"""
License plugin for Pelican
==========================
Simply add license variable in article's context, which contain
the license text.
Settings:
---------
Add LICENSE to your settings file to define 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

@ -0,0 +1,40 @@
import hashlib
from pelican import signals
"""
Gravatar plugin for Pelican
===========================
Simply add author_gravatar variable in article's context, which contains
the gravatar url.
Settings:
---------
Add AUTHOR_EMAIL to your settings file to define default author email.
Article metadata:
------------------
:email: article's author email
If one of them are defined, the author_gravatar variable is added to
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():
gravatar_url = "http://www.gravatar.com/avatar/" + \
hashlib.md5(metadata['email'].lower()).hexdigest()
metadata["author_gravatar"] = gravatar_url
def register():
signals.article_generate_context.connect(add_gravatar)

View file

@ -0,0 +1,63 @@
from docutils import nodes
from docutils.parsers.rst import directives, Directive
from pelican import log
"""
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 = u' '.join(self.content)
node = nodes.raw('', html, format='html')
return [node]
def register():
directives.register_directive('html', RawHtml)

View file

@ -0,0 +1,7 @@
from pelican import signals
def test(sender):
print "%s initialized !!" % sender
def register():
signals.initialized.connect(test)

View file

@ -69,6 +69,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
'TYPOGRIFY': False,
'LESS_GENERATOR': False,
'WEBASSETS': False,
'PLUGINS': [],
}

5
pelican/signals.py Normal file
View file

@ -0,0 +1,5 @@
from blinker import signal
initialized = signal('pelican_initialized')
article_generate_context = signal('article_generate_context')
article_generator_init = signal('article_generator_init')

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
from setuptools import setup
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz']
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz', 'blinker']
try:
import argparse
@ -25,7 +25,7 @@ setup(
author_email = 'alexis@notmyidea.org',
description = "A tool to generate a static blog from reStructuredText or Markdown input files.",
long_description=open('README.rst').read(),
packages = ['pelican', 'pelican.tools'],
packages = ['pelican', 'pelican.tools', 'pelican.plugins'],
include_package_data = True,
install_requires = requires,
entry_points = entry_points,