Merged from upstream/master

This commit is contained in:
icyleaf 2013-06-03 18:45:06 +08:00
commit d73e393206
262 changed files with 5498 additions and 4504 deletions

2
.gitignore vendored
View file

@ -11,3 +11,5 @@ tags
.tox
.coverage
htmlcov
six-*.egg/
*.orig

24
.mailmap Normal file
View file

@ -0,0 +1,24 @@
Alexis Métaireau <alexis@notmyidea.org>
Alexis Métaireau <alexis@notmyidea.org> <alexis, notmyidea, org>
Alexis Métaireau <alexis@notmyidea.org> <ametaireau@gmail.com>
Axel Haustant <noirbizarre@gmail.com> <axel.haustant.ext@mappy.com>
Axel Haustant <noirbizarre@gmail.com> <axel.haustant@valtech.fr>
Dave Mankoff <mankyd@gmail.com>
Feth Arezki <feth@tuttu.info>
Guillaume <guillaume@lame.homelinux.com>
Guillaume <guillaume@lame.homelinux.com> <guillaume@mint.(none)>
Guillaume B <guitreize@gmail.com>
Guillermo López <guilan70@hotmail.com>
Guillermo López <guilan70@hotmail.com> <guillermo.lopez@outlook.com>
Jomel Imperio <jimperio@gmail.com>
Justin Mayer <entrop@gmail.com>
Justin Mayer <entrop@gmail.com> <entroP@gmail.com>
Marco Milanesi <kpanic@gnufunk.org> <marcom@openquake.org>
Massimo Santini <santini@dsi.unimi.it> <santini@spillane.docenti.dsi.unimi.it>
Rémy HUBSCHER <hubscher.remy@gmail.com> <remy.hubscher@ionyse.com>
Simon Conseil <contact@saimon.org>
Simon Liedtke <liedtke.simon@googlemail.com>
Skami18 <skami@skami-laptop.dyndns.org>
Stuart Colville <muffinresearchlabs@gmail.com> <muffinresearch@gmail.com>
Stéphane Bunel <stephane@lutetium.(none)>
tBunnyMan <WagThatTail@Me.com>

View file

@ -1,19 +1,14 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq ruby-sass
- sudo apt-get install -qq --no-install-recommends asciidoc
- sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8
install:
- pip install nose unittest2 mock --use-mirrors
- 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 mock --use-mirrors
- pip install . --use-mirrors
- pip install Markdown
- pip install webassets
- pip install cssmin
script: nosetests -s tests
notifications:
irc:
channels:
- "irc.freenode.org#pelican"
on_success: change
- pip install --use-mirrors Markdown
script: python -m unittest discover

40
CONTRIBUTING.rst Normal file
View file

@ -0,0 +1,40 @@
Contribution submission guidelines
==================================
* Consider whether your new feature might be better suited as a plugin_. Folks
are usually available in the `#pelican IRC channel`_ if help is needed to
make that determination.
* `Create a new git branch`_ specific to your change (as opposed to making
your commits in the master branch).
* **Don't put multiple fixes/features in the same branch / pull request.**
For example, if you're hacking on a new feature and find a bugfix that
doesn't *require* your new feature, **make a new distinct branch and pull
request** for the bugfix.
* Adhere to PEP8 coding standards whenever possible.
* Check for unnecessary whitespace via ``git diff --check`` before committing.
* **Add docs and tests for your changes**.
* `Run all the tests`_ **on both Python 2.7 and 3.3** to ensure nothing was
accidentally broken.
* First line of your commit message should start with present-tense verb, be 50
characters or less, and include the relevant issue number(s) if applicable.
*Example:* ``Ensure proper PLUGIN_PATH behavior. Refs #428.`` If the commit
*completely fixes* an existing bug report, please use ``Fixes #585`` or ``Fix
#585`` syntax (so the relevant issue is automatically closed upon PR merge).
* After the first line of the commit message, add a blank line and then a more
detailed explanation (when relevant).
* If you have previously filed a GitHub issue and want to contribute code that
addresses that issue, **please use** ``hub pull-request`` instead of using
GitHub's web UI to submit the pull request. This isn't an absolute
requirement, but makes the maintainers' lives much easier! Specifically:
`install hub <https://github.com/defunkt/hub/#installation>`_ and then run
`hub pull-request <https://github.com/defunkt/hub/#git-pull-request>`_ to
turn your GitHub issue into a pull request containing your code.
Check out our `Git Tips`_ page or ask on the `#pelican IRC channel`_ if you
need assistance or have any questions about these guidelines.
.. _`plugin`: http://docs.getpelican.com/en/latest/plugins.html
.. _`#pelican IRC channel`: http://webchat.freenode.net/?channels=pelican&uio=d4
.. _`Create a new git branch`: https://github.com/getpelican/pelican/wiki/Git-Tips#making-your-changes
.. _`Run all the tests`: http://docs.getpelican.com/en/latest/contribute.html#running-the-test-suite
.. _`Git Tips`: https://github.com/getpelican/pelican/wiki/Git-Tips

View file

@ -1,6 +1,4 @@
include *.rst
global-include *.py
recursive-include pelican *.html *.css *png *.in
recursive-include pelican *.html *.css *png *.in *.rst *.md *.mkd *.xml
include LICENSE THANKS docs/changelog.rst
recursive-include tests *
recursive-exclude tests *.pyc

View file

@ -2,7 +2,7 @@ Pelican
=======
.. image:: https://secure.travis-ci.org/getpelican/pelican.png?branch=master
:target: http://travis-ci.org/#!/getpelican/pelican
:target: http://travis-ci.org/getpelican/pelican
:alt: Travis-ci: continuous integration status.
Pelican is a static site generator, written in Python_.
@ -27,7 +27,6 @@ Pelican currently supports:
* Publication of articles in multiple languages
* Atom/RSS feeds
* Code syntax highlighting
* Asset management with `webassets`_ (optional)
* Import from WordPress, Dotclear, or RSS feeds
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
@ -67,4 +66,3 @@ client handy, use the webchat_ for quick feedback.
.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4
.. _contribute: http://docs.getpelican.com/en/latest/contribute.html
.. _webassets: https://github.com/miracle2k/webassets

55
THANKS
View file

@ -1,10 +1,10 @@
Pelican is a project by Alexis Métaireau <http://notmyidea.org> but there is
a quite big number of people that contributed or implemented key features over
time. We try to keep this list up to date, but you can have a look at the nice
graphs proposed by github about contributors here:
https://github.com/getpelican/pelican/graphs/contributors
Pelican is a project originally created by Alexis Métaireau
<http://notmyidea.org/>, but there are a large number of people that have
contributed or implemented key features over time. We do our best to keep this
list up-to-date, but you can also have a look at the nice contributor graphs
produced by GitHub: https://github.com/getpelican/pelican/graphs/contributors
If you want to contibute, check the documentation section about how to do so
If you want to contibute, check the documentation section about how to do so:
<http://docs.getpelican.com/en/latest/contribute.html>
Aaron Kavlie
@ -20,8 +20,11 @@ Alexis Métaireau
Allan Whatmough
Andrea Crotti
Andrew Laski
Andrew Spiers
Arnaud BOS
asselinpaul
Axel Haustant
Benoît HERVIER
Borgar
Brandon W Maister
Brendan Wholihan
@ -30,19 +33,26 @@ Brian Hsu
Brian St. Pierre
Bruno Binet
BunnyMan
Chenguang Wang
Chris Elston
Chris McDonald (Wraithan)
Chris Streeter
Christophe Chauvet
Clint Howarth
Colin Dunklau
Dafydd Crosby
Dana Woodman
dave mankoff
Dave King
Dave Mankoff
David Beitey
David Marble
Deniz Turgut (Avaris)
derdon
Dirkjan Ochtman
Dirk Makowski
draftcode
Edward Delaporte
Emily Strickland
epatters
Eric Case
Erik Hetzner
@ -50,31 +60,43 @@ FELD Boris
Feth Arezki
Florian Jacob
Florian Preinstorfer
Félix Delval
Freeculture
George V. Reilly
Guillaume
Guillaume B
Guillermo López
Guillermo López
guillermooo
Ian Cordasco
Igor Kalnitsky
Irfan Ahmad
Iuri de Silvio
Ivan Dyedov
James King
James Rowe
jawher
Jered Boxman
Jerome
Jiachen Yang
Jochen Breuer
joe di castro
John Kristensen
John Mastro
Jökull Sólberg Auðunsson
Jomel Imperio
Joseph Reagle
Joshua Adelman
Julian Berman
justinmayer
Justin Mayer
Kyle Fuller
Laureline Guerin
Leonard Huang
Leroy Jiang
Marcel Hellkamp
Marco Milanesi
Marcus Fredriksson
Mario Rodas
Mark Caudill
Martin Brochhaus
Massimo Santini
Matt Bowcock
@ -91,32 +113,45 @@ Nico Di Rocco
Nicolas Duhamel
Nicolas Perriault
Nicolas Steinmetz
Paul Asselin
Pavel Puchkin
Perry Roper
Peter Desmet
Philippe Pepiot
Rachid Belaid
Randall Degges
Ranjhith Kalisamy
Remi Rampin
Rémy HUBSCHER
renhbo
Richard Duivenvoorde
Rogdham
Roman Skvazh
Ronny Pfannschmidt
Rory McCann
Rıdvan Örsvuran
saghul
sam
Samrat Man Singh
Simon Conseil
Simon Liedtke
Skami18
solsTiCe d'Hiver
Steve Schwarz
Stéphane Bunel
Stéphane Raimbault
Stuart Colville
Talha Mansoor
Tarek Ziade
Thanos Lefteris
the Bunny Man
Thomas Thurman
Tobias
Tomi Pieviläinen
Trae Blain
Tshepang Lekhonkhobe
Valentin-Costel Hăloiu
Vlad Niculae
William Light
Wladislaw Merezhko
W. Trevor King
Zoresvit

View file

@ -1,8 +1,8 @@
# Tests
unittest2
mock
# Optional Packages
Markdown
BeautifulSoup
BeautifulSoup4
lxml
typogrify
webassets

View file

@ -1,10 +1,28 @@
Release history
###############
3.2 (XXXX-XX-XX)
3.3 (XXXX-XX-XX)
================
* [...]
3.2 (2013-04-24)
================
* Support for Python 3!
* Override page save-to location from meta-data (enables using a static page as
the site's home page, for example)
* Time period archives (per-year, per-month, and per-day archives of posts)
* Posterous blog import
* Improve WordPress blog import
* Migrate plugins to separate repository
* Improve HTML parser
* Provide ability to show or hide categories from menu using
``DISPLAY_CATEGORIES_ON_MENU`` option
* Auto-regeneration can be told to ignore files via ``IGNORE_FILES`` setting
* Improve post-generation feedback to user
* For multilingual posts, use meta-data to designate which is the original
and which is the translation
* Add ``.mdown`` to list of supported Markdown file extensions
* Document-relative URL generation (``RELATIVE_URLS``) is now off by default
3.1 (2012-12-04)
================

View file

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys, os
sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath(os.pardir))
from pelican import __version__, __major__
@ -10,8 +11,8 @@ templates_path = ['_templates']
extensions = ['sphinx.ext.autodoc',]
source_suffix = '.rst'
master_doc = 'index'
project = u'Pelican'
copyright = u'2010, Alexis Metaireau and contributors'
project = 'Pelican'
copyright = '2010, Alexis Metaireau and contributors'
exclude_patterns = ['_build']
version = __version__
release = __major__
@ -34,16 +35,16 @@ htmlhelp_basename = 'Pelicandoc'
# -- Options for LaTeX output --------------------------------------------------
latex_documents = [
('index', 'Pelican.tex', u'Pelican Documentation',
u'Alexis Métaireau', 'manual'),
('index', 'Pelican.tex', 'Pelican Documentation',
'Alexis Métaireau', 'manual'),
]
# -- Options for manual page output --------------------------------------------
man_pages = [
('index', 'pelican', u'pelican documentation',
[u'Alexis Métaireau'], 1),
('pelican-themes', 'pelican-themes', u'A theme manager for Pelican',
[u'Mickaël Raybaud'], 1),
('themes', 'pelican-theming', u'How to create themes for Pelican',
[u'The Pelican contributors'], 1)
('index', 'pelican', 'pelican documentation',
['Alexis Métaireau'], 1),
('pelican-themes', 'pelican-themes', 'A theme manager for Pelican',
['Mickaël Raybaud'], 1),
('themes', 'pelican-theming', 'How to create themes for Pelican',
['The Pelican contributors'], 1)
]

View file

@ -1,29 +1,34 @@
How to contribute?
###################
There are many ways to contribute to Pelican. You can enhance the
documentation, add missing features, and fix bugs (or just report them).
How to contribute
#################
Don't hesitate to fork and make a pull request on GitHub. When doing so, please
create a new feature branch as opposed to making your commits in the master
branch.
There are many ways to contribute to Pelican. You can improve the
documentation, add missing features, and fix bugs (or just report them). You
can also help out by reviewing and commenting on
`existing issues <https://github.com/getpelican/pelican/issues>`_.
Don't hesitate to fork Pelican and submit a pull request on GitHub. When doing
so, please adhere to the following guidelines.
.. include:: ../CONTRIBUTING.rst
Setting up the development environment
======================================
You're free to set up your development environment any way you like. Here is a
way using the `virtualenv <http://www.virtualenv.org/>`_ and `virtualenvwrapper
<http://www.doughellmann.com/projects/virtualenvwrapper/>`_ tools. If you don't
have them, you can install these both of these packages via::
While there are many ways to set up one's development environment, following
is a method that uses `virtualenv <http://www.virtualenv.org/>`_. If you don't
have ``virtualenv`` installed, you can install it via::
$ pip install virtualenvwrapper
$ pip install virtualenv
Virtual environments allow you to work on Python projects which are isolated
from one another so you can use different packages (and package versions) with
different projects.
To create a virtual environment, use the following syntax::
To create and activate a virtual environment, use the following syntax::
$ mkvirtualenv pelican
$ virtualenv ~/virtualenvs/pelican
$ cd ~/virtualenvs/pelican
$ . bin/activate
To clone the Pelican source::
@ -38,32 +43,102 @@ To install Pelican and its dependencies::
$ python setup.py develop
Running the test suite
======================
Or using ``pip``::
Each time you add a feature, there are two things to do regarding tests:
checking that the existing tests pass, and adding tests for the new feature
or bugfix.
The tests live in "pelican/tests" and you can run them using the
"discover" feature of unittest2::
$ unit2 discover
If you have made changes that affect the output of a Pelican-generated weblog,
then you should update the output used by functional tests.
To do so, you can use the following two commands::
$ LC_ALL="C" pelican -o tests/output/custom/ -s samples/pelican.conf.py \
samples/content/
$ LC_ALL="C" pelican -o tests/output/basic/ samples/content/
$ pip install -e .
Coding standards
================
Try to respect what is described in the `PEP8 specification
<http://www.python.org/dev/peps/pep-0008/>`_ when providing patches. This can be
eased via the `pep8 <http://pypi.python.org/pypi/pep8>`_ or `flake8
<http://www.python.org/dev/peps/pep-0008/>`_ when making contributions. This
can be eased via the `pep8 <http://pypi.python.org/pypi/pep8>`_ or `flake8
<http://pypi.python.org/pypi/flake8/>`_ tools, the latter of which in
particular will give you some useful hints about ways in which the
code/formatting can be improved.
Building the docs
=================
If you make changes to the documentation, you should preview your changes
before committing them::
$ pip install sphinx
$ cd src/pelican/docs
$ make html
Open ``_build/html/index.html`` in your browser to preview the documentation.
Running the test suite
======================
Each time you add a feature, there are two things to do regarding tests:
check that the existing tests pass, and add tests for the new feature
or bugfix.
The tests live in ``pelican/tests`` and you can run them using the
"discover" feature of ``unittest``::
$ python -m unittest discover
After making your changes and running the tests, you may see a test failure
mentioning that "some generated files differ from the expected functional tests
output." If you have made changes that affect the HTML output generated by
Pelican, and the changes to that output are expected and deemed correct given
the nature of your changes, then you should update the output used by the
functional tests. To do so, you can use the following two commands::
$ pelican -o pelican/tests/output/custom/ -s samples/pelican.conf.py \
samples/content/
$ pelican -o pelican/tests/output/basic/ samples/content/
Testing on Python 2 and 3
-------------------------
Testing on Python 3 currently requires some extra steps: installing
Python 3-compatible versions of dependent packages and plugins.
Tox_ is a useful tool to run tests on both versions. It will install the
Python 3-compatible version of dependent packages.
.. _Tox: http://testrun.org/tox/latest/
Python 3 development tips
=========================
Here are some tips that may be useful when doing some code for both Python 2.7
and Python 3 at the same time:
- Assume every string and literal is unicode (import unicode_literals):
- Do not use prefix ``u'``.
- Do not encode/decode strings in the middle of sth. Follow the code to the
source (or target) of a string and encode/decode at the first/last possible
point.
- In other words, write your functions to expect and to return unicode.
- Encode/decode strings if e.g. the source is a Python function that is known
to handle this badly, e.g. strftime() in Python 2.
- Use new syntax: print function, "except ... *as* e" (not comma) etc.
- Refactor method calls like ``dict.iteritems()``, ``xrange()`` etc. in a way
that runs without code change in both Python versions.
- Do not use magic method ``__unicode()__`` in new classes. Use only ``__str()__``
and decorate the class with ``@python_2_unicode_compatible``.
- Do not start int literals with a zero. This is a syntax error in Py3k.
- Unfortunately I did not find an octal notation that is valid in both
Pythons. Use decimal instead.
- use six, e.g.:
- ``isinstance(.., basestring) -> isinstance(.., six.string_types)``
- ``isinstance(.., unicode) -> isinstance(.., six.text_type)``
- ``setlocale()`` in Python 2 bails when we give the locale name as unicode,
and since we are using ``from __future__ import unicode_literals``, we do
that everywhere! As a workaround, I enclosed the localename with ``str()``;
in Python 2 this casts the name to a byte string, in Python 3 this should do
nothing, because the locale name already had been unicode.
- Kept range() almost everywhere as-is (2to3 suggests list(range())), just
changed it where I felt necessary.
- Changed xrange() back to range(), so it is valid in both Python versions.

View file

@ -10,32 +10,34 @@ If you have a problem, question, or suggestion, please start by striking up a
conversation on `#pelican on Freenode <irc://irc.freenode.net/pelican>`_.
Those who don't have an IRC client handy can jump in immediately via
`IRC webchat <http://webchat.freenode.net/?channels=pelican&uio=d4>`_. Because
of differing time zones, you may not get an immediate response to your question,
but please be patient and stay logged into IRC — someone will almost always
respond.
of differing time zones, you may not get an immediate response to your
question, but please be patient and stay logged into IRC — someone will almost
always respond if you wait long enough (it may take a few hours).
If you are unable to resolve your issue or if you have a feature request, please
If you're unable to resolve your issue or if you have a feature request, please
refer to the `issue tracker <https://github.com/getpelican/pelican/issues>`_.
How can I help?
================
There are several ways to help out. First, you can use Pelican and report any
There are several ways to help out. First, you can report any Pelican
suggestions or problems you might have via IRC or the `issue tracker
<https://github.com/getpelican/pelican/issues>`_.
<https://github.com/getpelican/pelican/issues>`_. If submitting an issue
report, please check the existing issue list first in order to avoid submitting
a duplicate issue.
If you want to contribute, please fork `the git repository
<https://github.com/getpelican/pelican/>`_, create a new feature branch, make
your changes, and issue a pull request. Someone will review your changes as soon
as possible. Please refer to the :doc:`How to Contribute <contribute>` section
for more details.
your changes, and issue a pull request. Someone will review your changes as
soon as possible. Please refer to the :doc:`How to Contribute <contribute>`
section for more details.
You can also contribute by creating themes and improving the documentation.
Is it mandatory to have a configuration file?
=============================================
No, it's not. Configuration files are just an easy way to configure Pelican.
Configuration files are optional and are just an easy way to configure Pelican.
For basic operations, it's possible to specify options while invoking Pelican
via the command line. See ``pelican --help`` for more information.
@ -44,10 +46,19 @@ I'm creating my own theme. How do I use Pygments for syntax highlighting?
Pygments adds some classes to the generated content. These classes are used by
themes to style code syntax highlighting via CSS. Specifically, you can
customize the appearance of your syntax highlighting via the ``.codehilite pre``
customize the appearance of your syntax highlighting via the ``.highlight pre``
class in your theme's CSS file. To see how various styles can be used to render
Django code, for example, you can use the demo `on the project website
<http://pygments.org/demo/15101/>`_.
Django code, for example, use the style selector drop-down at top-right on the
`Pygments project demo site <http://pygments.org/demo/15101/>`_.
You can use the following example commands to generate a starting CSS file from
a Pygments built-in style (in this case, "monokai") and then copy the generated
CSS file to your new theme::
pygmentize -S monokai -f html -a .highlight > pygment.css
cp pygment.css path/to/theme/static/css/
Don't forget to import your ``pygment.css`` file from your main CSS file.
How do I create my own theme?
==============================
@ -58,16 +69,16 @@ I want to use Markdown, but I got an error.
===========================================
Markdown is not a hard dependency for Pelican, so you will need to explicitly
install it. You can do so by typing the following, including ``sudo`` if
required::
install it. You can do so by typing the following command, prepending ``sudo``
if permissions require it::
(sudo) pip install markdown
pip install markdown
If you don't have pip installed, consider installing the pip installer via::
If you don't have ``pip`` installed, consider installing it via::
(sudo) easy_install pip
easy_install pip
Can I use arbitrary meta-data in my templates?
Can I use arbitrary metadata in my templates?
==============================================
Yes. For example, to include a modified date in a Markdown post, one could
@ -75,7 +86,11 @@ include the following at the top of the article::
Modified: 2012-08-08
That meta-data can then be accessed in the template::
For reStructuredText, this metadata should of course be prefixed with a colon::
:Modified: 2012-08-08
This metadata can then be accessed in the template::
{% if article.modified %}
Last modified: {{ article.modified }}
@ -84,23 +99,57 @@ That meta-data can then be accessed in the template::
How do I assign custom templates on a per-page basis?
=====================================================
It's as simple as adding an extra line of metadata to any pages or articles you
want to have its own template.
It's as simple as adding an extra line of metadata to any page or article that
you want to have its own template. For example, this is how it would be handled
for content in reST format::
:template: template_name
For content in Markdown format::
Template: template_name
Then just make sure your theme contains the relevant template file (e.g.
``template_name.html``).
How can I override the generated URL of a specific page or article?
===================================================================
Include ``url`` and ``save_as`` metadata in any pages or articles that you want
to override the generated URL. Here is an example page in reST format::
Override url/save_as page
#########################
:url: override/url/
:save_as: override/url/index.html
With this metadata, the page will be written to ``override/url/index.html``
and Pelican will use url ``override/url/`` to link to this page.
How can I use a static page as my home page?
============================================
The override feature mentioned above can be used to specify a static page as
your home page. The following Markdown example could be stored in
``content/pages/home.md``::
Title: Welcome to My Site
URL:
save_as: index.html
Thank you for visiting. Welcome!
What if I want to disable feed generation?
==========================================
To disable all feed generation, all feed settings should be set to ``None``.
All but two feed settings already default to ``None``, so if you want to disable
all feed generation, you only need to specify the following settings::
To disable feed generation, all feed settings should be set to ``None``.
All but three feed settings already default to ``None``, so if you want to
disable all feed generation, you only need to specify the following settings::
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
Please note that ``None`` and ``''`` are not the same thing. The word ``None``
should not be surrounded by quotes.
@ -108,23 +157,20 @@ should not be surrounded by quotes.
I'm getting a warning about feeds generated without SITEURL being set properly
==============================================================================
`RSS and Atom feeds require all URLs and links in them to be absolute
`RSS and Atom feeds require all URL links to be absolute
<http://validator.w3.org/feed/docs/rss2.html#comments>`_.
In order to properly generate all URLs properly in Pelican you will need to set
``SITEURL`` to the full path of your blog. When using ``make html`` and the
default Makefile provided by the `pelican-quickstart` bootstrap script to test
build your site, it's normal to see this warning since ``SITEURL`` is
deliberately left undefined. If configured properly no other ``make`` commands
should result in this warning.
In order to properly generate links in Pelican you will need to set ``SITEURL``
to the full path of your site.
Feeds are still generated when this warning is displayed but may not validate.
Feeds are still generated when this warning is displayed, but links within may
be malformed and thus the feed may not validate.
My feeds are broken since I upgraded to Pelican 3.x
===================================================
Starting in 3.0, some of the FEED setting names were changed to more explicitly
refer to the Atom feeds they inherently represent (much like the FEED_RSS
setting names). Here is an exact list of the renamed setting names::
setting names). Here is an exact list of the renamed settings::
FEED -> FEED_ATOM
TAG_FEED -> TAG_FEED_ATOM

View file

@ -4,28 +4,34 @@ Getting started
Installing Pelican
==================
You're ready? Let's go! You can install Pelican via several different methods.
The simplest is via `pip <http://www.pip-installer.org/>`_::
Pelican currently runs best on Python 2.7.x; earlier versions of Python are
not supported. There is provisional support for Python 3.3, although there may
be rough edges, particularly with regards to optional 3rd-party components.
You can install Pelican via several different methods. The simplest is via
`pip <http://www.pip-installer.org/>`_::
$ pip install pelican
If you don't have ``pip`` installed, an alternative method is ``easy_install``::
If you don't have ``pip`` installed, an alternative method is
``easy_install``::
$ easy_install pelican
While the above is the simplest method, the recommended approach is to create
a virtual environment for Pelican via virtualenv_ and virtualenvwrapper_ before
installing Pelican. Assuming you've followed the virtualenvwrapper
`installation <http://virtualenvwrapper.readthedocs.org/en/latest/install.html>`_
and `shell configuration
<http://virtualenvwrapper.readthedocs.org/en/latest/install.html#shell-startup-file>`_
steps, you can then open a new terminal session and create a new virtual
environment for Pelican::
(Keep in mind that operating systems will often require you to prefix the above
commands with ``sudo`` in order to install Pelican system-wide.)
$ mkvirtualenv pelican
While the above is the simplest method, the recommended approach is to create
a virtual environment for Pelican via virtualenv_ before installing Pelican.
Assuming you have virtualenv_ installed, you can then open a new terminal
session and create a new virtual environment for Pelican::
$ virtualenv ~/virtualenvs/pelican
$ cd ~/virtualenvs/pelican
$ . bin/activate
Once the virtual environment has been created and activated, Pelican can be
be installed via ``pip`` or ``easy_install`` as noted above. Alternatively, if
be installed via ``pip install pelican`` as noted above. Alternatively, if
you have the project source, you can install Pelican using the distutils
method::
@ -46,6 +52,46 @@ If you want to use AsciiDoc you need to install it from `source
<http://www.methods.co.nz/asciidoc/INSTALL.html>`_ or use your operating
system's package manager.
Basic usage
-----------
Once Pelican is installed, you can use it to convert your Markdown or reST
content into HTML via the ``pelican`` command, specifying the path to your
content and (optionally) the path to your settings file::
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
The above command will generate your site and save it in the ``output/``
folder, using the default theme to produce a simple site. The default theme
consists of very simple HTML without styling and is provided so folks may use
it as a basis for creating their own themes.
You can also tell Pelican to watch for your modifications, instead of
manually re-running it every time you want to see your changes. To enable this,
run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
Pelican has other command-line switches available. Have a look at the help to
see all the options you can use::
$ pelican --help
Continue reading below for more detail, and check out the Pelican wiki's
`Tutorials <https://github.com/getpelican/pelican/wiki/Tutorials>`_ page for
links to community-published tutorials.
Viewing the generated files
---------------------------
The files generated by Pelican are static files, so you don't actually need
anything special to view them. You can either use your browser to open the
files on your disk::
firefox output/index.html
Or run a simple web server using Python::
cd output && python -m SimpleHTTPServer
Upgrading
---------
@ -61,60 +107,72 @@ perform the same step to install the most recent version.
Dependencies
------------
At this time, Pelican is dependent on the following Python packages:
When Pelican is installed, the following dependent Python packages should be
automatically installed without any action on your part:
* feedgenerator, to generate the Atom feeds
* jinja2, for templating support
* docutils, for supporting reStructuredText as an input format
* `feedgenerator <http://pypi.python.org/pypi/feedgenerator>`_, to generate the
Atom feeds
* `jinja2 <http://pypi.python.org/pypi/Jinja2>`_, for templating support
* `pygments <http://pypi.python.org/pypi/Pygments>`_, for syntax highlighting
* `docutils <http://pypi.python.org/pypi/docutils>`_, for supporting
reStructuredText as an input format
* `pytz <http://pypi.python.org/pypi/pytz>`_, for timezone definitions
* `blinker <http://pypi.python.org/pypi/blinker>`_, an object-to-object and
broadcast signaling system
* `unidecode <http://pypi.python.org/pypi/Unidecode>`_, for ASCII
transliterations of Unicode text
If you're not using Python 2.7, you will also need the ``argparse`` package.
If you want the following optional packages, you will need to install them
manually via ``pip``:
Optionally:
* `markdown <http://pypi.python.org/pypi/Markdown>`_, for supporting Markdown as
an input format
* `typogrify <http://pypi.python.org/pypi/typogrify>`_, for typographical
enhancements
* pygments, for syntax highlighting
* Markdown, for supporting Markdown as an input format
* Typogrify, for typographical enhancements
Kickstart your site
===================
Kickstart a blog
================
Following is a brief tutorial for those who want to get started right away.
We're going to assume that virtualenv_ and virtualenvwrapper_ are installed and
configured; if you've installed Pelican outside of a virtual environment,
you can skip to the ``pelican-quickstart`` command. Let's first create a new
virtual environment and install Pelican into it::
$ mkvirtualenv pelican
$ pip install pelican Markdown
Next we'll create a directory to house our site content and configuration files,
which can be located any place you prefer, and associate this new project with
the currently-active virtual environment::
$ mkdir ~/code/yoursitename
$ cd ~/code/yoursitename
$ setvirtualenvproject
Now we can run the ``pelican-quickstart`` command, which will ask some questions
about your site::
Once Pelican has been installed, you can create a skeleton project via the
``pelican-quickstart`` command, which begins by asking some questions about
your site::
$ pelican-quickstart
Once you finish answering all the questions, you can begin adding content to the
*content* folder that has been created for you. (See *Writing articles using
Pelican* section below for more information about how to format your content.)
Once you have some content to generate, you can convert it to HTML via the
following command::
Once you finish answering all the questions, your project will consist of the
following hierarchy (except for "pages", which you can optionally add yourself
if you plan to create non-chronological content)::
yourproject/
├── content
│   └── (pages)
├── output
├── develop_server.sh
├── Makefile
├── pelicanconf.py # Main settings file
└── publishconf.py # Settings to use when ready to publish
The next step is to begin to adding content to the *content* folder that has
been created for you. (See *Writing articles using Pelican* section below for
more information about how to format your content.)
Once you have written some content to generate, you can use the ``pelican``
command to generate your site, which will be placed in the output folder.
Alternatively, you can use automation tools that "wrap" the ``pelican`` command
to simplify the process of generating, previewing, and uploading your site. One
such tool is the ``Makefile`` that's automatically created for you when you use
``pelican-quickstart`` to create a skeleton project. To use ``make`` to
generate your site, run::
$ make html
If you'd prefer to have Pelican automatically regenerate your site every time a
change is detected (handy when testing locally), use the following command
instead::
change is detected (which is handy when testing locally), use the following
command instead::
$ make regenerate
To serve the site so it can be previewed in your browser at
To serve the generated site so it can be previewed in your browser at
http://localhost:8000::
$ make serve
@ -138,18 +196,29 @@ use rsync over ssh::
That's it! Your site should now be live.
Writing articles using Pelican
==============================
Writing content using Pelican
=============================
Articles and pages
------------------
Pelican considers "articles" to be chronological content, such as posts on a
blog, and thus associated with a date.
The idea behind "pages" is that they are usually not temporal in nature and are
used for content that does not change very often (e.g., "About" or "Contact"
pages).
File metadata
--------------
-------------
Pelican tries to be smart enough to get the information it needs from the
file system (for instance, about the category of your articles), but some
information you need to provide in the form of metadata inside your files.
You can provide this metadata in reStructuredText text files via the
following syntax (give your file the ``.rst`` extension)::
If you are writing your content in reStructuredText format, you can provide
this metadata in text files via the following syntax (give your file the
``.rst`` extension)::
My super title
##############
@ -166,77 +235,78 @@ Pelican implements an extension to reStructuredText to enable support for the
This will be turned into :abbr:`HTML (HyperText Markup Language)`.
You can also use Markdown syntax (with a file ending in ``.md``, ``.markdown``,
or ``.mkd``). Markdown generation will not work until you explicitly install the
``Markdown`` package, which can be done via ``pip install Markdown``. Metadata
syntax for Markdown posts should follow this pattern::
You can also use Markdown syntax (with a file ending in ``.md``,
``.markdown``, ``.mkd``, or ``.mdown``). Markdown generation requires that you
first explicitly install the ``Markdown`` package, which can be done via ``pip
install Markdown``. Metadata syntax for Markdown posts should follow this
pattern::
Title: My super title
Date: 2010-12-03 10:20
Tags: thats, awesome
Category: yeah
Category: Python
Tags: pelican, publishing
Slug: my-super-post
Author: Alexis Metaireau
Summary: Short version for index and feeds
This is the content of my super blog post.
Note that, aside from the title, none of this metadata is mandatory: if the
date is not specified, Pelican can rely on the file's "mtime" timestamp through
the ``DEFAULT_DATE`` setting, and the category can be determined by the
directory in which the file resides. For example, a file located at
Pelican can also process HTML files ending in ``.html`` and ``.htm``. Pelican
interprets the HTML in a very straightforward manner, reading metadata from
``meta`` tags, the title from the ``title`` tag, and the body out from the
``body`` tag::
<html>
<head>
<title>My super title</title>
<meta name="tags" contents="thats, awesome" />
<meta name="date" contents="2012-07-09 22:28" />
<meta name="category" contents="yeah" />
<meta name="author" contents="Alexis Métaireau" />
<meta name="summary" contents="Short version for index and feeds" />
</head>
<body>
This is the content of my super blog post.
</body>
</html>
With HTML, there is one simple exception to the standard metadata: ``tags`` can
be specified either via the ``tags`` metadata, as is standard in Pelican, or
via the ``keywords`` metadata, as is standard in HTML. The two can be used
interchangeably.
Note that, aside from the title, none of this article metadata is mandatory:
if the date is not specified and ``DEFAULT_DATE`` is set to ``fs``, Pelican
will rely on the file's "mtime" timestamp, and the category can be determined
by the directory in which the file resides. For example, a file located at
``python/foobar/myfoobar.rst`` will have a category of ``foobar``. If you would
like to organize your files in other ways where the name of the subfolder would
not be a good category name, you can set the setting ``USE_FOLDER_AS_CATEGORY``
to ``False``. If there is no summary metadata for a given post, the
to ``False``.
If you do not explicitly specify summary metadata for a given post, the
``SUMMARY_MAX_LENGTH`` setting can be used to specify how many words from the
beginning of an article are used as the summary.
You can also extract any metadata from the filename through a regular
expression to be set in the ``FILENAME_METADATA`` setting.
All named groups that are matched will be set in the metadata object. The
default value for the ``FILENAME_METADATA`` setting will only extract the date
from the filename. For example, if you would like to extract both the date and
the slug, you could set something like:
``'(?P<date>\d{4}-\d{2}-\d{2})_(?P<slug>.*)'``
expression to be set in the ``FILENAME_METADATA`` setting. All named groups
that are matched will be set in the metadata object. The default value for the
``FILENAME_METADATA`` setting will only extract the date from the filename. For
example, if you would like to extract both the date and the slug, you could set
something like: ``'(?P<date>\d{4}-\d{2}-\d{2})_(?P<slug>.*)'``
Please note that the metadata available inside your files takes precedence over
the metadata extracted from the filename.
Generate your blog
------------------
The ``make`` shortcut commands mentioned in the *Kickstart a blog* section
are mostly wrappers around the ``pelican`` command that generates the HTML from
the content. The ``pelican`` command can also be run directly::
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
The above command will generate your weblog and save it in the ``output/``
folder, using the default theme to produce a simple site. The default theme is
simple HTML without styling and is provided so folks may use it as a basis for
creating their own themes.
Pelican has other command-line switches available. Have a look at the help to
see all the options you can use::
$ pelican --help
Auto-reload
-----------
It's possible to tell Pelican to watch for your modifications, instead of
manually re-running it every time you want to see your changes. To enable this,
run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
Pages
-----
If you create a folder named ``pages`` inside the content folder, all the
files in it will be used to generate static pages.
files in it will be used to generate static pages, such as **About** or
**Contact** pages. (See example filesystem layout below.)
Then, use the ``DISPLAY_PAGES_ON_MENU`` setting to add all those pages to
the primary navigation menu.
You can use the ``DISPLAY_PAGES_ON_MENU`` setting to control whether all those
pages are displayed in the primary navigation menu. (Default is ``True``.)
If you want to exclude any pages from being linked to or listed in the menu
then add a ``status: hidden`` attribute to its metadata. This is useful for
@ -251,23 +321,25 @@ hierarchy. This makes it easier to link from the current post to other posts
and images that may be sitting alongside the current post (instead of having
to determine where those resources will be placed after site generation).
To link to internal content, use the following syntax:
``|filename|path/to/file``.
To link to internal content (files in the ``content`` directory), use the
following syntax: ``|filename|path/to/file``::
For example, you may want to add links between "article1" and "article2" given
the structure::
website/
├── content
│   ├── article1.rst
│   └── cat/
│      └── article2.md
│   ├── cat/
│   │   └── article2.md
│ └── pages
│      └── about.md
└── pelican.conf.py
In this example, ``article1.rst`` could look like::
Title: The first article
Date: 2012-12-01
The first article
#################
:date: 2012-12-01 10:02
See below intra-site link examples in reStructuredText format.
@ -277,18 +349,37 @@ In this example, ``article1.rst`` could look like::
and ``article2.md``::
Title: The second article
Date: 2012-12-01
Date: 2012-12-01 10:02
See below intra-site link examples in Markdown format.
[a link relative to content root](|filename|/article1.rst)
[a link relative to current file](|filename|../article1.rst)
.. note::
Embedding non-article or non-page content is slightly different in that the
directories need to be specified in ``pelicanconf.py`` file. The ``images``
directory is configured for this by default but others will need to be added
manually::
You can use the same syntax to link to internal pages or even static
content (like images) which would be available in a directory listed in
``settings["STATIC_PATHS"]``.
content
├── images
│   └── han.jpg
└── misc
   └── image-test.md
And ``image-test.md`` would include::
![Alt Text](|filename|/images/han.jpg)
Any content can be linked in this way. What happens is that the ``images``
directory gets copied to ``output/static/`` upon publishing. This is
because ``images`` is in the ``settings["STATIC_PATHS"]`` list by default. If
you want to have another directory, say ``pdfs`` you would need to add the
following to ``pelicanconf.py``::
STATIC_PATHS = ['images', 'pdfs']
And then the ``pdfs`` directory would also be copied to ``output/static/``.
Importing an existing blog
--------------------------
@ -338,6 +429,19 @@ identifier. If you'd rather not explicitly define the slug this way, you must
then instead ensure that the translated article titles are identical, since the
slug will be auto-generated from the article title.
If you do not want the original version of one specific article to be detected
by the ``DEFAULT_LANG`` setting, use the ``translation`` metadata to specify
which posts are translations::
Foobar is not dead
##################
:slug: foobar-is-not-dead
:lang: en
:translation: true
That's true, foobar is still alive!
Syntax highlighting
-------------------
@ -369,19 +473,4 @@ publishing, for example), you can add a ``status: draft`` attribute to its
metadata. That article will then be output to the ``drafts`` folder and not
listed on the index page nor on any category page.
Viewing the generated files
---------------------------
The files generated by Pelican are static files, so you don't actually need
anything special to see what's happening with the generated files.
You can either use your browser to open the files on your disk::
firefox output/index.html
Or run a simple web server using Python::
cd output && python -m SimpleHTTPServer
.. _virtualenv: http://www.virtualenv.org/
.. _virtualenvwrapper: http://www.doughellmann.com/projects/virtualenvwrapper/

View file

@ -4,55 +4,61 @@
Import from other blog software
=================================
Description
===========
``pelican-import`` is a command line tool for converting articles from other
software to ReStructuredText. The supported formats are:
``pelican-import`` is a command-line tool for converting articles from other
software to reStructuredText or Markdown. The supported import formats are:
- WordPress XML export
- Dotclear export
- Posterous API
- RSS/Atom feed
The conversion from HTML to reStructuredText relies on `pandoc
<http://johnmacfarlane.net/pandoc/>`_. For Dotclear, if the source posts are
written with Markdown syntax, they will not be converted (as Pelican also
supports Markdown).
The conversion from HTML to reStructuredText or Markdown relies on `Pandoc`_.
For Dotclear, if the source posts are written with Markdown syntax, they will
not be converted (as Pelican also supports Markdown).
Dependencies
""""""""""""
============
``pelican-import`` has two dependencies not required by the rest of pelican:
``pelican-import`` has some dependencies not required by the rest of Pelican:
- BeautifulSoup
- pandoc
- *BeautifulSoup4* and *lxml*, for WordPress and Dotclear import. Can be installed like
any other Python package (``pip install BeautifulSoup4 lxml``).
- *Feedparser*, for feed import (``pip install feedparser``).
- *Pandoc*, see the `Pandoc site`_ for installation instructions on your
operating system.
BeatifulSoup can be installed like any other Python package::
$ pip install BeautifulSoup
For pandoc, install a package for your operating system from the
`pandoc site <http://johnmacfarlane.net/pandoc/installing.html>`_.
.. _Pandoc: http://johnmacfarlane.net/pandoc/
.. _Pandoc site: http://johnmacfarlane.net/pandoc/installing.html
Usage
"""""
=====
| pelican-import [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT]
| [-m MARKUP] [--dir-cat] [--strip-raw] [--disable-slugs]
| input
::
pelican-import [-h] [--wpfile] [--dotclear] [--posterous] [--feed] [-o OUTPUT]
[-m MARKUP] [--dir-cat] [--dir-page] [--strip-raw] [--disable-slugs]
[-e EMAIL] [-p PASSWORD]
input|api_token
Positional arguments
====================
--------------------
input The input file to read
api_token [Posterous only] api_token can be obtained from http://posterous.com/api/
Optional arguments
""""""""""""""""""
------------------
-h, --help show this help message and exit
--wpfile Wordpress XML export (default: False)
-h, --help Show this help message and exit
--wpfile WordPress XML export (default: False)
--dotclear Dotclear export (default: False)
--posterous Posterous API (default: False)
--feed Feed to parse (default: False)
-o OUTPUT, --output OUTPUT
Output path (default: output)
@ -61,6 +67,8 @@ Optional arguments
(default: rst)
--dir-cat Put files in directories with categories name
(default: False)
--dir-page Put files recognised as pages in "pages/" sub-
directory (wordpress import only) (default: False)
--strip-raw Strip raw HTML code that can't be converted to markup
such as flash embeds or iframes (wordpress import
only) (default: False)
@ -68,6 +76,11 @@ Optional arguments
output. With this disabled, your Pelican URLs may not
be consistent with your original posts. (default:
False)
-e EMAIL, --email=EMAIL
Email used to authenticate Posterous API
-p PASSWORD, --password=PASSWORD
Password used to authenticate Posterous API
Examples
========
@ -80,10 +93,15 @@ For Dotclear::
$ pelican-import --dotclear -o ~/output ~/backup.txt
for Posterous::
$ pelican-import --posterous -o ~/output --email=<email_address> --password=<password> <api_token>
Tests
=====
To test the module, one can use sample files:
- for Wordpress: http://wpcandy.com/made/the-sample-post-collection
- for WordPress: http://wpcandy.com/made/the-sample-post-collection
- for Dotclear: http://themes.dotaddict.org/files/public/downloads/lorem-backup.txt

View file

@ -3,10 +3,10 @@ Pelican
Pelican is a static site generator, written in Python_.
* Write your weblog entries directly with your editor of choice (vim!)
in reStructuredText_, Markdown_, or AsciiDoc_
* Includes a simple CLI tool to (re)generate the weblog
* Easy to interface with DVCSes and web hooks
* Write your content directly with your editor of choice (vim!)
in reStructuredText_, Markdown_, or AsciiDoc_ formats
* Includes a simple CLI tool to (re)generate your site
* Easy to interface with distributed version control systems and web hooks
* Completely static output is easy to host anywhere
Features
@ -14,16 +14,15 @@ Features
Pelican currently supports:
* Blog articles and pages
* Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact")
* Comments, via an external service (Disqus). (Please note that while
useful, Disqus is an external service, and thus the comment data will be
somewhat outside of your control and potentially subject to data loss.)
* Theming support (themes are created using Jinja2_ templates)
* PDF generation of the articles/pages (optional)
* Publication of articles in multiple languages
* Atom/RSS feeds
* Code syntax highlighting
* Asset management with `webassets`_ (optional)
* PDF generation of the articles/pages (optional)
* Import from WordPress, Dotclear, or RSS feeds
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
@ -80,4 +79,3 @@ A French version of the documentation is available at :doc:`fr/index`.
.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4
.. _webassets: https://github.com/miracle2k/webassets

View file

@ -23,8 +23,8 @@ The logic is separated into different classes and concepts:
on. Since those operations are commonly used, the object is created once and
then passed to the generators.
* **Readers** are used to read from various formats (AsciiDoc, Markdown and
reStructuredText for now, but the system is extensible). Given a file, they
* **Readers** are used to read from various formats (AsciiDoc, HTML, Markdown and
reStructuredText for now, but the system is extensible). Given a file, they
return metadata (author, tags, category, etc.) and content (HTML-formatted).
* **Generators** generate the different outputs. For instance, Pelican comes with
@ -47,19 +47,17 @@ Take a look at the Markdown reader::
class MarkdownReader(Reader):
enabled = bool(Markdown)
def read(self, filename):
def read(self, source_path):
"""Parse content and metadata of markdown files"""
text = open(filename)
text = pelican_open(source_path)
md = Markdown(extensions = ['meta', 'codehilite'])
content = md.convert(text)
metadata = {}
for name, value in md.Meta.items():
if name in _METADATA_FIELDS:
meta = _METADATA_FIELDS[name](value[0])
else:
meta = value[0]
metadata[name.lower()] = meta
name = name.lower()
meta = self.process_metadata(name, value[0])
metadata[name] = meta
return content, metadata
Simple, isn't it?

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,19 +13,32 @@ 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::
in the settings. ``PLUGIN_PATH`` can be an absolute path or a path relative to
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 visit the `pelican-plugins`_ repository for a list of available plugins.
.. _pelican-plugins: https://github.com/getpelican/pelican-plugins
Please note that while we do our best to review and maintain these plugins,
they are submitted by the Pelican community and thus may have varying levels of
support and interoperability.
How to create plugins
=====================
@ -47,8 +57,6 @@ which you map the signals to your plugin logic. Let's take a simple example::
def register():
signals.initialized.connect(test)
List of signals
===============
@ -73,6 +81,8 @@ get_generators generators invoked in Pelic
generator in a tuple or in a list.
pages_generate_context pages_generator, metadata
pages_generator_init pages_generator invoked in the PagesGenerator.__init__
pages_generator_finalized pages_generator invoked at the end of PagesGenerator.generate_context
content_object_init content_object invoked at the end of Content.__init__ (see note below)
============================= ============================ ===========================================================================
The list is currently small, so don't hesitate to add signals and make a pull
@ -94,270 +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``
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. However, this also means that
the LESS and SASS files are not compiled. This should be fixed in a future
version of Webassets (cf. the related `bug report
<https://github.com/getpelican/pelican/issues/481>`_).
.. _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.
To enable, add the following to your settings file::
from pelican.plugins import related_posts
PLUGINS = [related_posts]
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>{{ related_post }}</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'
}
}

View file

@ -40,6 +40,9 @@ Setting name (default value) What doe
`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on.
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the
template. Templates may or may not honor this
setting.
`DISPLAY_CATEGORIES_ON_MENU` (``True``) Whether to display categories on the menu of the
template. Templates may or not honor this
setting.
`DEFAULT_DATE` (``None``) The default date you want to use.
@ -59,23 +62,39 @@ Setting name (default value) What doe
For example, if you would like to extract both the
date and the slug, you could set something like:
``'(?P<date>\d{4}-\d{2}-\d{2})_(?P<slug>.*)'``.
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
generating new files.
`PATH_METADATA` (``''``) Like ``FILENAME_METADATA``, but parsed from a page's
full path relative to the content source directory.
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory, and **all** of its contents, before
generating new files. This can be useful in preventing older,
unnecessary files from persisting in your output. However, **this is
a destructive setting and should be handled with extreme care.**
`FILES_TO_COPY` (``()``) A list of files (or directories) to copy from the source (inside the
content directory) to the destination (inside the output directory).
For example: ``(('extra/robots.txt', 'robots.txt'),)``.
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
`JINJA_FILTERS` (``{}``) A list of custom Jinja2 filters you want to use.
The dictionary should map the filtername to the filter function.
For example: ``{'urlencode': urlencode_filter}``
See `Jinja custom filters documentation`_.
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
here or a single string representing one locale.
When providing a list, all the locales will be tried
until one works.
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
to use. For the moment, the only available values
are `rst`, `md`, `markdown`, `mkd`, `html`, and `htm`.
`MD_EXTENSIONS` (``['codehilite','extra']``) A list of the extensions that the Markdown processor
will use. Refer to the extensions chapter in the
Python-Markdown documentation for a complete list of
supported extensions.
are `rst`, `md`, `markdown`, `mkd`, `mdown`, `html`, and `htm`.
`IGNORE_FILES` (``['.#*']``) A list of file globbing patterns to match against the
source files to be ignored by the processor. For example,
the default ``['.#*']`` will ignore emacs lock files.
`MD_EXTENSIONS` (``['codehilite(css_class=highlight)','extra']``) A list of the extensions that the Markdown processor
will use. Refer to the Python Markdown documentation's
`Extensions section <http://pythonhosted.org/Markdown/extensions/>`_
for a complete list of supported extensions. (Note that
defining this in your settings file will override and
replace the default values. If your goal is to *add*
to the default values for this setting, you'll need to
include them explicitly and enumerate the full list of
desired Markdown extensions.)
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
`PATH` (``None``) Path to content directory to be processed by Pelican.
`PAGE_DIR` (``'pages'``) Directory to look at for pages, relative to `PATH`.
@ -90,9 +109,9 @@ Setting name (default value) What doe
`OUTPUT_SOURCES_EXTENSION` (``.text``) Controls the extension that will be used by the SourcesGenerator.
Defaults to ``.text``. If not a valid string the default value
will be used.
`RELATIVE_URLS` (``True``) Defines whether Pelican should use document-relative URLs or
not. If set to ``False``, Pelican will use the SITEURL
setting to construct absolute URLs.
`RELATIVE_URLS` (``False``) Defines whether Pelican should use document-relative URLs or
not. Only set this to ``True`` when developing/testing and only
if you fully understand the effect it can have on links/feeds.
`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,
@ -176,6 +195,27 @@ Example usage:
This would save your articles in something like ``/posts/2011/Aug/07/sample-post/index.html``,
and the URL to this would be ``/posts/2011/Aug/07/sample-post/``.
Pelican can optionally create per-year, per-month, and per-day archives of your
posts. These secondary archives are disabled by default but are automatically
enabled if you supply format strings for their respective `_SAVE_AS` settings.
Period archives fit intuitively with the hierarchical model of web URLs and can
make it easier for readers to navigate through the posts you've written over time.
Example usage:
* YEAR_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/index.html'``
* MONTH_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/{date:%b}/index.html'``
With these settings, Pelican will create an archive of all your posts for the year
at (for instance) 'posts/2011/index.html', and an archive of all your posts for
the month at 'posts/2011/Aug/index.html'.
.. note::
Period archives work best when the final path segment is 'index.html'.
This way a reader can remove a portion of your URL and automatically
arrive at an appropriate archive of posts, without having to specify
a page name.
==================================================== =====================================================
Setting name (default value) What does it do?
==================================================== =====================================================
@ -186,7 +226,9 @@ Setting name (default value) What does it do?
`ARTICLE_LANG_SAVE_AS` (``'{slug}-{lang}.html'``) The place where we will save an article which
doesn't use the default language.
`PAGE_URL` (``'pages/{slug}.html'``) The URL we will use to link to a page.
`PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page.
`PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page. This value has to be
the same as PAGE_URL or you need to use a rewrite in
your server config.
`PAGE_LANG_URL` (``'pages/{slug}-{lang}.html'``) The URL we will use to link to a page which doesn't
use the default language.
`PAGE_LANG_SAVE_AS` (``'pages/{slug}-{lang}.html'``) The location we will save the page which doesn't
@ -200,6 +242,12 @@ Setting name (default value) What does it do?
`<DIRECT_TEMPLATE_NAME>_SAVE_AS` The location to save content generated from direct
templates. Where <DIRECT_TEMPLATE_NAME> is the
upper case template name.
`YEAR_ARCHIVE_SAVE_AS` (False) The location to save per-year archives of your
posts.
`MONTH_ARCHIVE_SAVE_AS` (False) The location to save per-month archives of your
posts.
`DAY_ARCHIVE_SAVE_AS` (False) The location to save per-day archives of your
posts.
==================================================== =====================================================
.. note::
@ -223,10 +271,10 @@ Have a look at `the wikipedia page`_ to get a list of valid timezone values.
Date format and locale
----------------------
If no DATE_FORMATS is set, fall back to DEFAULT_DATE_FORMAT. If you need to
maintain multiple languages with different date formats, you can set this dict
using language name (``lang`` in your posts) as key. Regarding available format
codes, see `strftime document of python`_ :
If no DATE_FORMATS are set, Pelican will fall back to DEFAULT_DATE_FORMAT. If
you need to maintain multiple languages with different date formats, you can
set this dict using the language name (``lang`` metadata in your post content)
as the key. Regarding available format codes, see `strftime document of python`_ :
.. parsed-literal::
@ -386,7 +434,7 @@ The default theme does not support tag clouds, but it is pretty easy to add::
<ul>
{% for tag in tag_cloud %}
<li class="tag-{{ tag.1 }}"><a href="/tag/{{ tag.0 }}/">{{ tag.0 }}</a></li>
<li class="tag-{{ tag.1 }}"><a href="/tag/{{ tag.0|string|replace(" ", "-" ) }}.html">{{ tag.0 }}</a></li>
{% endfor %}
</ul>
@ -470,6 +518,7 @@ free to use them in your themes as well.
======================= =======================================================
Setting name What does it do ?
======================= =======================================================
`SITESUBTITLE` A subtitle to appear in the header.
`DISQUS_SITENAME` Pelican can handle Disqus comments. Specify the
Disqus sitename identifier here.
`GITHUB_URL` Your GitHub URL (if you have one). It will then
@ -503,3 +552,6 @@ Example settings
.. literalinclude:: ../samples/pelican.conf.py
:language: python
.. _Jinja custom filters documentation: http://jinja.pocoo.org/docs/api/#custom-filters

View file

@ -17,16 +17,17 @@ To make your own theme, you must follow the following structure::
│   ├── css
│   └── images
└── templates
├── archives.html // to display archives
├── article.html // processed for each article
├── author.html // processed for each author
├── authors.html // must list all the authors
├── categories.html // must list all the categories
├── category.html // processed for each category
├── index.html // the index. List all the articles
├── page.html // processed for each page
├── tag.html // processed for each tag
└── tags.html // must list all the tags. Can be a tag cloud.
├── archives.html // to display archives
├── period_archives.html // to display time-period archives
├── article.html // processed for each article
├── author.html // processed for each author
├── authors.html // must list all the authors
├── categories.html // must list all the categories
├── category.html // processed for each category
├── index.html // the index. List all the articles
├── page.html // processed for each page
├── tag.html // processed for each tag
└── tags.html // must list all the tags. Can be a tag cloud.
* `static` contains all the static assets, which will be copied to the output
`theme` folder. I've put the CSS and image folders here, but they are
@ -54,10 +55,15 @@ All of these settings will be available to all templates.
============= ===================================================
Variable Description
============= ===================================================
output_file The name of the file currently being generated. For
instance, when Pelican is rendering the homepage,
output_file will be "index.html".
articles The list of articles, ordered descending by date
All the elements are `Article` objects, so you can
access their attributes (e.g. title, summary, author
etc.)
etc.). Sometimes this is shadowed (for instance in
the tags page). You will then find info about it
in the `all_articles` variable.
dates The same list of articles, but ordered by date,
ascending
tags A list of (tag, articles) tuples, containing all
@ -68,6 +74,37 @@ categories A list of (category, articles) tuples, containing
pages The list of pages
============= ===================================================
Sorting
-------
URL wrappers (currently categories, tags, and authors), have
comparison methods that allow them to be easily sorted by name::
{% for tag, articles in tags|sort %}
If you want to sort based on different criteria, `Jinja's sort
command`__ has a number of options.
__ http://jinja.pocoo.org/docs/templates/#sort
Date Formatting
---------------
Pelican formats the date with according to your settings and locale
(``DATE_FORMATS``/``DEFAULT_DATE_FORMAT``) and provides a
``locale_date`` attribute. On the other hand, ``date`` attribute will
be a `datetime`_ object. If you need custom formatting for a date
different than your settings, use the Jinja filter ``strftime``
that comes with Pelican. Usage is same as Python `strftime`_ format,
but the filter will do the right thing and format your date according
to the locale given in your settings::
{{ article.date|strftime('%d %B %Y') }}
.. _datetime: http://docs.python.org/2/library/datetime.html#datetime-objects
.. _strftime: http://docs.python.org/2/library/datetime.html#strftime-strptime-behavior
index.html
----------

View file

@ -6,47 +6,80 @@ Here are some tips about Pelican that you might find useful.
Publishing to GitHub
====================
GitHub comes with an interesting "pages" feature: you can upload things there
and it will be available directly from their servers. As Pelican is a static
file generator, we can take advantage of this.
User Pages
----------
GitHub allows you to create user pages in the form of ``username.github.com``.
Whatever is created in the master branch will be published. For this purpose,
just the output generated by Pelican needs to pushed to GitHub.
So given a repository containing your articles, just run Pelican over the posts
and deploy the master branch to GitHub::
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
Now add all the files in the output directory generated by Pelican::
$ git add /path/to/output/*
$ git commit -am "Your Message"
$ git push origin master
`GitHub Pages <https://help.github.com/categories/20/articles>`_ offer an easy
and convenient way to publish Pelican sites. There are `two types of GitHub
Pages <https://help.github.com/articles/user-organization-and-project-pages>`_:
*Project Pages* and *User Pages*. Pelican sites can be published as both
Project Pages and User Pages.
Project Pages
-------------
For creating Project pages, a branch called ``gh-pages`` is used for publishing.
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_ makes this
really easy, which can be installed via::
$ pip install ghp-import
To publish a Pelican site as Project Pages you need to *push* the content of
the ``output`` dir generated by Pelican to a repository's ``gh-pages`` branch
on GitHub.
Then, given a repository containing your articles, you would simply run
Pelican and upload the output to GitHub::
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_, which can
be installed with ``easy_install`` or ``pip``, makes this process really easy.
$ pelican -s pelican.conf.py .
For example, if the sources of your Pelican site are contained in a GitHub
repository, and if you want to publish your Pelican site as Project Pages of
this repository, you can then use the following::
$ pelican content -o output pelicanconf.py
$ ghp-import output
$ git push origin gh-pages
And that's it.
The ``ghp-import output`` command updates the local ``gh-pages`` branch with
the content of the ``output`` directory (creating the branch if it doesn't
already exist). The ``git push origin gh-pages`` command updates the remote
``gh-pages`` branch, effectively publishing the Pelican site.
If you want, you can put that directly into a post-commit hook, so each time you
commit, your blog is up-to-date on GitHub!
.. note::
Put the following into ``.git/hooks/post-commit``::
The ``github`` target of the Makefile created by the ``pelican-quickstart``
command publishes the Pelican site as Project Pages as described above.
pelican -s pelican.conf.py . && ghp-import output && git push origin gh-pages
User Pages
----------
To publish a Pelican site as User Pages you need to *push* the content of the
``output`` dir generated by Pelican to the ``master`` branch of your
``<username>.github.com`` repository on GitHub.
Again, you can take advantage of ``ghp-import``::
$ pelican content -o output pelicanconf.py
$ ghp-import output
$ git push git@github.com:elemoine/elemoine.github.com.git gh-pages:master
The ``git push`` command pushes the local ``gh-pages`` branch (freshly updated
by the ``ghp-import`` command) to the ``elemoine.github.com`` repository's
``master`` branch on GitHub.
.. note::
To publish your Pelican site as User Pages feel free to adjust the the
``github`` target of the Makefile.
Extra Tips
----------
Tip #1:
To automatically update your Pelican site on each commit you can create
a post-commit hook. For example, you can add the following to
``.git/hooks/post-commit``::
pelican pelican content -o output pelicanconf.py && ghp-import output && git push origin gh-pages
Tip #2:
To use a `custom domain
<https://help.github.com/articles/setting-up-a-custom-domain-with-pages>`_ with
GitHub Pages you need to have a ``CNAME`` file at the root of your pages. For
that you will add ``CNAME`` file to your ``content``, dir and use the
``FILES_TO_COPY`` setting variable to tell Pelican to copy that file
to the ``output`` dir. For example::
FILES_TO_COPY = (('extra/CNAME', 'CNAME'),)

View file

@ -1,9 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import six
import os
import re
import sys
import time
import logging
import argparse
import locale
from pelican import signals
@ -12,8 +17,7 @@ from pelican.generators import (ArticlesGenerator, PagesGenerator,
SourceFileGenerator, TemplatePagesGenerator)
from pelican.log import init
from pelican.settings import read_settings
from pelican.utils import (clean_output_dir, files_changed, file_changed,
NoFilesError)
from pelican.utils import clean_output_dir, folder_watcher, file_watcher
from pelican.writers import Writer
__major__ = 3
@ -21,6 +25,8 @@ __minor__ = 2
__micro__ = 0
__version__ = "{0}.{1}.{2}".format(__major__, __minor__, __micro__)
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
logger = logging.getLogger(__name__)
@ -40,6 +46,7 @@ class Pelican(object):
self.theme = settings['THEME']
self.output_path = settings['OUTPUT_PATH']
self.markup = settings['MARKUP']
self.ignore_files = settings['IGNORE_FILES']
self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
self.init_path()
@ -47,20 +54,30 @@ class Pelican(object):
signals.initialized.send(self)
def init_path(self):
if not any(p in sys.path for p in ['', '.']):
if not any(p in sys.path for p in ['', os.curdir]):
logger.debug("Adding current directory to system path")
sys.path.insert(0, '')
def init_plugins(self):
self.plugins = self.settings['PLUGINS']
for plugin in self.plugins:
self.plugins = []
logger.debug('Temporarily adding PLUGIN_PATH to system path')
_sys_path = sys.path[:]
sys.path.insert(0, self.settings['PLUGIN_PATH'])
for plugin in self.settings['PLUGINS']:
# if it's a string, then import it
if isinstance(plugin, basestring):
logger.debug("Loading plugin `{0}' ...".format(plugin))
plugin = __import__(plugin, globals(), locals(), 'module')
if isinstance(plugin, six.string_types):
logger.debug("Loading plugin `{0}`".format(plugin))
try:
plugin = __import__(plugin, globals(), locals(), str('module'))
except ImportError as e:
logger.error("Can't find plugin `{0}`: {1}".format(plugin, e))
continue
logger.debug("Registering plugin `{0}'".format(plugin.__name__))
logger.debug("Registering plugin `{0}`".format(plugin.__name__))
plugin.register()
self.plugins.append(plugin)
logger.debug('Restoring system path')
sys.path = _sys_path
def _handle_deprecation(self):
@ -133,10 +150,11 @@ class Pelican(object):
def run(self):
"""Run the generators and return"""
start_time = time.time()
context = self.settings.copy()
context['filenames'] = {} # share the dict between all the generators
context['localsiteurl'] = self.settings.get('SITEURL') # share
context['localsiteurl'] = self.settings['SITEURL'] # share
generators = [
cls(
context,
@ -166,6 +184,14 @@ class Pelican(object):
signals.finalized.send(self)
articles_generator = next(g for g in generators if isinstance(g, ArticlesGenerator))
pages_generator = next(g for g in generators if isinstance(g, PagesGenerator))
print('Done: Processed {} articles and {} pages in {:.2f} seconds.'.format(
len(articles_generator.articles) + len(articles_generator.translations),
len(pages_generator.pages) + len(pages_generator.translations),
time.time() - start_time))
def get_generator_classes(self):
generators = [StaticGenerator, ArticlesGenerator, PagesGenerator]
@ -215,11 +241,14 @@ def parse_arguments():
'them separated by commas.')
parser.add_argument('-s', '--settings', dest='settings',
help='The settings of the application.')
help='The settings of the application, this is automatically set to '
'{0} if a file exists with this name.'.format(DEFAULT_CONFIG_NAME))
parser.add_argument('-d', '--delete-output-directory',
dest='delete_outputdir',
action='store_true', help='Delete the output directory.')
action='store_true',
default=None,
help='Delete the output directory.')
parser.add_argument('-v', '--verbose', action='store_const',
const=logging.INFO, dest='verbosity',
@ -257,15 +286,31 @@ def get_config(args):
config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme
if args.delete_outputdir is not None:
config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
# argparse returns bytes in Py2. There is no definite answer as to which
# encoding argparse (or sys.argv) uses.
# "Best" option seems to be locale.getpreferredencoding()
# ref: http://mail.python.org/pipermail/python-list/2006-October/405766.html
if not six.PY3:
enc = locale.getpreferredencoding()
for key in config:
if key in ('PATH', 'OUTPUT_PATH', 'THEME'):
config[key] = config[key].decode(enc)
if key == "MARKUP":
config[key] = [a.decode(enc) for a in config[key]]
return config
def get_instance(args):
settings = read_settings(args.settings, override=get_config(args))
config_file = args.settings
if config_file is None and os.path.isfile(DEFAULT_CONFIG_NAME):
config_file = DEFAULT_CONFIG_NAME
cls = settings.get('PELICAN_CLASS')
if isinstance(cls, basestring):
settings = read_settings(config_file, override=get_config(args))
cls = settings['PELICAN_CLASS']
if isinstance(cls, six.string_types):
module, cls_name = cls.rsplit('.', 1)
module = __import__(module)
cls = getattr(module, cls_name)
@ -278,9 +323,19 @@ def main():
init(args.verbosity)
pelican = get_instance(args)
watchers = {'content': folder_watcher(pelican.path,
pelican.markup,
pelican.ignore_files),
'theme': folder_watcher(pelican.theme,
[''],
pelican.ignore_files),
'settings': file_watcher(args.settings)}
try:
if args.autoreload:
files_found_error = True
print(' --- AutoReload Mode: Monitoring `content`, `theme` and `settings`'
' for changes. ---')
while True:
try:
# Check source dir for changed files ending with the given
@ -288,38 +343,54 @@ def main():
# restriction; all files are recursively checked if they
# have changed, no matter what extension the filenames
# have.
if files_changed(pelican.path, pelican.markup) or \
files_changed(pelican.theme, ['']):
if not files_found_error:
files_found_error = True
pelican.run()
modified = {k: next(v) for k, v in watchers.items()}
# reload also if settings.py changed
if file_changed(args.settings):
logger.info('%s changed, re-generating' %
args.settings)
if modified['settings']:
pelican = get_instance(args)
if any(modified.values()):
print('\n-> Modified: {}. re-generating...'.format(
', '.join(k for k, v in modified.items() if v)))
if modified['content'] is None:
logger.warning('No valid files found in content.')
if modified['theme'] is None:
logger.warning('Empty theme folder. Using `basic` theme.')
pelican.run()
time.sleep(.5) # sleep to avoid cpu load
except KeyboardInterrupt:
logger.warning("Keyboard interrupt, quitting.")
break
except NoFilesError:
if files_found_error:
logger.warning("No valid files found in content. "
"Nothing to generate.")
files_found_error = False
time.sleep(1) # sleep to avoid cpu load
except Exception, e:
except Exception as e:
if (args.verbosity == logging.DEBUG):
logger.critical(e.args)
raise
logger.warning(
"Caught exception \"{}\". Reloading.".format(e)
)
continue
'Caught exception "{0}". Reloading.'.format(e))
finally:
time.sleep(.5) # sleep to avoid cpu load
else:
if next(watchers['content']) is None:
logger.warning('No valid files found in content.')
if next(watchers['theme']) is None:
logger.warning('Empty theme folder. Using `basic` theme.')
pelican.run()
except Exception, e:
logger.critical(unicode(e))
except Exception as e:
# localized systems have errors in native language if locale is set
# so convert the message to unicode with the correct encoding
msg = str(e)
if not six.PY3:
msg = msg.decode(locale.getpreferredencoding(False))
logger.critical(msg)
if (args.verbosity == logging.DEBUG):
raise

View file

@ -1,49 +1,63 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import six
import copy
import locale
import logging
import functools
import os
import re
import sys
from datetime import datetime
from sys import platform, stdin
from pelican.settings import _DEFAULT_CONFIG
from pelican.utils import slugify, truncate_html_words, memoized
from pelican import signals
from pelican.settings import DEFAULT_CONFIG
from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
python_2_unicode_compatible, deprecated_attribute,
path_to_url)
# Import these so that they're avalaible when you import from pelican.contents.
from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
logger = logging.getLogger(__name__)
class Page(object):
"""Represents a page
Given a content, and metadata, create an adequate object.
class Content(object):
"""Represents a content.
:param content: the string to parse, containing the original content.
:param metadata: the metadata associated to this page (optional).
:param settings: the settings dictionary (optional).
:param source_path: The location of the source of this content (if any).
:param context: The shared context between generators.
"""
mandatory_properties = ('title',)
default_template = 'page'
@deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))
def filename():
return None
def __init__(self, content, metadata=None, settings=None,
filename=None, context=None):
# init parameters
if not metadata:
source_path=None, context=None):
if metadata is None:
metadata = {}
if not settings:
settings = copy.deepcopy(_DEFAULT_CONFIG)
if settings is None:
settings = copy.deepcopy(DEFAULT_CONFIG)
self.settings = settings
self._content = content
self._context = context
self.translations = []
local_metadata = dict(settings.get('DEFAULT_METADATA', ()))
local_metadata = dict(settings['DEFAULT_METADATA'])
local_metadata.update(metadata)
# set metadata as attributes
for key, value in local_metadata.items():
if key in ('save_as', 'url'):
key = 'override_' + key
setattr(self, key.lower(), value)
# also keep track of the metadata attributes available
@ -57,6 +71,8 @@ class Page(object):
if 'AUTHOR' in settings:
self.author = Author(settings['AUTHOR'], settings)
# XXX Split all the following code into pieces, there is too much here.
# manage languages
self.in_default_lang = True
if 'DEFAULT_LANG' in settings:
@ -70,8 +86,7 @@ class Page(object):
if not hasattr(self, 'slug') and hasattr(self, 'title'):
self.slug = slugify(self.title)
if filename:
self.filename = filename
self.source_path = source_path
# manage the date format
if not hasattr(self, 'date_format'):
@ -81,17 +96,15 @@ class Page(object):
self.date_format = settings['DEFAULT_DATE_FORMAT']
if isinstance(self.date_format, tuple):
locale.setlocale(locale.LC_ALL, self.date_format[0])
locale_string = self.date_format[0]
if sys.version_info < (3, ) and isinstance(locale_string,
six.text_type):
locale_string = locale_string.encode('ascii')
locale.setlocale(locale.LC_ALL, locale_string)
self.date_format = self.date_format[1]
if hasattr(self, 'date'):
encoded_date = self.date.strftime(
self.date_format.encode('ascii', 'xmlcharrefreplace'))
if platform == 'win32':
self.locale_date = encoded_date.decode(stdin.encoding)
else:
self.locale_date = encoded_date.decode('utf')
self.locale_date = strftime(self.date, self.date_format)
# manage status
if not hasattr(self, 'status'):
@ -104,40 +117,57 @@ class Page(object):
if 'summary' in metadata:
self._summary = metadata['summary']
signals.content_object_init.send(self.__class__, instance=self)
signals.content_object_init.send(self)
def __str__(self):
if self.source_path is None:
return repr(self)
elif six.PY3:
return self.source_path or repr(self)
else:
return str(self.source_path.encode('utf-8', 'replace'))
def check_properties(self):
"""test that each mandatory property is set."""
"""Test mandatory properties are set."""
for prop in self.mandatory_properties:
if not hasattr(self, prop):
raise NameError(prop)
@property
def url_format(self):
return {
"""Returns the URL, formatted with the proper values"""
metadata = copy.copy(self.metadata)
path = self.metadata.get('path', self.get_relative_source_path())
metadata.update({
'path': path_to_url(path),
'slug': getattr(self, 'slug', ''),
'lang': getattr(self, 'lang', 'en'),
'date': getattr(self, 'date', datetime.now()),
'author': getattr(self, 'author', ''),
'category': getattr(self, 'category',
self.settings['DEFAULT_CATEGORY']),
}
self.settings['DEFAULT_CATEGORY']),
})
return metadata
def _expand_settings(self, key):
fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper()
return self.settings[fq_key].format(**self.url_format)
def get_url_setting(self, key):
if hasattr(self, 'override_' + key):
return getattr(self, 'override_' + key)
key = key if self.in_default_lang else 'lang_%s' % key
return self._expand_settings(key)
def _update_content(self, content, siteurl):
"""Change all the relative paths of the content to relative paths
"""Update the content attribute.
Change all the relative paths of the content to relative paths
suitable for the ouput content.
:param content: content resource that will be passed to the templates.
:param siteurl: siteurl which is locally generated by the writer in
case of RELATIVE_URLS.
case of RELATIVE_URLS.
"""
hrefs = re.compile(r"""
(?P<markup><\s*[^\>]* # match tag with src and href attr
@ -151,59 +181,68 @@ class Page(object):
what = m.group('what')
value = m.group('value')
origin = m.group('path')
# we support only filename for now. the plan is to support
# categories, tags, etc. in the future, but let's keep things
# simple for now.
# XXX Put this in a different location.
if what == 'filename':
if value.startswith('/'):
value = value[1:]
else:
# relative to the filename of this content
value = self.get_relative_filename(
# relative to the source path of this content
value = self.get_relative_source_path(
os.path.join(self.relative_dir, value)
)
if value in self._context['filenames']:
origin = '/'.join((siteurl,
self._context['filenames'][value].url))
origin = origin.replace('\\', '/') # Fow windows paths.
else:
logger.warning(u"Unable to find {fn}, skipping url"
" replacement".format(fn=value))
logger.warning("Unable to find {fn}, skipping url"
" replacement".format(fn=value))
return m.group('markup') + m.group('quote') + origin \
+ m.group('quote')
return ''.join((m.group('markup'), m.group('quote'), origin,
m.group('quote')))
return hrefs.sub(replacer, content)
@memoized
def get_content(self, siteurl):
return self._update_content(
self._get_content() if hasattr(self, "_get_content")
else self._content,
siteurl)
if hasattr(self, '_get_content'):
content = self._get_content()
else:
content = self._content
return self._update_content(content, siteurl)
@property
def content(self):
return self.get_content(self._context['localsiteurl'])
def _get_summary(self):
"""Returns the summary of an article, based on the summary metadata
if it is set, else truncate the content."""
"""Returns the summary of an article.
This is based on the summary metadata if set, otherwise truncate the
content.
"""
if hasattr(self, '_summary'):
return self._summary
else:
if self.settings['SUMMARY_MAX_LENGTH']:
return truncate_html_words(self.content,
self.settings['SUMMARY_MAX_LENGTH'])
if self.settings['SUMMARY_MAX_LENGTH'] is None:
return self.content
return truncate_html_words(self.content,
self.settings['SUMMARY_MAX_LENGTH'])
def _set_summary(self, summary):
"""Dummy function"""
pass
summary = property(_get_summary, _set_summary, "Summary of the article."
"Based on the content. Can't be set")
url = property(functools.partial(get_url_setting, key='url'))
save_as = property(functools.partial(get_url_setting, key='save_as'))
@ -213,28 +252,36 @@ class Page(object):
else:
return self.default_template
def get_relative_filename(self, filename=None):
def get_relative_source_path(self, source_path=None):
"""Return the relative path (from the content path) to the given
filename.
source_path.
If no filename is specified, use the filename of this content object.
If no source path is specified, use the source path of this
content object.
"""
if not filename:
filename = self.filename
if not source_path:
source_path = self.source_path
if source_path is None:
return None
return os.path.relpath(
os.path.abspath(os.path.join(self.settings['PATH'], filename)),
os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
os.path.abspath(self.settings['PATH'])
)
@property
def relative_dir(self):
return os.path.dirname(os.path.relpath(
os.path.abspath(self.filename),
os.path.abspath(self.source_path),
os.path.abspath(self.settings['PATH']))
)
class Page(Content):
mandatory_properties = ('title',)
default_template = 'page'
class Article(Page):
mandatory_properties = ('title', 'date', 'category')
default_template = 'article'
@ -244,82 +291,26 @@ class Quote(Page):
base_properties = ('author', 'date')
class URLWrapper(object):
def __init__(self, name, settings):
self.name = unicode(name)
self.slug = slugify(self.name)
self.settings = settings
@python_2_unicode_compatible
class Static(Page):
@deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))
def filepath():
return None
def as_dict(self):
return self.__dict__
@deprecated_attribute(old='src', new='source_path', since=(3, 2, 0))
def src():
return None
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return self.name == unicode(other)
def __str__(self):
return str(self.name.encode('utf-8', 'replace'))
def __unicode__(self):
return self.name
def _from_settings(self, key, get_page_name=False):
"""Returns URL information as defined in settings.
When get_page_name=True returns URL without anything after {slug}
e.g. if in settings: CATEGORY_URL="cat/{slug}.html" this returns "cat/{slug}"
Useful for pagination."""
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
value = self.settings[setting]
if not isinstance(value, basestring):
logger.warning(u'%s is set to %s' % (setting, value))
return value
else:
if get_page_name:
return unicode(os.path.splitext(value)[0]).format(**self.as_dict())
else:
return unicode(value).format(**self.as_dict())
page_name = property(functools.partial(_from_settings, key='URL', get_page_name=True))
url = property(functools.partial(_from_settings, key='URL'))
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
class Category(URLWrapper):
pass
class Tag(URLWrapper):
def __init__(self, name, *args, **kwargs):
super(Tag, self).__init__(unicode.strip(name), *args, **kwargs)
class Author(URLWrapper):
pass
class StaticContent(object):
def __init__(self, src, dst=None, settings=None):
if not settings:
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.src = src
self.url = dst or src
self.filepath = os.path.join(settings['PATH'], src)
self.save_as = os.path.join(settings['OUTPUT_PATH'], self.url)
def __str__(self):
return str(self.filepath.encode('utf-8', 'replace'))
def __unicode__(self):
return self.filepath
@deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0))
def dst():
return None
def is_valid_content(content, f):
try:
content.check_properties()
return True
except NameError, e:
logger.error(u"Skipping %s: impossible to find informations about"
"'%s'" % (f, e))
except NameError as e:
logger.error("Skipping %s: could not find information about "
"'%s'" % (f, e))
return False

View file

@ -1,26 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import os
import math
import random
import logging
import datetime
import subprocess
import shutil
from codecs import open
from collections import defaultdict
from functools import partial
from itertools import chain
from itertools import chain, groupby
from operator import attrgetter, itemgetter
from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader,
BaseLoader, TemplateNotFound)
from jinja2 import (
Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, BaseLoader,
TemplateNotFound
)
from pelican.contents import Article, Page, Category, StaticContent, \
is_valid_content
from pelican.contents import (
Article, Page, Category, Static, is_valid_content
)
from pelican.readers import read_file
from pelican.utils import copy, process_translations, mkdir_p
from pelican.utils import copy, process_translations, mkdir_p, DateFormatter
from pelican import signals
import pelican.utils
logger = logging.getLogger(__name__)
@ -42,7 +47,7 @@ class Generator(object):
self._templates_path = []
self._templates_path.append(os.path.expanduser(
os.path.join(self.theme, 'templates')))
self._templates_path += self.settings.get('EXTRA_TEMPLATES_PATHS', [])
self._templates_path += self.settings['EXTRA_TEMPLATES_PATHS']
theme_path = os.path.dirname(os.path.abspath(__file__))
@ -55,13 +60,16 @@ class Generator(object):
simple_loader, # implicit inheritance
PrefixLoader({'!simple': simple_loader}) # explicit one
]),
extensions=self.settings.get('JINJA_EXTENSIONS', []),
extensions=self.settings['JINJA_EXTENSIONS'],
)
logger.debug('template list: {0}'.format(self.env.list_templates()))
# provide utils.strftime as a jinja filter
self.env.filters.update({'strftime': DateFormatter()})
# get custom Jinja filters from user settings
custom_filters = self.settings.get('JINJA_FILTERS', {})
custom_filters = self.settings['JINJA_FILTERS']
self.env.filters.update(custom_filters)
signals.generator_init.send(self)
@ -75,10 +83,25 @@ class Generator(object):
try:
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))
raise Exception(
('[templates] unable to load %s.html from %s'
% (name, self._templates_path)))
return self._templates[name]
def _include_path(self, path, extensions=None):
"""Inclusion logic for .get_files(), returns True/False
:param path: the path which might be including
:param extensions: the list of allowed extensions (if False, all
extensions are allowed)
"""
if extensions is None:
extensions = self.markup
basename = os.path.basename(path)
if extensions is False or basename.endswith(extensions):
return True
return False
def get_files(self, path, exclude=[], extensions=None):
"""Return a list of files to use, based on rules
@ -87,29 +110,23 @@ class Generator(object):
:param extensions: the list of allowed extensions (if False, all
extensions are allowed)
"""
if extensions is None:
extensions = self.markup
files = []
try:
iter = os.walk(path, followlinks=True)
except TypeError: # python 2.5 does not support followlinks
iter = os.walk(path)
for root, dirs, temp_files in iter:
for e in exclude:
if e in dirs:
dirs.remove(e)
for f in temp_files:
if extensions is False or \
(True in [f.endswith(ext) for ext in extensions]):
files.append(os.sep.join((root, f)))
if os.path.isdir(path):
for root, dirs, temp_files in os.walk(path, followlinks=True):
for e in exclude:
if e in dirs:
dirs.remove(e)
for f in temp_files:
fp = os.path.join(root, f)
if self._include_path(fp, extensions):
files.append(fp)
elif os.path.exists(path) and self._include_path(path, extensions):
files.append(path) # can't walk non-directories
return files
def add_filename(self, content):
location = os.path.relpath(os.path.abspath(content.filename),
os.path.abspath(self.path))
def add_source_path(self, content):
location = content.get_relative_source_path()
self.context['filenames'][location] = content
def _update_context(self, items):
@ -119,7 +136,7 @@ class Generator(object):
for item in items:
value = getattr(self, item)
if hasattr(value, 'items'):
value = value.items()
value = list(value.items()) # py3k safeguard for iterators
self.context[item] = value
@ -133,10 +150,10 @@ class _FileLoader(BaseLoader):
if template != self.path or not os.path.exists(self.fullpath):
raise TemplateNotFound(template)
mtime = os.path.getmtime(self.fullpath)
with file(self.fullpath) as f:
source = f.read().decode('utf-8')
return source, self.fullpath, \
lambda: mtime == os.path.getmtime(self.fullpath)
with open(self.fullpath, 'r', encoding='utf-8') as f:
source = f.read()
return (source, self.fullpath,
lambda: mtime == os.path.getmtime(self.fullpath))
class TemplatePagesGenerator(Generator):
@ -146,7 +163,7 @@ class TemplatePagesGenerator(Generator):
self.env.loader.loaders.insert(0, _FileLoader(source, self.path))
try:
template = self.env.get_template(source)
rurls = self.settings.get('RELATIVE_URLS')
rurls = self.settings['RELATIVE_URLS']
writer.write_file(dest, template, self.context, rurls)
finally:
del self.env.loader.loaders[0]
@ -164,8 +181,8 @@ class ArticlesGenerator(Generator):
self.categories = defaultdict(list)
self.related_posts = []
self.authors = defaultdict(list)
super(ArticlesGenerator, self).__init__(*args, **kwargs)
self.drafts = []
super(ArticlesGenerator, self).__init__(*args, **kwargs)
signals.article_generator_init.send(self)
def generate_feeds(self, writer):
@ -179,8 +196,8 @@ class ArticlesGenerator(Generator):
writer.write_feed(self.articles, self.context,
self.settings['FEED_RSS'], feed_type='rss')
if self.settings.get('FEED_ALL_ATOM') or \
self.settings.get('FEED_ALL_RSS'):
if (self.settings.get('FEED_ALL_ATOM')
or self.settings.get('FEED_ALL_RSS')):
all_articles = list(self.articles)
for article in self.articles:
all_articles.extend(article.translations)
@ -192,34 +209,37 @@ class ArticlesGenerator(Generator):
if self.settings.get('FEED_ALL_RSS'):
writer.write_feed(all_articles, self.context,
self.settings['FEED_ALL_RSS'], feed_type='rss')
self.settings['FEED_ALL_RSS'],
feed_type='rss')
for cat, arts in self.categories:
arts.sort(key=attrgetter('date'), reverse=True)
if self.settings.get('CATEGORY_FEED_ATOM'):
writer.write_feed(arts, self.context,
self.settings['CATEGORY_FEED_ATOM'] % cat)
self.settings['CATEGORY_FEED_ATOM']
% cat.slug)
if self.settings.get('CATEGORY_FEED_RSS'):
writer.write_feed(arts, self.context,
self.settings['CATEGORY_FEED_RSS'] % cat,
feed_type='rss')
self.settings['CATEGORY_FEED_RSS']
% cat.slug, feed_type='rss')
if self.settings.get('TAG_FEED_ATOM') \
or self.settings.get('TAG_FEED_RSS'):
if (self.settings.get('TAG_FEED_ATOM')
or self.settings.get('TAG_FEED_RSS')):
for tag, arts in self.tags.items():
arts.sort(key=attrgetter('date'), reverse=True)
if self.settings.get('TAG_FEED_ATOM'):
writer.write_feed(arts, self.context,
self.settings['TAG_FEED_ATOM'] % tag)
self.settings['TAG_FEED_ATOM']
% tag.slug)
if self.settings.get('TAG_FEED_RSS'):
writer.write_feed(arts, self.context,
self.settings['TAG_FEED_RSS'] % tag,
self.settings['TAG_FEED_RSS'] % tag.slug,
feed_type='rss')
if self.settings.get('TRANSLATION_FEED_ATOM') or \
self.settings.get('TRANSLATION_FEED_RSS'):
if (self.settings.get('TRANSLATION_FEED_ATOM')
or self.settings.get('TRANSLATION_FEED_RSS')):
translations_feeds = defaultdict(list)
for article in chain(self.articles, self.translations):
translations_feeds[article.lang].append(article)
@ -240,10 +260,50 @@ class ArticlesGenerator(Generator):
write(article.save_as, self.get_template(article.template),
self.context, article=article, category=article.category)
def generate_period_archives(self, write):
"""Generate per-year, per-month, and per-day archives."""
try:
template = self.get_template('period_archives')
except Exception:
template = self.get_template('archives')
def _generate_period_archives(dates, key, save_as_fmt):
"""Generate period archives from `dates`, grouped by
`key` and written to `save_as`.
"""
# `dates` is already sorted by date
for _period, group in groupby(dates, key=key):
archive = list(group)
# arbitrarily grab the first date so that the usual
# format string syntax can be used for specifying the
# period archive dates
date = archive[0].date
save_as = save_as_fmt.format(date=date)
write(save_as, template, self.context,
dates=archive, blog=True)
period_save_as = {
'year' : self.settings['YEAR_ARCHIVE_SAVE_AS'],
'month': self.settings['MONTH_ARCHIVE_SAVE_AS'],
'day' : self.settings['DAY_ARCHIVE_SAVE_AS'],
}
period_date_key = {
'year' : attrgetter('date.year'),
'month': attrgetter('date.year', 'date.month'),
'day' : attrgetter('date.year', 'date.month', 'date.day')
}
for period in 'year', 'month', 'day':
save_as = period_save_as[period]
if save_as:
key = period_date_key[period]
_generate_period_archives(self.dates, key, save_as)
def generate_direct_templates(self, write):
"""Generate direct templates pages"""
PAGINATED_TEMPLATES = self.settings.get('PAGINATED_DIRECT_TEMPLATES')
for template in self.settings.get('DIRECT_TEMPLATES'):
PAGINATED_TEMPLATES = self.settings['PAGINATED_DIRECT_TEMPLATES']
for template in self.settings['DIRECT_TEMPLATES']:
paginated = {}
if template in PAGINATED_TEMPLATES:
paginated = {'articles': self.articles, 'dates': self.dates}
@ -253,8 +313,8 @@ class ArticlesGenerator(Generator):
continue
write(save_as, self.get_template(template),
self.context, blog=True, paginated=paginated,
page_name=template)
self.context, blog=True, paginated=paginated,
page_name=os.path.splitext(save_as)[0])
def generate_tags(self, write):
"""Generate Tags pages."""
@ -265,7 +325,7 @@ class ArticlesGenerator(Generator):
write(tag.save_as, tag_template, self.context, tag=tag,
articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name=tag.page_name)
page_name=tag.page_name, all_articles=self.articles)
def generate_categories(self, write):
"""Generate category pages."""
@ -275,7 +335,7 @@ class ArticlesGenerator(Generator):
write(cat.save_as, category_template, self.context,
category=cat, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name=cat.page_name)
page_name=cat.page_name, all_articles=self.articles)
def generate_authors(self, write):
"""Generate Author pages."""
@ -285,23 +345,25 @@ class ArticlesGenerator(Generator):
write(aut.save_as, author_template, self.context,
author=aut, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name=aut.page_name)
page_name=aut.page_name, all_articles=self.articles)
def generate_drafts(self, write):
"""Generate drafts pages."""
for article in self.drafts:
write('drafts/%s.html' % article.slug,
write(os.path.join('drafts', '%s.html' % article.slug),
self.get_template(article.template), self.context,
article=article, category=article.category)
article=article, category=article.category,
all_articles=self.articles)
def generate_pages(self, writer):
"""Generate the pages on the disk"""
write = partial(writer.write_file,
relative_urls=self.settings.get('RELATIVE_URLS'))
relative_urls=self.settings['RELATIVE_URLS'])
# to minimize the number of relative path stuff modification
# in writer, articles pass first
self.generate_articles(write)
self.generate_period_archives(write)
self.generate_direct_templates(write)
# and subfolders after that
@ -323,8 +385,8 @@ class ArticlesGenerator(Generator):
try:
signals.article_generate_preread.send(self)
content, metadata = read_file(f, settings=self.settings)
except Exception, e:
logger.warning(u'Could not process %s\n%s' % (f, str(e)))
except Exception as e:
logger.warning('Could not process %s\n%s' % (f, str(e)))
continue
# if no category is set, use the name of the path as a category
@ -333,8 +395,7 @@ class ArticlesGenerator(Generator):
if (self.settings['USE_FOLDER_AS_CATEGORY']
and os.path.dirname(f) != article_path):
# if the article is in a subdirectory
category = os.path.basename(os.path.dirname(f))\
.decode('utf-8')
category = os.path.basename(os.path.dirname(f))
else:
# if the article is not in a subdirectory
category = self.settings['DEFAULT_CATEGORY']
@ -352,33 +413,35 @@ class ArticlesGenerator(Generator):
signals.article_generate_context.send(self, metadata=metadata)
article = Article(content, metadata, settings=self.settings,
filename=f, context=self.context)
source_path=f, context=self.context)
if not is_valid_content(article, f):
continue
self.add_filename(article)
self.add_source_path(article)
if article.status == "published":
if hasattr(article, 'tags'):
for tag in article.tags:
self.tags[tag].append(article)
all_articles.append(article)
elif article.status == "draft":
self.drafts.append(article)
else:
logger.warning(u"Unknown status %s for file %s, skipping it." %
(repr(unicode.encode(article.status, 'utf-8')),
logger.warning("Unknown status %s for file %s, skipping it." %
(repr(article.status),
repr(f)))
self.articles, self.translations = process_translations(all_articles)
for article in self.articles:
# only main articles are listed in categories, not translations
# only main articles are listed in categories and tags
# not translations
self.categories[article.category].append(article)
if hasattr(article, 'tags'):
for tag in article.tags:
self.tags[tag].append(article)
# ignore blank authors as well as undefined
if hasattr(article,'author') and article.author.name != '':
if hasattr(article, 'author') and article.author.name != '':
self.authors[article.author].append(article)
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
self.dates = list(self.articles)
@ -394,7 +457,7 @@ class ArticlesGenerator(Generator):
tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True)
tag_cloud = tag_cloud[:self.settings.get('TAG_CLOUD_MAX_ITEMS')]
tags = map(itemgetter(1), tag_cloud)
tags = list(map(itemgetter(1), tag_cloud))
if tags:
max_count = max(tags)
steps = self.settings.get('TAG_CLOUD_STEPS')
@ -416,11 +479,10 @@ class ArticlesGenerator(Generator):
# order the categories per name
self.categories = list(self.categories.items())
self.categories.sort(
key=lambda item: item[0].name,
reverse=self.settings['REVERSE_CATEGORY_ORDER'])
self.authors = list(self.authors.items())
self.authors.sort(key=lambda item: item[0].name)
self.authors.sort()
self._update_context(('articles', 'dates', 'tags', 'categories',
'tag_cloud', 'authors', 'related_posts'))
@ -450,38 +512,41 @@ class PagesGenerator(Generator):
exclude=self.settings['PAGE_EXCLUDES']):
try:
content, metadata = read_file(f, settings=self.settings)
except Exception, e:
logger.warning(u'Could not process %s\n%s' % (f, str(e)))
except Exception as e:
logger.warning('Could not process %s\n%s' % (f, str(e)))
continue
signals.pages_generate_context.send(self, metadata=metadata)
page = Page(content, metadata, settings=self.settings,
filename=f, context=self.context)
source_path=f, context=self.context)
if not is_valid_content(page, f):
continue
self.add_filename(page)
self.add_source_path(page)
if page.status == "published":
all_pages.append(page)
elif page.status == "hidden":
hidden_pages.append(page)
else:
logger.warning(u"Unknown status %s for file %s, skipping it." %
(repr(unicode.encode(page.status, 'utf-8')),
logger.warning("Unknown status %s for file %s, skipping it." %
(repr(page.status),
repr(f)))
self.pages, self.translations = process_translations(all_pages)
self.hidden_pages, self.hidden_translations = process_translations(hidden_pages)
self.hidden_pages, self.hidden_translations = (
process_translations(hidden_pages))
self._update_context(('pages', ))
self.context['PAGES'] = self.pages
signals.pages_generator_finalized.send(self)
def generate_output(self, writer):
for page in chain(self.translations, self.pages,
self.hidden_translations, self.hidden_pages):
writer.write_file(page.save_as, self.get_template(page.template),
self.context, page=page,
relative_urls=self.settings.get('RELATIVE_URLS'))
relative_urls=self.settings['RELATIVE_URLS'])
class StaticGenerator(Generator):
@ -503,25 +568,42 @@ class StaticGenerator(Generator):
for f in self.get_files(
os.path.join(self.path, static_path), extensions=False):
f_rel = os.path.relpath(f, self.path)
content, metadata = read_file(
f, fmt='static', settings=self.settings)
# TODO remove this hardcoded 'static' subdirectory
sc = StaticContent(f_rel, os.path.join('static', f_rel),
settings=self.settings)
metadata['save_as'] = os.path.join('static', f_rel)
metadata['url'] = pelican.utils.path_to_url(metadata['save_as'])
sc = Static(
content=None,
metadata=metadata,
settings=self.settings,
source_path=f_rel)
self.staticfiles.append(sc)
self.context['filenames'][f_rel] = sc
self.add_source_path(sc)
# same thing for FILES_TO_COPY
for src, dest in self.settings['FILES_TO_COPY']:
sc = StaticContent(src, dest, settings=self.settings)
content, metadata = read_file(
src, fmt='static', settings=self.settings)
metadata['save_as'] = dest
metadata['url'] = pelican.utils.path_to_url(metadata['save_as'])
sc = Static(
content=None,
metadata={'save_as': dest},
settings=self.settings,
source_path=src)
self.staticfiles.append(sc)
self.context['filenames'][src] = sc
self.add_source_path(sc)
def generate_output(self, writer):
self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
'theme', self.output_path, '.')
# copy all StaticContent files
'theme', self.output_path, os.curdir)
# copy all Static files
for sc in self.staticfiles:
mkdir_p(os.path.dirname(sc.save_as))
shutil.copy(sc.filepath, sc.save_as)
logger.info('copying %s to %s' % (sc.filepath, sc.save_as))
source_path = os.path.join(self.path, sc.source_path)
save_as = os.path.join(self.output_path, sc.save_as)
mkdir_p(os.path.dirname(save_as))
shutil.copy(source_path, save_as)
logger.info('copying {} to {}'.format(sc.source_path, sc.save_as))
class PdfGenerator(Generator):
@ -531,12 +613,8 @@ class PdfGenerator(Generator):
super(PdfGenerator, self).__init__(*args, **kwargs)
try:
from rst2pdf.createpdf import RstToPdf
pdf_style_path = os.path.join(self.settings['PDF_STYLE_PATH']) \
if 'PDF_STYLE_PATH' in self.settings.keys() \
else ''
pdf_style = self.settings['PDF_STYLE'] if 'PDF_STYLE' \
in self.settings.keys() \
else 'twelvepoint'
pdf_style_path = os.path.join(self.settings['PDF_STYLE_PATH'])
pdf_style = self.settings['PDF_STYLE']
self.pdfcreator = RstToPdf(breakside=0,
stylesheets=[pdf_style],
style_path=[pdf_style_path])
@ -544,13 +622,13 @@ class PdfGenerator(Generator):
raise Exception("unable to find rst2pdf")
def _create_pdf(self, obj, output_path):
if obj.filename.endswith(".rst"):
if obj.source_path.endswith('.rst'):
filename = obj.slug + ".pdf"
output_pdf = os.path.join(output_path, filename)
# print "Generating pdf for", obj.filename, " in ", output_pdf
with open(obj.filename) as f:
# print('Generating pdf for', obj.source_path, 'in', output_pdf)
with open(obj.source_path) as f:
self.pdfcreator.createPdf(text=f.read(), output=output_pdf)
logger.info(u' [ok] writing %s' % output_pdf)
logger.info(' [ok] writing %s' % output_pdf)
def generate_context(self):
pass
@ -558,7 +636,7 @@ class PdfGenerator(Generator):
def generate_output(self, writer=None):
# we don't use the writer passed as argument here
# since we write our own files
logger.info(u' Generating PDF files...')
logger.info(' Generating PDF files...')
pdf_path = os.path.join(self.output_path, 'pdf')
if not os.path.exists(pdf_path):
try:
@ -573,16 +651,20 @@ class PdfGenerator(Generator):
for page in self.context['pages']:
self._create_pdf(page, pdf_path)
class SourceFileGenerator(Generator):
def generate_context(self):
self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION']
def _create_source(self, obj, output_path):
filename = os.path.splitext(obj.save_as)[0]
dest = os.path.join(output_path, filename + self.output_extension)
copy('', obj.filename, dest)
def _create_source(self, obj):
output_path, _ = os.path.splitext(obj.save_as)
dest = os.path.join(self.output_path,
output_path + self.output_extension)
copy('', obj.source_path, dest)
def generate_output(self, writer=None):
logger.info(u' Generating source files...')
for object in chain(self.context['articles'], self.context['pages']):
self._create_source(object, self.output_path)
logger.info(' Generating source files...')
for obj in chain(self.context['articles'], self.context['pages']):
self._create_source(obj)
for obj_trans in obj.translations:
self._create_source(obj_trans)

View file

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
__all__ = [
'init'
]
@ -9,7 +12,7 @@ import logging
from logging import Formatter, getLogger, StreamHandler, DEBUG
RESET_TERM = u'\033[0;m'
RESET_TERM = '\033[0;m'
COLOR_CODES = {
'red': 31,
@ -24,47 +27,47 @@ COLOR_CODES = {
def ansi(color, text):
"""Wrap text in an ansi escape sequence"""
code = COLOR_CODES[color]
return u'\033[1;{0}m{1}{2}'.format(code, text, RESET_TERM)
return '\033[1;{0}m{1}{2}'.format(code, text, RESET_TERM)
class ANSIFormatter(Formatter):
"""
Convert a `logging.LogReport' object into colored text, using ANSI escape sequences.
"""
## colors:
"""Convert a `logging.LogRecord' object into colored text, using ANSI
escape sequences.
"""
def format(self, record):
if record.levelname is 'INFO':
return ansi('cyan', '-> ') + unicode(record.msg)
elif record.levelname is 'WARNING':
return ansi('yellow', record.levelname) + ': ' + unicode(record.msg)
elif record.levelname is 'ERROR':
return ansi('red', record.levelname) + ': ' + unicode(record.msg)
elif record.levelname is 'CRITICAL':
return ansi('bgred', record.levelname) + ': ' + unicode(record.msg)
elif record.levelname is 'DEBUG':
return ansi('bggrey', record.levelname) + ': ' + unicode(record.msg)
msg = record.getMessage()
if record.levelname == 'INFO':
return ansi('cyan', '-> ') + msg
elif record.levelname == 'WARNING':
return ansi('yellow', record.levelname) + ': ' + msg
elif record.levelname == 'ERROR':
return ansi('red', record.levelname) + ': ' + msg
elif record.levelname == 'CRITICAL':
return ansi('bgred', record.levelname) + ': ' + msg
elif record.levelname == 'DEBUG':
return ansi('bggrey', record.levelname) + ': ' + msg
else:
return ansi('white', record.levelname) + ': ' + unicode(record.msg)
return ansi('white', record.levelname) + ': ' + msg
class TextFormatter(Formatter):
"""
Convert a `logging.LogReport' object into text.
Convert a `logging.LogRecord' object into text.
"""
def format(self, record):
if not record.levelname or record.levelname is 'INFO':
return record.msg
if not record.levelname or record.levelname == 'INFO':
return record.getMessage()
else:
return record.levelname + ': ' + record.msg
return record.levelname + ': ' + record.getMessage()
def init(level=None, logger=getLogger(), handler=StreamHandler()):
logger = logging.getLogger()
if os.isatty(sys.stdout.fileno()) \
and not sys.platform.startswith('win'):
if (os.isatty(sys.stdout.fileno())
and not sys.platform.startswith('win')):
fmt = ANSIFormatter()
else:
fmt = TextFormatter()

View file

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
# From django.core.paginator
from math import ceil
@ -37,7 +40,7 @@ class Paginator(object):
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
return range(1, self.num_pages + 1)
return list(range(1, self.num_pages + 1))
page_range = property(_get_page_range)

View file

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

@ -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,42 +0,0 @@
import hashlib
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():
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

@ -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, ex:
logger.critical('Gzip compression failed: %s' % ex)
finally:
compressed.close()
def register():
signals.finalized.connect(create_gzip_cache)

View file

@ -1,63 +0,0 @@
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

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

View file

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (c) FELD Boris <lothiraldan@gmail.com>
Multiple part support
=====================
Create a navigation menu for multi-part related_posts
Article metadata:
------------------
:parts: a unique identifier for multi-part posts, must be the same in each
post part.
Usage
-----
{% if article.metadata.parts_articles %}
<ol>
{% for part_article in article.metadata.parts_articles %}
{% if part_article == article %}
<li>
<a href='{{ SITEURL }}/{{ part_article.url }}'><b>{{ part_article.title }}</b>
</a>
</li>
{% else %}
<li>
<a href='{{ SITEURL }}/{{ part_article.url }}'>{{ part_article.title }}
</a>
</li>
{% endif %}
{% endfor %}
</ol>
{% endif %}
"""
from collections import defaultdict
from pelican import signals
def aggregate_multi_part(generator):
multi_part = defaultdict(list)
for article in generator.articles:
if 'parts' in article.metadata:
multi_part[article.metadata['parts']].append(article)
for part_id in multi_part:
parts = multi_part[part_id]
# Sort by date
parts.sort(key=lambda x: x.metadata['date'])
for article in parts:
article.metadata['parts_articles'] = parts
def register():
signals.article_generator_finalized.connect(aggregate_multi_part)

View file

@ -1,52 +0,0 @@
from pelican import signals
"""
Related posts plugin for Pelican
================================
Adds related_posts variable to article's context
Settings
--------
To enable, add
from pelican.plugins import related_posts
PLUGINS = [related_posts]
to your settings.py.
Usage
-----
{% if article.related_posts %}
<ul>
{% for related_post in article.related_posts %}
<li>{{ related_post }}</li>
{% endfor %}
</ul>
{% endif %}
"""
related_posts = []
def add_related_posts(generator, metadata):
if 'tags' in metadata:
for tag in metadata['tags']:
#print tag
for related_article in generator.tags[tag]:
related_posts.append(related_article)
if len(related_posts) < 1:
return
relation_score = dict(zip(set(related_posts), map(related_posts.count,
set(related_posts))))
ranked_related = sorted(relation_score, key=relation_score.get)
metadata["related_posts"] = ranked_related[:5]
def register():
signals.article_generate_context.connect(add_related_posts)

View file

@ -1,190 +0,0 @@
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 = u"""{0}/index.html
{0}/archives.html
{0}/tags.html
{0}/categories.html
"""
XML_HEADER = u"""<?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 = u"""
<url>
<loc>{0}/{1}</loc>
<lastmod>{2}</lastmod>
<changefreq>{3}</changefreq>
<priority>{4}</priority>
</url>
"""
XML_FOOTER = u"""
</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):
for k, v in pris.iteritems():
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):
for k, v in chfreqs.iteritems():
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,4 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import os
import re
try:
@ -20,15 +22,23 @@ try:
asciidoc = True
except ImportError:
asciidoc = False
try:
from html import escape
except ImportError:
from cgi import escape
try:
from html.parser import HTMLParser
except ImportError:
from HTMLParser import HTMLParser
from pelican.contents import Category, Tag, Author
from pelican.utils import get_date, pelican_open
_METADATA_PROCESSORS = {
'tags': lambda x, y: [Tag(tag, y) for tag in unicode(x).split(',')],
METADATA_PROCESSORS = {
'tags': lambda x, y: [Tag(tag, y) for tag in x.split(',')],
'date': lambda x, y: get_date(x),
'status': lambda x, y: unicode.strip(x),
'status': lambda x, y: x.strip(),
'category': Category,
'author': Author,
}
@ -36,16 +46,23 @@ _METADATA_PROCESSORS = {
class Reader(object):
enabled = True
file_extensions = ['static']
extensions = None
def __init__(self, settings):
self.settings = settings
def process_metadata(self, name, value):
if name in _METADATA_PROCESSORS:
return _METADATA_PROCESSORS[name](value, self.settings)
if name in METADATA_PROCESSORS:
return METADATA_PROCESSORS[name](value, self.settings)
return value
def read(self, source_path):
"No-op parser"
content = None
metadata = {}
return content, metadata
class _FieldBodyTranslator(HTMLTranslator):
@ -85,6 +102,9 @@ class RstReader(Reader):
enabled = bool(docutils)
file_extensions = ['rst']
def __init__(self, *args, **kwargs):
super(RstReader, self).__init__(*args, **kwargs)
def _parse_metadata(self, document):
"""Return the dict containing document metadata"""
output = {}
@ -105,20 +125,26 @@ class RstReader(Reader):
output[name] = self.process_metadata(name, value)
return output
def _get_publisher(self, filename):
extra_params = {'initial_header_level': '2'}
def _get_publisher(self, source_path):
extra_params = {'initial_header_level': '2',
'syntax_highlight': 'short',
'input_encoding': 'utf-8'}
user_params = self.settings.get('DOCUTILS_SETTINGS')
if user_params:
extra_params.update(user_params)
pub = docutils.core.Publisher(
destination_class=docutils.io.StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html')
pub.writer.translator_class = PelicanHTMLTranslator
pub.process_programmatic_settings(None, extra_params, None)
pub.set_source(source_path=filename)
pub.set_source(source_path=source_path)
pub.publish()
return pub
def read(self, filename):
def read(self, source_path):
"""Parses restructured text"""
pub = self._get_publisher(filename)
pub = self._get_publisher(source_path)
parts = pub.writer.parts
content = parts.get('body')
@ -130,48 +156,142 @@ class RstReader(Reader):
class MarkdownReader(Reader):
enabled = bool(Markdown)
file_extensions = ['md', 'markdown', 'mkd']
extensions = ['codehilite', 'extra']
file_extensions = ['md', 'markdown', 'mkd', 'mdown']
def __init__(self, *args, **kwargs):
super(MarkdownReader, self).__init__(*args, **kwargs)
self.extensions = self.settings['MD_EXTENSIONS']
self.extensions.append('meta')
self._md = Markdown(extensions=self.extensions)
def _parse_metadata(self, meta):
"""Return the dict containing document metadata"""
md = Markdown(extensions=set(self.extensions + ['meta']))
output = {}
for name, value in meta.items():
name = name.lower()
if name == "summary":
summary_values = "\n".join(str(item) for item in value)
summary = md.convert(summary_values)
summary_values = "\n".join(value)
# reset the markdown instance to clear any state
self._md.reset()
summary = self._md.convert(summary_values)
output[name] = self.process_metadata(name, summary)
else:
output[name] = self.process_metadata(name, value[0])
return output
def read(self, filename):
def read(self, source_path):
"""Parse content and metadata of markdown files"""
text = pelican_open(filename)
md = Markdown(extensions=set(self.extensions + ['meta']))
content = md.convert(text)
metadata = self._parse_metadata(md.Meta)
with pelican_open(source_path) as text:
content = self._md.convert(text)
metadata = self._parse_metadata(self._md.Meta)
return content, metadata
class HtmlReader(Reader):
file_extensions = ['html', 'htm']
_re = re.compile('\<\!\-\-\#\s?[A-z0-9_-]*\s?\:s?[A-z0-9\s_-]*\s?\-\-\>')
class HTMLReader(Reader):
"""Parses HTML files as input, looking for meta, title, and body tags"""
file_extensions = ['htm', 'html']
enabled = True
class _HTMLParser(HTMLParser):
def __init__(self, settings):
HTMLParser.__init__(self)
self.body = ''
self.metadata = {}
self.settings = settings
self._data_buffer = ''
self._in_top_level = True
self._in_head = False
self._in_title = False
self._in_body = False
self._in_tags = False
def handle_starttag(self, tag, attrs):
if tag == 'head' and self._in_top_level:
self._in_top_level = False
self._in_head = True
elif tag == 'title' and self._in_head:
self._in_title = True
self._data_buffer = ''
elif tag == 'body' and self._in_top_level:
self._in_top_level = False
self._in_body = True
self._data_buffer = ''
elif tag == 'meta' and self._in_head:
self._handle_meta_tag(attrs)
elif self._in_body:
self._data_buffer += self.build_tag(tag, attrs, False)
def handle_endtag(self, tag):
if tag == 'head':
if self._in_head:
self._in_head = False
self._in_top_level = True
elif tag == 'title':
self._in_title = False
self.metadata['title'] = self._data_buffer
elif tag == 'body':
self.body = self._data_buffer
self._in_body = False
self._in_top_level = True
elif self._in_body:
self._data_buffer += '</{}>'.format(escape(tag))
def handle_startendtag(self, tag, attrs):
if tag == 'meta' and self._in_head:
self._handle_meta_tag(attrs)
if self._in_body:
self._data_buffer += self.build_tag(tag, attrs, True)
def handle_comment(self, data):
self._data_buffer += '<!--{}-->'.format(data)
def handle_data(self, data):
self._data_buffer += data
def handle_entityref(self, data):
self._data_buffer += '&{};'.format(data)
def handle_charref(self, data):
self._data_buffer += '&#{};'.format(data)
def build_tag(self, tag, attrs, close_tag):
result = '<{}'.format(escape(tag))
for k, v in attrs:
result += ' ' + escape(k)
if v is not None:
result += '="{}"'.format(escape(v))
if close_tag:
return result + ' />'
return result + '>'
def _handle_meta_tag(self, attrs):
name = self._attr_value(attrs, 'name').lower()
contents = self._attr_value(attrs, 'contents', '')
if name == 'keywords':
name = 'tags'
self.metadata[name] = contents
@classmethod
def _attr_value(cls, attrs, name, default=None):
return next((x[1] for x in attrs if x[0] == name), default)
def read(self, filename):
"""Parse content and metadata of (x)HTML files"""
with open(filename) as content:
metadata = {'title': 'unnamed'}
for i in self._re.findall(content):
key = i.split(':')[0][5:].strip()
value = i.split(':')[-1][:-3].strip()
name = key.lower()
metadata[name] = self.process_metadata(name, value)
"""Parse content and metadata of HTML files"""
with pelican_open(filename) as content:
parser = self._HTMLParser(self.settings)
parser.feed(content)
parser.close()
return content, metadata
metadata = {}
for k in parser.metadata:
metadata[k] = self.process_metadata(k, parser.metadata[k])
return parser.body, metadata
class AsciiDocReader(Reader):
@ -179,14 +299,15 @@ class AsciiDocReader(Reader):
file_extensions = ['asc']
default_options = ["--no-header-footer", "-a newline=\\n"]
def read(self, filename):
def read(self, source_path):
"""Parse content and metadata of asciidoc files"""
from cStringIO import StringIO
text = StringIO(pelican_open(filename))
with pelican_open(source_path) as source:
text = StringIO(source)
content = StringIO()
ad = AsciiDocAPI()
options = self.settings.get('ASCIIDOC_OPTIONS', [])
options = self.settings['ASCIIDOC_OPTIONS']
if isinstance(options, (str, unicode)):
options = [m.strip() for m in options.split(',')]
options = self.default_options + options
@ -205,23 +326,26 @@ class AsciiDocReader(Reader):
return content, metadata
_EXTENSIONS = {}
EXTENSIONS = {}
for cls in Reader.__subclasses__():
for cls in [Reader] + Reader.__subclasses__():
for ext in cls.file_extensions:
_EXTENSIONS[ext] = cls
EXTENSIONS[ext] = cls
def read_file(filename, fmt=None, settings=None):
def read_file(path, fmt=None, settings=None):
"""Return a reader object using the given format."""
base, ext = os.path.splitext(os.path.basename(filename))
base, ext = os.path.splitext(os.path.basename(path))
if not fmt:
fmt = ext[1:]
if fmt not in _EXTENSIONS:
raise TypeError('Pelican does not know how to parse %s' % filename)
if fmt not in EXTENSIONS:
raise TypeError('Pelican does not know how to parse {}'.format(path))
reader = _EXTENSIONS[fmt](settings)
if settings is None:
settings = {}
reader = EXTENSIONS[fmt](settings)
settings_key = '%s_EXTENSIONS' % fmt.upper()
if settings and settings_key in settings:
@ -230,21 +354,53 @@ def read_file(filename, fmt=None, settings=None):
if not reader.enabled:
raise ValueError("Missing dependencies for %s" % fmt)
content, metadata = reader.read(filename)
metadata = parse_path_metadata(
path=path, settings=settings, process=reader.process_metadata)
content, reader_metadata = reader.read(path)
metadata.update(reader_metadata)
# eventually filter the content with typogrify if asked so
if settings and settings.get('TYPOGRIFY'):
if content and settings and settings['TYPOGRIFY']:
from typogrify.filters import typogrify
content = typogrify(content)
metadata['title'] = typogrify(metadata['title'])
filename_metadata = settings and settings.get('FILENAME_METADATA')
if filename_metadata:
match = re.match(filename_metadata, base)
if match:
for k, v in match.groupdict().iteritems():
if k not in metadata:
k = k.lower() # metadata must be lowercase
metadata[k] = reader.process_metadata(k, v)
return content, metadata
def parse_path_metadata(path, settings=None, process=None):
"""Extract a metadata dictionary from a file's path
>>> import pprint
>>> settings = {
... 'FILENAME_METADATA': '(?P<slug>[^.]*).*',
... 'PATH_METADATA':
... '(?P<category>[^/]*)/(?P<date>\d{4}-\d{2}-\d{2})/.*',
... }
>>> reader = Reader(settings=settings)
>>> metadata = parse_path_metadata(
... path='my-cat/2013-01-01/my-slug.html',
... settings=settings,
... process=reader.process_metadata)
>>> pprint.pprint(metadata) # doctest: +ELLIPSIS
{'category': <pelican.urlwrappers.Category object at ...>,
'date': datetime.datetime(2013, 1, 1, 0, 0),
'slug': 'my-slug'}
"""
metadata = {}
base, ext = os.path.splitext(os.path.basename(path))
if settings:
for key,data in [('FILENAME_METADATA', base),
('PATH_METADATA', path),
]:
regexp = settings.get(key)
if regexp:
match = re.match(regexp, data)
if match:
# .items() for py3k compat.
for k, v in match.groupdict().items():
if k not in metadata:
k = k.lower() # metadata must be lowercase
if process:
v = process(k, v)
metadata[k] = v
return metadata

View file

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
from docutils import nodes, utils
from docutils.parsers.rst import directives, roles, Directive
from pygments.formatters import HtmlFormatter
@ -32,7 +34,7 @@ class Pygments(Directive):
# take an arbitrary option if more than one is given
formatter = self.options and VARIANTS[self.options.keys()[0]] \
or DEFAULT
parsed = highlight(u'\n'.join(self.content), lexer, formatter)
parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
directives.register_directive('code-block', Pygments)

29
pelican/server.py Normal file
View file

@ -0,0 +1,29 @@
from __future__ import print_function
import sys
try:
import SimpleHTTPServer as srvmod
except ImportError:
import http.server as srvmod # NOQA
try:
import SocketServer as socketserver
except ImportError:
import socketserver # NOQA
PORT = 8000
Handler = srvmod.SimpleHTTPRequestHandler
try:
httpd = socketserver.TCPServer(("", PORT), Handler)
except OSError as e:
print("Could not listen on port", PORT)
sys.exit(getattr(e, 'exitcode', 1))
print("serving at port", PORT)
try:
httpd.serve_forever()
except KeyboardInterrupt as e:
print("shutting down server")
httpd.socket.close()

View file

@ -1,11 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import six
import copy
import imp
import inspect
import os
import locale
import logging
import re
try:
# SourceFileLoader is the recommended way in 3.3+
from importlib.machinery import SourceFileLoader
load_source = lambda name, path: SourceFileLoader(name, path).load_module()
except ImportError:
# but it does not exist in 3.2-, so fall back to imp
import imp
load_source = imp.load_source
from os.path import isabs
@ -13,88 +23,102 @@ from os.path import isabs
logger = logging.getLogger(__name__)
DEFAULT_THEME = os.sep.join([os.path.dirname(os.path.abspath(__file__)),
"themes/notmyidea"])
_DEFAULT_CONFIG = {'PATH': '.',
'ARTICLE_DIR': '',
'ARTICLE_EXCLUDES': ('pages',),
'PAGE_DIR': 'pages',
'PAGE_EXCLUDES': (),
'THEME': DEFAULT_THEME,
'OUTPUT_PATH': 'output/',
'MARKUP': ('rst', 'md'),
'STATIC_PATHS': ['images', ],
'THEME_STATIC_PATHS': ['static', ],
'FEED_ALL_ATOM': 'feeds/all.atom.xml',
'CATEGORY_FEED_ATOM': 'feeds/%s.atom.xml',
'TRANSLATION_FEED_ATOM': 'feeds/all-%s.atom.xml',
'FEED_MAX_ITEMS': '',
'SITEURL': '',
'SITENAME': 'A Pelican Blog',
'DISPLAY_PAGES_ON_MENU': True,
'PDF_GENERATOR': False,
'OUTPUT_SOURCES': False,
'OUTPUT_SOURCES_EXTENSION': '.text',
'USE_FOLDER_AS_CATEGORY': True,
'DEFAULT_CATEGORY': 'misc',
'WITH_FUTURE_DATES': True,
'CSS_FILE': 'main.css',
'NEWEST_FIRST_ARCHIVES': True,
'REVERSE_CATEGORY_ORDER': False,
'DELETE_OUTPUT_DIRECTORY': False,
'ARTICLE_URL': '{slug}.html',
'ARTICLE_SAVE_AS': '{slug}.html',
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
'PAGE_URL': 'pages/{slug}.html',
'PAGE_SAVE_AS': 'pages/{slug}.html',
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html',
'CATEGORY_URL': 'category/{slug}.html',
'CATEGORY_SAVE_AS': 'category/{slug}.html',
'TAG_URL': 'tag/{slug}.html',
'TAG_SAVE_AS': 'tag/{slug}.html',
'AUTHOR_URL': u'author/{slug}.html',
'AUTHOR_SAVE_AS': u'author/{slug}.html',
'RELATIVE_URLS': True,
'DEFAULT_LANG': 'en',
'TAG_CLOUD_STEPS': 4,
'TAG_CLOUD_MAX_ITEMS': 100,
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
'EXTRA_TEMPLATES_PATHS': [],
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
'PELICAN_CLASS': 'pelican.Pelican',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
'DATE_FORMATS': {},
'JINJA_EXTENSIONS': [],
'LOCALE': '', # default to user locale
'DEFAULT_PAGINATION': False,
'DEFAULT_ORPHANS': 0,
'DEFAULT_METADATA': (),
'FILENAME_METADATA': '(?P<date>\d{4}-\d{2}-\d{2}).*',
'FILES_TO_COPY': (),
'DEFAULT_STATUS': 'published',
'ARTICLE_PERMALINK_STRUCTURE': '',
'TYPOGRIFY': False,
'SUMMARY_MAX_LENGTH': 50,
'PLUGINS': [],
'TEMPLATE_PAGES': {}
}
DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'themes', 'notmyidea')
DEFAULT_CONFIG = {
'PATH': os.curdir,
'ARTICLE_DIR': '',
'ARTICLE_EXCLUDES': ('pages',),
'PAGE_DIR': 'pages',
'PAGE_EXCLUDES': (),
'THEME': DEFAULT_THEME,
'OUTPUT_PATH': 'output',
'MARKUP': ('rst', 'md'),
'STATIC_PATHS': ['images', ],
'THEME_STATIC_PATHS': ['static', ],
'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'),
'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
'TRANSLATION_FEED_ATOM': os.path.join('feeds', 'all-%s.atom.xml'),
'FEED_MAX_ITEMS': '',
'SITEURL': '',
'SITENAME': 'A Pelican Blog',
'DISPLAY_PAGES_ON_MENU': True,
'DISPLAY_CATEGORIES_ON_MENU': True,
'PDF_GENERATOR': False,
'OUTPUT_SOURCES': False,
'OUTPUT_SOURCES_EXTENSION': '.text',
'USE_FOLDER_AS_CATEGORY': True,
'DEFAULT_CATEGORY': 'misc',
'WITH_FUTURE_DATES': True,
'CSS_FILE': 'main.css',
'NEWEST_FIRST_ARCHIVES': True,
'REVERSE_CATEGORY_ORDER': False,
'DELETE_OUTPUT_DIRECTORY': False,
'ARTICLE_URL': '{slug}.html',
'ARTICLE_SAVE_AS': '{slug}.html',
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
'PAGE_URL': 'pages/{slug}.html',
'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'),
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
'PAGE_LANG_SAVE_AS': os.path.join('pages', '{slug}-{lang}.html'),
'STATIC_URL': '{path}',
'STATIC_SAVE_AS': '{path}',
'PDF_STYLE_PATH': '',
'PDF_STYLE': 'twelvepoint',
'CATEGORY_URL': 'category/{slug}.html',
'CATEGORY_SAVE_AS': os.path.join('category', '{slug}.html'),
'TAG_URL': 'tag/{slug}.html',
'TAG_SAVE_AS': os.path.join('tag', '{slug}.html'),
'AUTHOR_URL': 'author/{slug}.html',
'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'),
'YEAR_ARCHIVE_SAVE_AS': False,
'MONTH_ARCHIVE_SAVE_AS': False,
'DAY_ARCHIVE_SAVE_AS': False,
'RELATIVE_URLS': False,
'DEFAULT_LANG': 'en',
'TAG_CLOUD_STEPS': 4,
'TAG_CLOUD_MAX_ITEMS': 100,
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
'EXTRA_TEMPLATES_PATHS': [],
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
'PELICAN_CLASS': 'pelican.Pelican',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
'DATE_FORMATS': {},
'ASCIIDOC_OPTIONS': [],
'MD_EXTENSIONS': ['codehilite(css_class=highlight)', 'extra'],
'JINJA_EXTENSIONS': [],
'JINJA_FILTERS': {},
'LOCALE': [], # defaults to user locale
'DEFAULT_PAGINATION': False,
'DEFAULT_ORPHANS': 0,
'DEFAULT_METADATA': (),
'FILENAME_METADATA': '(?P<date>\d{4}-\d{2}-\d{2}).*',
'PATH_METADATA': '',
'FILES_TO_COPY': (),
'DEFAULT_STATUS': 'published',
'ARTICLE_PERMALINK_STRUCTURE': '',
'TYPOGRIFY': False,
'SUMMARY_MAX_LENGTH': 50,
'PLUGIN_PATH': '',
'PLUGINS': [],
'TEMPLATE_PAGES': {},
'IGNORE_FILES': ['.#*'],
}
def read_settings(filename=None, override=None):
if filename:
local_settings = get_settings_from_file(filename)
def read_settings(path=None, override=None):
if path:
local_settings = get_settings_from_file(path)
# Make the paths relative to the settings file
for p in ['PATH', 'OUTPUT_PATH', 'THEME']:
for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'PLUGIN_PATH']:
if p in local_settings and local_settings[p] is not None \
and not isabs(local_settings[p]):
absp = os.path.abspath(os.path.normpath(os.path.join(
os.path.dirname(filename), local_settings[p])))
if p != 'THEME' or os.path.exists(p):
os.path.dirname(path), local_settings[p])))
if p not in ('THEME', 'PLUGIN_PATH') or os.path.exists(absp):
local_settings[p] = absp
else:
local_settings = copy.deepcopy(_DEFAULT_CONFIG)
local_settings = copy.deepcopy(DEFAULT_CONFIG)
if override:
local_settings.update(override)
@ -102,10 +126,8 @@ def read_settings(filename=None, override=None):
return configure_settings(local_settings)
def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
"""
Load settings from a module, returning a dict.
"""
def get_settings_from_module(module=None, default_settings=DEFAULT_CONFIG):
"""Loads settings from a module, returns a dictionary."""
context = copy.deepcopy(default_settings)
if module is not None:
@ -114,61 +136,81 @@ def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
return context
def get_settings_from_file(filename, default_settings=_DEFAULT_CONFIG):
"""
Load settings from a file path, returning a dict.
def get_settings_from_file(path, default_settings=DEFAULT_CONFIG):
"""Loads settings from a file path, returning a dict."""
"""
name = os.path.basename(filename).rpartition(".")[0]
module = imp.load_source(name, filename)
name, ext = os.path.splitext(os.path.basename(path))
module = load_source(name, path)
return get_settings_from_module(module, default_settings=default_settings)
def configure_settings(settings):
"""
Provide optimizations, error checking, and warnings for loaded settings
"""Provide optimizations, error checking and warnings for the given
settings.
"""
if not 'PATH' in settings or not os.path.isdir(settings['PATH']):
raise Exception('You need to specify a path containing the content'
' (see pelican --help for more information)')
# find the theme in pelican.theme if the given one does not exists
# lookup the theme in "pelican/themes" if the given one doesn't exist
if not os.path.isdir(settings['THEME']):
theme_path = os.sep.join([os.path.dirname(
os.path.abspath(__file__)), "themes/%s" % settings['THEME']])
theme_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'themes',
settings['THEME'])
if os.path.exists(theme_path):
settings['THEME'] = theme_path
else:
raise Exception("Impossible to find the theme %s"
raise Exception("Could not find the theme %s"
% settings['THEME'])
# if locales is not a list, make it one
locales = settings['LOCALE']
# standardize strings to lowercase strings
for key in [
'DEFAULT_LANG',
]:
if key in settings:
settings[key] = settings[key].lower()
if isinstance(locales, basestring):
locales = [locales]
# standardize strings to lists
for key in [
'LOCALE',
]:
if key in settings and isinstance(settings[key], six.string_types):
settings[key] = [settings[key]]
# check settings that must be a particular type
for key, types in [
('OUTPUT_SOURCES_EXTENSION', six.string_types),
('FILENAME_METADATA', six.string_types),
]:
if key in settings and not isinstance(settings[key], types):
value = settings.pop(key)
logger.warn(
'Detected misconfigured {} ({}), '
'falling back to the default ({})'.format(
key, value, DEFAULT_CONFIG[key]))
# try to set the different locales, fallback on the default.
if not locales:
locales = _DEFAULT_CONFIG['LOCALE']
locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE'])
for locale_ in locales:
try:
locale.setlocale(locale.LC_ALL, locale_)
locale.setlocale(locale.LC_ALL, str(locale_))
break # break if it is successful
except locale.Error:
pass
else:
logger.warn("LOCALE option doesn't contain a correct value")
logger.warning("LOCALE option doesn't contain a correct value")
if ('SITEURL' in settings):
# If SITEURL has a trailing slash, remove it and provide a warning
siteurl = settings['SITEURL']
if (siteurl.endswith('/')):
settings['SITEURL'] = siteurl[:-1]
logger.warn("Removed extraneous trailing slash from SITEURL.")
# If SITEURL is defined but FEED_DOMAIN isn't, set FEED_DOMAIN = SITEURL
logger.warning("Removed extraneous trailing slash from SITEURL.")
# If SITEURL is defined but FEED_DOMAIN isn't,
# set FEED_DOMAIN to SITEURL
if not 'FEED_DOMAIN' in settings:
settings['FEED_DOMAIN'] = settings['SITEURL']
@ -181,37 +223,45 @@ def configure_settings(settings):
]
if any(settings.get(k) for k in feed_keys):
if not settings.get('FEED_DOMAIN'):
logger.warn("Since feed URLs should always be absolute, you should specify "
"FEED_DOMAIN in your settings. (e.g., 'FEED_DOMAIN = "
"http://www.example.com')")
if not settings.get('SITEURL'):
logger.warn("Feeds generated without SITEURL set properly may not be valid")
logger.warning('Feeds generated without SITEURL set properly may not'
' be valid')
if not 'TIMEZONE' in settings:
logger.warn("No timezone information specified in the settings. Assuming"
" your timezone is UTC for feed generation. Check "
"http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "
"for more information")
logger.warning(
'No timezone information specified in the settings. Assuming'
' your timezone is UTC for feed generation. Check '
'http://docs.getpelican.com/en/latest/settings.html#timezone '
'for more information')
if 'LESS_GENERATOR' in settings:
logger.warn("The LESS_GENERATOR setting has been removed in favor "
"of the Webassets plugin")
# Save people from accidentally setting a string rather than a list
path_keys = (
'ARTICLE_EXCLUDES',
'DEFAULT_METADATA',
'DIRECT_TEMPLATES',
'EXTRA_TEMPLATES_PATHS',
'FILES_TO_COPY',
'IGNORE_FILES',
'JINJA_EXTENSIONS',
'MARKUP',
'PAGINATED_DIRECT_TEMPLATES',
'PLUGINS',
'STATIC_PATHS',
'THEME_STATIC_PATHS',)
for PATH_KEY in filter(lambda k: k in settings, path_keys):
if isinstance(settings[PATH_KEY], six.string_types):
logger.warning("Detected misconfiguration with %s setting (must "
"be a list), falling back to the default"
% PATH_KEY)
settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY]
if 'OUTPUT_SOURCES_EXTENSION' in settings:
if not isinstance(settings['OUTPUT_SOURCES_EXTENSION'], str):
settings['OUTPUT_SOURCES_EXTENSION'] = _DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION']
logger.warn("Detected misconfiguration with OUTPUT_SOURCES_EXTENSION."
" falling back to the default extension " +
_DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION'])
filename_metadata = settings.get('FILENAME_METADATA')
if filename_metadata and not isinstance(filename_metadata, basestring):
logger.error("Detected misconfiguration with FILENAME_METADATA"
" setting (must be string or compiled pattern), falling"
"back to the default")
settings['FILENAME_METADATA'] = \
_DEFAULT_CONFIG['FILENAME_METADATA']
for old,new,doc in [
('LESS_GENERATOR', 'the Webassets plugin', None),
]:
if old in settings:
message = 'The {} setting has been removed in favor of {}'
if doc:
message += ', see {} for details'
logger.warning(message)
return settings

View file

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
from blinker import signal
initialized = signal('pelican_initialized')
@ -10,4 +12,5 @@ article_generator_finalized = signal('article_generate_finalized')
get_generators = signal('get_generators')
pages_generate_context = signal('pages_generate_context')
pages_generator_init = signal('pages_generator_init')
pages_generator_finalized = signal('pages_generator_finalized')
content_object_init = signal('content_object_init')

View file

@ -0,0 +1,2 @@
import logging
logging.getLogger().addHandler(logging.NullHandler())

View file

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
Body content
<!-- This comment is included (including extra whitespace) -->
</body>
</html>

View file

@ -0,0 +1,6 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="keywords" contents="foo, bar, foobar" />
</head>
</html>

View file

@ -0,0 +1,8 @@
Title: Article with markdown containing footnotes
Date: 2012-10-31
Summary: Summary with **inline** markup *should* be supported.
This is some content[^1] with some footnotes[^footnote]
[^1]: Numbered footnote
[^footnote]: Named footnote

View file

@ -0,0 +1,18 @@
Title: マックOS X 10.8でパイソンとVirtualenvをインストールと設定
Slug: python-virtualenv-on-mac-osx-mountain-lion-10.8
Date: 2012-12-20
Tags: パイソン, マック
Category: 指導書
Summary: パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
Writing unicode is certainly fun.
パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
And let's mix languages.
первый пост
Now another.
İlk yazı çok özel değil.

View file

@ -0,0 +1,10 @@
title: Test markdown File
category: test
Test Markdown File Header
=========================
Used for pelican test
---------------------
This is another markdown test file. Uses the markdown extension.

View file

@ -1,5 +1,8 @@
title: Test md File
category: test
Title: Test md File
Category: test
Tags: foo, bar, foobar
Date: 2010-12-02 10:14
Summary: I have a lot to test
Test Markdown File Header
=========================

View file

@ -0,0 +1,10 @@
title: Test mdown File
category: test
Test Markdown File Header
=========================
Used for pelican test
---------------------
This is another markdown test file. Uses the mdown extension.

View file

@ -0,0 +1,15 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="tags" contents="foo, bar, foobar" />
<meta name="date" contents="2010-12-02 10:14" />
<meta name="category" contents="yeah" />
<meta name="author" contents="Alexis Métaireau" />
<meta name="summary" contents="Summary and stuff" />
<meta name="custom_field" contents="http://notmyidea.org" />
</head>
<body>
Multi-line metadata should be supported
as well as <strong>inline markup</strong>.
</body>
</html>

View file

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
Ensure that empty attributes are copied properly.
<input name="test" disabled style="" />
</body>
</html>

View file

@ -0,0 +1,6 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="Category" contents="Yeah" />
</head>
</html>

View file

@ -0,0 +1,48 @@
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p><object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/XSrW-wAWZe4"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/XSrW-wAWZe4" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<blockquote><p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></blockquote>
<ul>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li>
</ul>
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

View file

@ -0,0 +1,55 @@
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/XSrW-wAWZe4"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/XSrW-wAWZe4" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>
<blockquote>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</blockquote>
<ul>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li>
<li>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li>
</ul>
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

View file

@ -628,5 +628,59 @@ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]></con
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Code in List</title>
<link>http://thisisa.test/?p=175</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=175</guid>
<description></description>
<content:encoded><![CDATA[Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<ul>
<li>List Item One!</li>
<li>List Item Two!</li>
<li>This is a code sample
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre></li>
<li>List Item Four!</li>
</ul>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>175</wp:post_id>
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>code-in-list-test</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-2"><![CDATA[Category 2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
</channel>
</rss>

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
AUTHOR = u'Alexis Métaireau'
SITENAME = u"Alexis' log"
from __future__ import unicode_literals, print_function
AUTHOR = 'Alexis Métaireau'
SITENAME = "Alexis' log"
SITEURL = 'http://blog.notmyidea.org'
TIMEZONE = 'UTC'
@ -18,7 +19,7 @@ LINKS = (('Biologeek', 'http://biologeek.org'),
('Filyb', "http://filyb.info/"),
('Libert-fr', "http://www.libert-fr.com"),
('N1k0', "http://prendreuncafe.com/blog/"),
(u'Tarek Ziadé', "http://ziade.org/blog"),
('Tarek Ziadé', "http://ziade.org/blog"),
('Zubin Mithra', "http://zubin71.wordpress.com/"),)
SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
@ -29,7 +30,7 @@ SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
DEFAULT_METADATA = (('yeah', 'it is'),)
# static paths will be copied under the same name
STATIC_PATHS = ["pictures",]
STATIC_PATHS = ["pictures", ]
# A list of files to copy from the source to the destination
FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A markdown powered article</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li class="active"><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./a-markdown-powered-article.html" rel="bookmark"
<a href="/a-markdown-powered-article.html" rel="bookmark"
title="Permalink to A markdown powered article">A markdown powered article</a></h1>
</header>
@ -43,17 +37,24 @@
Wed 20 April 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>You're mutually oblivious.</p>
<p><a href="./unbelievable.html">a root-relative link to unbelievable</a>
<a href="./unbelievable.html">a file-relative link to unbelievable</a></p>
<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
<a href="/unbelievable.html">a file-relative link to unbelievable</a></p>
</div><!-- /.entry-content -->
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<h1>Archives for A Pelican Blog</h1>
<dl>
<dt>Fri 30 November 2012</dt>
<dd><a href="/filename_metadata-example.html">FILENAME_METADATA example</a></dd>
<dt>Wed 29 February 2012</dt>
<dd><a href="/second-article.html">Second article</a></dd>
<dt>Wed 20 April 2011</dt>
<dd><a href="/a-markdown-powered-article.html">A markdown powered article</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-1.html">Article 1</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-2.html">Article 2</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-3.html">Article 3</a></dd>
<dt>Thu 02 December 2010</dt>
<dd><a href="/this-is-a-super-article.html">This is a super article !</a></dd>
<dt>Wed 20 October 2010</dt>
<dd><a href="/oh-yeah.html">Oh yeah !</a></dd>
<dt>Fri 15 October 2010</dt>
<dd><a href="/unbelievable.html">Unbelievable !</a></dd>
</dl>
</section>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Article 1</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li class="active"><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./article-1.html" rel="bookmark"
<a href="/article-1.html" rel="bookmark"
title="Permalink to Article 1">Article 1</a></h1>
</header>
@ -43,7 +37,7 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 1</p>
@ -52,7 +46,14 @@
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Article 2</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li class="active"><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./article-2.html" rel="bookmark"
<a href="/article-2.html" rel="bookmark"
title="Permalink to Article 2">Article 2</a></h1>
</header>
@ -43,7 +37,7 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 2</p>
@ -52,7 +46,14 @@
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Article 3</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li class="active"><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./article-3.html" rel="bookmark"
<a href="/article-3.html" rel="bookmark"
title="Permalink to Article 3">Article 3</a></h1>
</header>
@ -43,7 +37,7 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 3</p>
@ -52,7 +46,14 @@
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,50 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog - Alexis Métaireau</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie.css"/>
<script src="../js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="../">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="../pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="../category/bar.html">bar</a></li>
<li ><a href="../category/cat1.html">cat1</a></li>
<li ><a href="../category/misc.html">misc</a></li>
<li ><a href="../category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="../oh-yeah.html">Oh yeah !</a></h1>
<h1 class="entry-title"><a href="/oh-yeah.html">Oh yeah !</a></h1>
<footer class="post-info">
<abbr class="published" title="2010-10-20T10:14:00">
Wed 20 October 2010
</abbr>
<address class="vcard author">
By <a class="url fn" href="../author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="../category/bar.html">bar</a>. </p>
<p>tags: <a href="../tag/oh.html">oh</a><a href="../tag/bar.html">bar</a><a href="../tag/yeah.html">yeah</a></p>
<p>In <a href="/category/bar.html">bar</a>. </p>
<p>tags: <a href="/tag/oh.html">oh</a><a href="/tag/bar.html">bar</a><a href="/tag/yeah.html">yeah</a></p>
</footer><!-- /.post-info --><div class="section" id="why-not">
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
@ -62,7 +56,7 @@ YEAH !</p>
<li><article class="hentry">
<header>
<h1><a href="../this-is-a-super-article.html" rel="bookmark"
<h1><a href="/this-is-a-super-article.html" rel="bookmark"
title="Permalink to This is a super article !">This is a super article !</a></h1>
</header>
@ -73,20 +67,27 @@ YEAH !</p>
</abbr>
<address class="vcard author">
By <a class="url fn" href="../author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="../category/yeah.html">yeah</a>. </p>
<p>tags: <a href="../tag/foo.html">foo</a><a href="../tag/bar.html">bar</a><a href="../tag/foobar.html">foobar</a></p>
<p>In <a href="/category/yeah.html">yeah</a>. </p>
<p>tags: <a href="/tag/foo.html">foo</a><a href="/tag/bar.html">bar</a><a href="/tag/foobar.html">foobar</a></p>
</footer><!-- /.post-info --> <p class="first last">Multi-line metadata should be supported
as well as <strong>inline markup</strong>.</p>
<a class="readmore" href="../this-is-a-super-article.html">read more</a>
<a class="readmore" href="/this-is-a-super-article.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<ul>
<li><a href="/category/bar.html">bar</a></li>
<li><a href="/category/cat1.html">cat1</a></li>
<li><a href="/category/misc.html">misc</a></li>
<li><a href="/category/yeah.html">yeah</a></li>
</ul>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

View file

@ -1,50 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog - bar</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie.css"/>
<script src="../js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="../">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="../pages/this-is-a-test-page.html">This is a test page</a></li>
<li class="active"><a href="../category/bar.html">bar</a></li>
<li ><a href="../category/cat1.html">cat1</a></li>
<li ><a href="../category/misc.html">misc</a></li>
<li ><a href="../category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li class="active"><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="../oh-yeah.html">Oh yeah !</a></h1>
<h1 class="entry-title"><a href="/oh-yeah.html">Oh yeah !</a></h1>
<footer class="post-info">
<abbr class="published" title="2010-10-20T10:14:00">
Wed 20 October 2010
</abbr>
<address class="vcard author">
By <a class="url fn" href="../author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="../category/bar.html">bar</a>. </p>
<p>tags: <a href="../tag/oh.html">oh</a><a href="../tag/bar.html">bar</a><a href="../tag/yeah.html">yeah</a></p>
<p>In <a href="/category/bar.html">bar</a>. </p>
<p>tags: <a href="/tag/oh.html">oh</a><a href="/tag/bar.html">bar</a><a href="/tag/yeah.html">yeah</a></p>
</footer><!-- /.post-info --><div class="section" id="why-not">
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
@ -56,7 +50,14 @@ YEAH !</p>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,50 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog - cat1</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie.css"/>
<script src="../js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="../">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="../pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="../category/bar.html">bar</a></li>
<li class="active"><a href="../category/cat1.html">cat1</a></li>
<li ><a href="../category/misc.html">misc</a></li>
<li ><a href="../category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="../a-markdown-powered-article.html">A markdown powered article</a></h1>
<h1 class="entry-title"><a href="/a-markdown-powered-article.html">A markdown powered article</a></h1>
<footer class="post-info">
<abbr class="published" title="2011-04-20T00:00:00">
Wed 20 April 2011
</abbr>
<p>In <a href="../category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --><p>You're mutually oblivious.</p>
<p><a href="../unbelievable.html">a root-relative link to unbelievable</a>
<a href="../unbelievable.html">a file-relative link to unbelievable</a></p> </article>
<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
<a href="/unbelievable.html">a file-relative link to unbelievable</a></p> </article>
</aside><!-- /#featured -->
<section id="content" class="body">
<h1>Other articles</h1>
@ -55,7 +49,7 @@
<li><article class="hentry">
<header>
<h1><a href="../article-1.html" rel="bookmark"
<h1><a href="/article-1.html" rel="bookmark"
title="Permalink to Article 1">Article 1</a></h1>
</header>
@ -65,11 +59,11 @@
Thu 17 February 2011
</abbr>
<p>In <a href="../category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 1</p>
<a class="readmore" href="../article-1.html">read more</a>
<a class="readmore" href="/article-1.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -77,7 +71,7 @@
<li><article class="hentry">
<header>
<h1><a href="../article-2.html" rel="bookmark"
<h1><a href="/article-2.html" rel="bookmark"
title="Permalink to Article 2">Article 2</a></h1>
</header>
@ -87,11 +81,11 @@
Thu 17 February 2011
</abbr>
<p>In <a href="../category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 2</p>
<a class="readmore" href="../article-2.html">read more</a>
<a class="readmore" href="/article-2.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -99,7 +93,7 @@
<li><article class="hentry">
<header>
<h1><a href="../article-3.html" rel="bookmark"
<h1><a href="/article-3.html" rel="bookmark"
title="Permalink to Article 3">Article 3</a></h1>
</header>
@ -109,17 +103,24 @@
Thu 17 February 2011
</abbr>
<p>In <a href="../category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 3</p>
<a class="readmore" href="../article-3.html">read more</a>
<a class="readmore" href="/article-3.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,46 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog - misc</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie.css"/>
<script src="../js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="../">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="../pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="../category/bar.html">bar</a></li>
<li ><a href="../category/cat1.html">cat1</a></li>
<li class="active"><a href="../category/misc.html">misc</a></li>
<li ><a href="../category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li class="active"><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="../filename_metadata-example.html">FILENAME_METADATA example</a></h1>
<h1 class="entry-title"><a href="/filename_metadata-example.html">FILENAME_METADATA example</a></h1>
<footer class="post-info">
<abbr class="published" title="2012-11-30T00:00:00">
Fri 30 November 2012
</abbr>
<p>In <a href="../category/misc.html">misc</a>. </p>
<p>In <a href="/category/misc.html">misc</a>. </p>
</footer><!-- /.post-info --><p>Some cool stuff!</p>
</article>
@ -54,7 +48,7 @@
<li><article class="hentry">
<header>
<h1><a href="../second-article.html" rel="bookmark"
<h1><a href="/second-article.html" rel="bookmark"
title="Permalink to Second article">Second article</a></h1>
</header>
@ -64,13 +58,13 @@
Wed 29 February 2012
</abbr>
<p>In <a href="../category/misc.html">misc</a>. </p>
<p>tags: <a href="../tag/foo.html">foo</a><a href="../tag/bar.html">bar</a><a href="../tag/baz.html">baz</a></p>Translations:
<a href="../second-article-fr.html">fr</a>
<p>In <a href="/category/misc.html">misc</a>. </p>
<p>tags: <a href="/tag/foo.html">foo</a><a href="/tag/bar.html">bar</a><a href="/tag/baz.html">baz</a></p>Translations:
<a href="/second-article-fr.html">fr</a>
</footer><!-- /.post-info --> <p>This is some article, in english</p>
<a class="readmore" href="../second-article.html">read more</a>
<a class="readmore" href="/second-article.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -78,7 +72,7 @@
<li><article class="hentry">
<header>
<h1><a href="../unbelievable.html" rel="bookmark"
<h1><a href="/unbelievable.html" rel="bookmark"
title="Permalink to Unbelievable !">Unbelievable !</a></h1>
</header>
@ -88,19 +82,26 @@
Fri 15 October 2010
</abbr>
<p>In <a href="../category/misc.html">misc</a>. </p>
<p>In <a href="/category/misc.html">misc</a>. </p>
</footer><!-- /.post-info --> <p>Or completely awesome. Depends the needs.</p>
<p><a class="reference external" href="../a-markdown-powered-article.html">a root-relative link to markdown-article</a>
<a class="reference external" href="../a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
<a class="readmore" href="../unbelievable.html">read more</a>
<a class="readmore" href="/unbelievable.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,50 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog - yeah</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie.css"/>
<script src="../js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="../css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="../">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="../pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="../category/bar.html">bar</a></li>
<li ><a href="../category/cat1.html">cat1</a></li>
<li ><a href="../category/misc.html">misc</a></li>
<li class="active"><a href="../category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li class="active"><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="../this-is-a-super-article.html">This is a super article !</a></h1>
<h1 class="entry-title"><a href="/this-is-a-super-article.html">This is a super article !</a></h1>
<footer class="post-info">
<abbr class="published" title="2010-12-02T10:14:00">
Thu 02 December 2010
</abbr>
<address class="vcard author">
By <a class="url fn" href="../author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="../category/yeah.html">yeah</a>. </p>
<p>tags: <a href="../tag/foo.html">foo</a><a href="../tag/bar.html">bar</a><a href="../tag/foobar.html">foobar</a></p>
<p>In <a href="/category/yeah.html">yeah</a>. </p>
<p>tags: <a href="/tag/foo.html">foo</a><a href="/tag/bar.html">bar</a><a href="/tag/foobar.html">foobar</a></p>
</footer><!-- /.post-info --><p>Some content here !</p>
<div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2>
@ -62,7 +56,14 @@
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>FILENAME_METADATA example</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li ><a href="./category/cat1.html">cat1</a></li>
<li class="active"><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li class="active"><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./filename_metadata-example.html" rel="bookmark"
<a href="/filename_metadata-example.html" rel="bookmark"
title="Permalink to FILENAME_METADATA example">FILENAME_METADATA example</a></h1>
</header>
@ -43,7 +37,7 @@
Fri 30 November 2012
</abbr>
<p>In <a href="./category/misc.html">misc</a>. </p>
<p>In <a href="/category/misc.html">misc</a>. </p>
</footer><!-- /.post-info --> <p>Some cool stuff!</p>
@ -52,7 +46,14 @@
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,46 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="./category/bar.html">bar</a></li>
<li ><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="./filename_metadata-example.html">FILENAME_METADATA example</a></h1>
<h1 class="entry-title"><a href="/filename_metadata-example.html">FILENAME_METADATA example</a></h1>
<footer class="post-info">
<abbr class="published" title="2012-11-30T00:00:00">
Fri 30 November 2012
</abbr>
<p>In <a href="./category/misc.html">misc</a>. </p>
<p>In <a href="/category/misc.html">misc</a>. </p>
</footer><!-- /.post-info --><p>Some cool stuff!</p>
</article>
@ -54,7 +48,7 @@
<li><article class="hentry">
<header>
<h1><a href="./second-article.html" rel="bookmark"
<h1><a href="/second-article.html" rel="bookmark"
title="Permalink to Second article">Second article</a></h1>
</header>
@ -64,13 +58,13 @@
Wed 29 February 2012
</abbr>
<p>In <a href="./category/misc.html">misc</a>. </p>
<p>tags: <a href="./tag/foo.html">foo</a><a href="./tag/bar.html">bar</a><a href="./tag/baz.html">baz</a></p>Translations:
<a href="./second-article-fr.html">fr</a>
<p>In <a href="/category/misc.html">misc</a>. </p>
<p>tags: <a href="/tag/foo.html">foo</a><a href="/tag/bar.html">bar</a><a href="/tag/baz.html">baz</a></p>Translations:
<a href="/second-article-fr.html">fr</a>
</footer><!-- /.post-info --> <p>This is some article, in english</p>
<a class="readmore" href="./second-article.html">read more</a>
<a class="readmore" href="/second-article.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -78,7 +72,7 @@
<li><article class="hentry">
<header>
<h1><a href="./a-markdown-powered-article.html" rel="bookmark"
<h1><a href="/a-markdown-powered-article.html" rel="bookmark"
title="Permalink to A markdown powered article">A markdown powered article</a></h1>
</header>
@ -88,12 +82,12 @@
Wed 20 April 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>You're mutually oblivious.</p>
<p><a href="./unbelievable.html">a root-relative link to unbelievable</a>
<a href="./unbelievable.html">a file-relative link to unbelievable</a></p>
<a class="readmore" href="./a-markdown-powered-article.html">read more</a>
<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
<a href="/unbelievable.html">a file-relative link to unbelievable</a></p>
<a class="readmore" href="/a-markdown-powered-article.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -101,7 +95,7 @@
<li><article class="hentry">
<header>
<h1><a href="./article-1.html" rel="bookmark"
<h1><a href="/article-1.html" rel="bookmark"
title="Permalink to Article 1">Article 1</a></h1>
</header>
@ -111,11 +105,11 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 1</p>
<a class="readmore" href="./article-1.html">read more</a>
<a class="readmore" href="/article-1.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -123,7 +117,7 @@
<li><article class="hentry">
<header>
<h1><a href="./article-2.html" rel="bookmark"
<h1><a href="/article-2.html" rel="bookmark"
title="Permalink to Article 2">Article 2</a></h1>
</header>
@ -133,11 +127,11 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 2</p>
<a class="readmore" href="./article-2.html">read more</a>
<a class="readmore" href="/article-2.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -145,7 +139,7 @@
<li><article class="hentry">
<header>
<h1><a href="./article-3.html" rel="bookmark"
<h1><a href="/article-3.html" rel="bookmark"
title="Permalink to Article 3">Article 3</a></h1>
</header>
@ -155,11 +149,11 @@
Thu 17 February 2011
</abbr>
<p>In <a href="./category/cat1.html">cat1</a>. </p>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 3</p>
<a class="readmore" href="./article-3.html">read more</a>
<a class="readmore" href="/article-3.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -167,7 +161,7 @@
<li><article class="hentry">
<header>
<h1><a href="./this-is-a-super-article.html" rel="bookmark"
<h1><a href="/this-is-a-super-article.html" rel="bookmark"
title="Permalink to This is a super article !">This is a super article !</a></h1>
</header>
@ -178,14 +172,14 @@
</abbr>
<address class="vcard author">
By <a class="url fn" href="./author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="./category/yeah.html">yeah</a>. </p>
<p>tags: <a href="./tag/foo.html">foo</a><a href="./tag/bar.html">bar</a><a href="./tag/foobar.html">foobar</a></p>
<p>In <a href="/category/yeah.html">yeah</a>. </p>
<p>tags: <a href="/tag/foo.html">foo</a><a href="/tag/bar.html">bar</a><a href="/tag/foobar.html">foobar</a></p>
</footer><!-- /.post-info --> <p class="first last">Multi-line metadata should be supported
as well as <strong>inline markup</strong>.</p>
<a class="readmore" href="./this-is-a-super-article.html">read more</a>
<a class="readmore" href="/this-is-a-super-article.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -193,7 +187,7 @@ as well as <strong>inline markup</strong>.</p>
<li><article class="hentry">
<header>
<h1><a href="./oh-yeah.html" rel="bookmark"
<h1><a href="/oh-yeah.html" rel="bookmark"
title="Permalink to Oh yeah !">Oh yeah !</a></h1>
</header>
@ -204,10 +198,10 @@ as well as <strong>inline markup</strong>.</p>
</abbr>
<address class="vcard author">
By <a class="url fn" href="./author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="./category/bar.html">bar</a>. </p>
<p>tags: <a href="./tag/oh.html">oh</a><a href="./tag/bar.html">bar</a><a href="./tag/yeah.html">yeah</a></p>
<p>In <a href="/category/bar.html">bar</a>. </p>
<p>tags: <a href="/tag/oh.html">oh</a><a href="/tag/bar.html">bar</a><a href="/tag/yeah.html">yeah</a></p>
</footer><!-- /.post-info --> <div class="section" id="why-not">
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
@ -215,7 +209,7 @@ YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
<a class="readmore" href="./oh-yeah.html">read more</a>
<a class="readmore" href="/oh-yeah.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
@ -223,7 +217,7 @@ YEAH !</p>
<li><article class="hentry">
<header>
<h1><a href="./unbelievable.html" rel="bookmark"
<h1><a href="/unbelievable.html" rel="bookmark"
title="Permalink to Unbelievable !">Unbelievable !</a></h1>
</header>
@ -233,19 +227,26 @@ YEAH !</p>
Fri 15 October 2010
</abbr>
<p>In <a href="./category/misc.html">misc</a>. </p>
<p>In <a href="/category/misc.html">misc</a>. </p>
</footer><!-- /.post-info --> <p>Or completely awesome. Depends the needs.</p>
<p><a class="reference external" href="./a-markdown-powered-article.html">a root-relative link to markdown-article</a>
<a class="reference external" href="./a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
<a class="readmore" href="./unbelievable.html">read more</a>
<a class="readmore" href="/unbelievable.html">read more</a>
</div><!-- /.entry-content -->
</article></li>
</ol><!-- /#posts-list -->
</section><!-- /#content -->
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -1,39 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Oh yeah !</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="./theme/css/main.css" type="text/css" />
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie.css"/>
<script src="./js/IE8.js" type="text/javascript"></script><![endif]-->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="all" href="./css/ie6.css"/><![endif]-->
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="./">A Pelican Blog </a></h1>
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="./pages/this-is-a-test-page.html">This is a test page</a></li>
<li class="active"><a href="./category/bar.html">bar</a></li>
<li ><a href="./category/cat1.html">cat1</a></li>
<li ><a href="./category/misc.html">misc</a></li>
<li ><a href="./category/yeah.html">yeah</a></li>
</ul></nav>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li class="active"><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="./oh-yeah.html" rel="bookmark"
<a href="/oh-yeah.html" rel="bookmark"
title="Permalink to Oh yeah !">Oh yeah !</a></h1>
</header>
@ -44,10 +38,10 @@
</abbr>
<address class="vcard author">
By <a class="url fn" href="./author/alexis-metaireau.html">Alexis Métaireau</a>
By <a class="url fn" href="/author/alexis-metaireau.html">Alexis Métaireau</a>
</address>
<p>In <a href="./category/bar.html">bar</a>. </p>
<p>tags: <a href="./tag/oh.html">oh</a><a href="./tag/bar.html">bar</a><a href="./tag/yeah.html">yeah</a></p>
<p>In <a href="/category/bar.html">bar</a>. </p>
<p>tags: <a href="/tag/oh.html">oh</a><a href="/tag/bar.html">bar</a><a href="/tag/yeah.html">yeah</a></p>
</footer><!-- /.post-info --> <div class="section" id="why-not">
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
@ -60,7 +54,14 @@ YEAH !</p>
</article>
</section>
<section id="extras" class="body">
</section><!-- /#extras -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Override url/save_as</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<h1 class="entry-title">Override url/save_as</h1>
<p>Test page which overrides save_as and url so that this page will be generated
at a custom location.</p>
</section>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more