mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merged from upstream/master
This commit is contained in:
commit
d73e393206
262 changed files with 5498 additions and 4504 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -11,3 +11,5 @@ tags
|
|||
.tox
|
||||
.coverage
|
||||
htmlcov
|
||||
six-*.egg/
|
||||
*.orig
|
||||
|
|
|
|||
24
.mailmap
Normal file
24
.mailmap
Normal 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>
|
||||
19
.travis.yml
19
.travis.yml
|
|
@ -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
40
CONTRIBUTING.rst
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
55
THANKS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Tests
|
||||
unittest2
|
||||
mock
|
||||
|
||||
# Optional Packages
|
||||
Markdown
|
||||
BeautifulSoup
|
||||
BeautifulSoup4
|
||||
lxml
|
||||
typogrify
|
||||
webassets
|
||||
|
|
@ -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)
|
||||
================
|
||||
|
|
|
|||
23
docs/conf.py
23
docs/conf.py
|
|
@ -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)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
114
docs/faq.rst
114
docs/faq.rst
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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::
|
||||
|
||||

|
||||
|
||||
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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
295
docs/plugins.rst
295
docs/plugins.rst
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----------
|
||||
|
||||
|
|
|
|||
|
|
@ -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'),)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from pelican import signals
|
||||
|
||||
def test(sender):
|
||||
print "%s initialized !!" % sender
|
||||
|
||||
def register():
|
||||
signals.initialized.connect(test)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
29
pelican/server.py
Normal 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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
2
pelican/tests/__init__.py
Normal file
2
pelican/tests/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import logging
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
8
pelican/tests/content/article_with_comments.html
Normal file
8
pelican/tests/content/article_with_comments.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
Body content
|
||||
<!-- This comment is included (including extra whitespace) -->
|
||||
</body>
|
||||
</html>
|
||||
6
pelican/tests/content/article_with_keywords.html
Normal file
6
pelican/tests/content/article_with_keywords.html
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>This is a super article !</title>
|
||||
<meta name="keywords" contents="foo, bar, foobar" />
|
||||
</head>
|
||||
</html>
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
=========================
|
||||
10
pelican/tests/content/article_with_mdown_extension.mdown
Normal file
10
pelican/tests/content/article_with_mdown_extension.mdown
Normal 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.
|
||||
15
pelican/tests/content/article_with_metadata.html
Normal file
15
pelican/tests/content/article_with_metadata.html
Normal 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>
|
||||
8
pelican/tests/content/article_with_null_attributes.html
Normal file
8
pelican/tests/content/article_with_null_attributes.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
Ensure that empty attributes are copied properly.
|
||||
<input name="test" disabled style="" />
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>This is a super article !</title>
|
||||
<meta name="Category" contents="Yeah" />
|
||||
</head>
|
||||
</html>
|
||||
48
pelican/tests/content/wordpress_content_decoded
Normal file
48
pelican/tests/content/wordpress_content_decoded
Normal 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>
|
||||
55
pelican/tests/content/wordpress_content_encoded
Normal file
55
pelican/tests/content/wordpress_content_encoded
Normal 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.
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
@ -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'),)
|
||||
|
|
@ -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">
|
||||
69
pelican/tests/output/basic/archives.html
Normal file
69
pelican/tests/output/basic/archives.html
Normal 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>
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
51
pelican/tests/output/basic/categories.html
Normal file
51
pelican/tests/output/basic/categories.html
Normal 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>
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
|
|
@ -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">
|
||||
53
pelican/tests/output/basic/override/index.html
Normal file
53
pelican/tests/output/basic/override/index.html
Normal 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
Loading…
Add table
Add a link
Reference in a new issue