mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
merge win-encoding
This commit is contained in:
commit
dd299d272b
56 changed files with 1031 additions and 375 deletions
|
|
@ -1,10 +1,8 @@
|
||||||
Jinja2>=2.4
|
# Tests
|
||||||
Pygments
|
|
||||||
docutils
|
|
||||||
feedgenerator
|
|
||||||
unittest2
|
unittest2
|
||||||
pytz
|
|
||||||
mock
|
mock
|
||||||
|
# Optional Packages
|
||||||
Markdown
|
Markdown
|
||||||
blinker
|
|
||||||
BeautifulSoup
|
BeautifulSoup
|
||||||
|
typogrify
|
||||||
|
webassets
|
||||||
|
|
@ -23,7 +23,7 @@ different projects.
|
||||||
|
|
||||||
To create a virtual environment, use the following syntax::
|
To create a virtual environment, use the following syntax::
|
||||||
|
|
||||||
$ mkvirtualenv pelican
|
$ mkvirtualenv pelican
|
||||||
|
|
||||||
To clone the Pelican source::
|
To clone the Pelican source::
|
||||||
|
|
||||||
|
|
@ -65,5 +65,5 @@ Try to respect what is described in the `PEP8 specification
|
||||||
<http://www.python.org/dev/peps/pep-0008/>`_ when providing patches. This can be
|
<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
|
eased via the `pep8 <http://pypi.python.org/pypi/pep8>`_ or `flake8
|
||||||
<http://pypi.python.org/pypi/flake8/>`_ tools, the latter of which in
|
<http://pypi.python.org/pypi/flake8/>`_ tools, the latter of which in
|
||||||
particular will give you some useful hints about ways in which the
|
particular will give you some useful hints about ways in which the
|
||||||
code/formatting can be improved.
|
code/formatting can be improved.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ suggestions or problems you might have via IRC or the issue tracker.
|
||||||
If you want to contribute, please fork `the git repository
|
If you want to contribute, please fork `the git repository
|
||||||
<https://github.com/getpelican/pelican/>`_, create a new feature branch, make
|
<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
|
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
|
as possible. Please refer to the :doc:`How to Contribute <contribute>` section
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
You can also contribute by creating themes and improving the documentation.
|
You can also contribute by creating themes and improving the documentation.
|
||||||
|
|
@ -43,7 +43,7 @@ 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
|
Pygments adds some classes to the generated content. These classes are used by
|
||||||
themes to style code syntax highlighting via CSS. Specifically, you can
|
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 ``.codehilite pre``
|
||||||
class in your theme's CSS file. To see how various styles can be used to render
|
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
|
Django code, for example, you can use the demo `on the project website
|
||||||
<http://pygments.org/demo/15101/>`_.
|
<http://pygments.org/demo/15101/>`_.
|
||||||
|
|
@ -105,7 +105,7 @@ I'm getting a warning about feeds generated without SITEURL being set properly
|
||||||
In order to properly generate all URLs properly in Pelican you will need to set
|
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
|
``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
|
default Makefile provided by the `pelican-quickstart` bootstrap script to test
|
||||||
build your site, it's normal to see this warning since ``SITEURL`` is
|
build your site, it's normal to see this warning since ``SITEURL`` is
|
||||||
deliberately left undefined. If configured properly no other ``make`` commands
|
deliberately left undefined. If configured properly no other ``make`` commands
|
||||||
should result in this warning.
|
should result in this warning.
|
||||||
|
|
||||||
|
|
@ -124,5 +124,5 @@ setting names). Here is an exact list of the renamed setting names::
|
||||||
|
|
||||||
Older 2.x themes that referenced the old setting names may not link properly.
|
Older 2.x themes that referenced the old setting names may not link properly.
|
||||||
In order to rectify this, please update your theme for compatibility with 3.0+
|
In order to rectify this, please update your theme for compatibility with 3.0+
|
||||||
by changing the relevant values in your template files. For an example of
|
by changing the relevant values in your template files. For an example of
|
||||||
complete feed headers and usage please check out the ``simple`` theme.
|
complete feed headers and usage please check out the ``simple`` theme.
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,11 @@ Traductions
|
||||||
DEFAULT_LANG :
|
DEFAULT_LANG :
|
||||||
Le langage par défaut à utiliser. «*en*» par défaut ;
|
Le langage par défaut à utiliser. «*en*» par défaut ;
|
||||||
|
|
||||||
TRANSLATION_FEED :
|
TRANSLATION_FEED_ATOM :
|
||||||
Chemin du flux pour les traductions.
|
Chemin du flux Atom pour les traductions.
|
||||||
|
|
||||||
|
TRANSLATION_FEED_RSS :
|
||||||
|
Chemin du flux RSS pour les traductions.
|
||||||
|
|
||||||
|
|
||||||
Thèmes
|
Thèmes
|
||||||
|
|
@ -155,7 +158,5 @@ SITEURL :
|
||||||
STATIC_PATHS :
|
STATIC_PATHS :
|
||||||
Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ;
|
Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ;
|
||||||
|
|
||||||
|
MARKDOWN_EXTENSIONS :
|
||||||
|
Liste des extentions Markdown que vous souhaitez utiliser ;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ While the above is the simplest method, the recommended approach is to create
|
||||||
a virtual environment for Pelican via virtualenv_ and virtualenvwrapper_ before
|
a virtual environment for Pelican via virtualenv_ and virtualenvwrapper_ before
|
||||||
installing Pelican. Assuming you've followed the virtualenvwrapper
|
installing Pelican. Assuming you've followed the virtualenvwrapper
|
||||||
`installation <http://virtualenvwrapper.readthedocs.org/en/latest/install.html>`_
|
`installation <http://virtualenvwrapper.readthedocs.org/en/latest/install.html>`_
|
||||||
and `shell configuration
|
and `shell configuration
|
||||||
<http://virtualenvwrapper.readthedocs.org/en/latest/install.html#shell-startup-file>`_
|
<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
|
steps, you can then open a new terminal session and create a new virtual
|
||||||
environment for Pelican::
|
environment for Pelican::
|
||||||
|
|
@ -26,7 +26,7 @@ environment for Pelican::
|
||||||
|
|
||||||
Once the virtual environment has been created and activated, Pelican can be
|
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`` or ``easy_install`` as noted above. Alternatively, if
|
||||||
you have the project source, you can install Pelican using the distutils
|
you have the project source, you can install Pelican using the distutils
|
||||||
method::
|
method::
|
||||||
|
|
||||||
$ cd path-to-Pelican-source
|
$ cd path-to-Pelican-source
|
||||||
|
|
@ -69,6 +69,7 @@ Optionally:
|
||||||
|
|
||||||
* pygments, for syntax highlighting
|
* pygments, for syntax highlighting
|
||||||
* Markdown, for supporting Markdown as an input format
|
* Markdown, for supporting Markdown as an input format
|
||||||
|
* Typogrify, for typographical enhancements
|
||||||
|
|
||||||
Kickstart a blog
|
Kickstart a blog
|
||||||
================
|
================
|
||||||
|
|
@ -209,7 +210,7 @@ Pages
|
||||||
If you create a folder named ``pages``, all the files in it will be used to
|
If you create a folder named ``pages``, all the files in it will be used to
|
||||||
generate static pages.
|
generate static pages.
|
||||||
|
|
||||||
Then, use the ``DISPLAY_PAGES_ON_MENU`` setting, which will add all the pages to
|
Then, use the ``DISPLAY_PAGES_ON_MENU`` setting, which will add all the pages to
|
||||||
the menu.
|
the menu.
|
||||||
|
|
||||||
If you want to exclude any pages from being linked to or listed in the menu
|
If you want to exclude any pages from being linked to or listed in the menu
|
||||||
|
|
@ -219,7 +220,7 @@ things like making error pages that fit the generated theme of your site.
|
||||||
Importing an existing blog
|
Importing an existing blog
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
It is possible to import your blog from Dotclear, WordPress, and RSS feeds using
|
It is possible to import your blog from Dotclear, WordPress, and RSS feeds using
|
||||||
a simple script. See :ref:`import`.
|
a simple script. See :ref:`import`.
|
||||||
|
|
||||||
Translations
|
Translations
|
||||||
|
|
@ -277,14 +278,15 @@ For RestructuredText, use the code-block directive::
|
||||||
|
|
||||||
<indented code block goes here>
|
<indented code block goes here>
|
||||||
|
|
||||||
For Markdown, include the language identifier just above code blocks::
|
For Markdown, include the language identifier just above the code block,
|
||||||
|
indenting both the identifier and code::
|
||||||
|
|
||||||
|
A block of text.
|
||||||
|
|
||||||
:::identifier
|
:::identifier
|
||||||
<code goes here>
|
<code goes here>
|
||||||
|
|
||||||
(indent both the identifier and code)
|
|
||||||
|
|
||||||
The specified identifier (e.g. ``python``, ``ruby``) should be one that
|
The specified identifier (e.g. ``python``, ``ruby``) should be one that
|
||||||
appears on the `list of available lexers <http://pygments.org/docs/lexers/>`_.
|
appears on the `list of available lexers <http://pygments.org/docs/lexers/>`_.
|
||||||
|
|
||||||
Publishing drafts
|
Publishing drafts
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ BeatifulSoup can be installed like any other Python package::
|
||||||
|
|
||||||
$ pip install BeautifulSoup
|
$ pip install BeautifulSoup
|
||||||
|
|
||||||
For pandoc, install a package for your operating system from the
|
For pandoc, install a package for your operating system from the
|
||||||
`pandoc site <http://johnmacfarlane.net/pandoc/installing.html>`_.
|
`pandoc site <http://johnmacfarlane.net/pandoc/installing.html>`_.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ Pelican
|
||||||
|
|
||||||
Pelican is a static site generator, written in Python_.
|
Pelican is a static site generator, written in Python_.
|
||||||
|
|
||||||
* Write your weblog entries directly with your editor of choice (vim!)
|
* Write your weblog entries directly with your editor of choice (vim!)
|
||||||
in reStructuredText_ or Markdown_
|
in reStructuredText_ or Markdown_
|
||||||
* Includes a simple CLI tool to (re)generate the weblog
|
* Includes a simple CLI tool to (re)generate the weblog
|
||||||
* Easy to interface with DVCSes and web hooks
|
* Easy to interface with DVCSes and web hooks
|
||||||
|
|
@ -79,4 +79,4 @@ A French version of the documentation is available at :doc:`fr/index`.
|
||||||
.. _`Pelican documentation`: http://docs.getpelican.com/latest/
|
.. _`Pelican documentation`: http://docs.getpelican.com/latest/
|
||||||
.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
|
.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
|
||||||
.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican
|
.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican
|
||||||
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4
|
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ Take a look at the Markdown reader::
|
||||||
text = open(filename)
|
text = open(filename)
|
||||||
md = Markdown(extensions = ['meta', 'codehilite'])
|
md = Markdown(extensions = ['meta', 'codehilite'])
|
||||||
content = md.convert(text)
|
content = md.convert(text)
|
||||||
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
for name, value in md.Meta.items():
|
for name, value in md.Meta.items():
|
||||||
if name in _METADATA_FIELDS:
|
if name in _METADATA_FIELDS:
|
||||||
|
|
@ -81,7 +81,7 @@ both; only the existing ones will be called.
|
||||||
context is shared between all generators, and will be passed to the
|
context is shared between all generators, and will be passed to the
|
||||||
templates. For instance, the ``PageGenerator`` ``generate_context`` method
|
templates. For instance, the ``PageGenerator`` ``generate_context`` method
|
||||||
finds all the pages, transforms them into objects, and populates the context
|
finds all the pages, transforms them into objects, and populates the context
|
||||||
with them. Be careful *not* to output anything using this context at this
|
with them. Be careful *not* to output anything using this context at this
|
||||||
stage, as it is likely to change by the effect of other generators.
|
stage, as it is likely to change by the effect of other generators.
|
||||||
|
|
||||||
* ``generate_output`` is then called. And guess what is it made for? Oh,
|
* ``generate_output`` is then called. And guess what is it made for? Oh,
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ In this example, we can see there are three themes available: ``notmyidea``, ``s
|
||||||
Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get more verbose output, like this:
|
Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get more verbose output, like this:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pelican-themes -v -l
|
$ pelican-themes -v -l
|
||||||
/usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/notmyidea
|
/usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/notmyidea
|
||||||
/usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/two-column (symbolic link to `/home/skami/Dev/Python/pelican-themes/two-column')
|
/usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/two-column (symbolic link to `/home/skami/Dev/Python/pelican-themes/two-column')
|
||||||
|
|
@ -118,7 +118,7 @@ Creating symbolic links
|
||||||
To symbolically link a theme, you can use the ``-s`` or ``--symlink``, which works exactly as the ``--install`` option:
|
To symbolically link a theme, you can use the ``-s`` or ``--symlink``, which works exactly as the ``--install`` option:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# pelican-themes --symlink ~/Dev/Python/pelican-themes/two-column
|
# pelican-themes --symlink ~/Dev/Python/pelican-themes/two-column
|
||||||
|
|
||||||
In this example, the ``two-column`` theme is now symbolically linked to the Pelican themes path, so we can use it, but we can also modify it without having to reinstall it after each modification.
|
In this example, the ``two-column`` theme is now symbolically linked to the Pelican themes path, so we can use it, but we can also modify it without having to reinstall it after each modification.
|
||||||
|
|
@ -130,11 +130,11 @@ This is useful for theme development:
|
||||||
$ sudo pelican-themes -s ~/Dev/Python/pelican-themes/two-column
|
$ sudo pelican-themes -s ~/Dev/Python/pelican-themes/two-column
|
||||||
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
||||||
$ firefox /tmp/out/index.html
|
$ firefox /tmp/out/index.html
|
||||||
$ vim ~/Dev/Pelican/pelican-themes/two-coumn/static/css/main.css
|
$ vim ~/Dev/Pelican/pelican-themes/two-coumn/static/css/main.css
|
||||||
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
||||||
$ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-coumn/static/img/bg.png
|
$ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-coumn/static/img/bg.png
|
||||||
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
||||||
$ vim ~/Dev/Pelican/pelican-themes/two-coumn/templates/index.html
|
$ vim ~/Dev/Pelican/pelican-themes/two-coumn/templates/index.html
|
||||||
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
$ pelican ~/Blog/content -o /tmp/out -t two-column
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@ The ``--install``, ``--remove`` and ``--symlink`` option are not mutually exclus
|
||||||
--symlink ~/Dev/Python/pelican-themes/two-column \
|
--symlink ~/Dev/Python/pelican-themes/two-column \
|
||||||
--verbose
|
--verbose
|
||||||
|
|
||||||
In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr``
|
In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr``
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
196
docs/plugins.rst
196
docs/plugins.rst
|
|
@ -16,7 +16,7 @@ To load plugins, you have to specify them in your settings file. You have two
|
||||||
ways to do so.
|
ways to do so.
|
||||||
Either by specifying strings with the path to the callables::
|
Either by specifying strings with the path to the callables::
|
||||||
|
|
||||||
PLUGINS = ['pelican.plugins.gravatar',]
|
PLUGINS = ['pelican.plugins.gravatar',]
|
||||||
|
|
||||||
Or by importing them and adding them to the list::
|
Or by importing them and adding them to the list::
|
||||||
|
|
||||||
|
|
@ -48,35 +48,71 @@ which you map the signals to your plugin logic. Let's take a simple example::
|
||||||
signals.initialized.connect(test)
|
signals.initialized.connect(test)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List of signals
|
List of signals
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Here is the list of currently implemented signals:
|
Here is the list of currently implemented signals:
|
||||||
|
|
||||||
========================= ============================ =========================================
|
============================= ============================ ===========================================================================
|
||||||
Signal Arguments Description
|
Signal Arguments Description
|
||||||
========================= ============================ =========================================
|
============================= ============================ ===========================================================================
|
||||||
initialized pelican object
|
initialized pelican object
|
||||||
article_generate_context article_generator, metadata
|
finalized pelican object invoked after all the generators are executed and just before pelican exits
|
||||||
article_generator_init article_generator invoked in the ArticlesGenerator.__init__
|
usefull for custom post processing actions, such as:
|
||||||
pages_generate_context pages_generator, metadata
|
- minifying js/css assets.
|
||||||
pages_generator_init pages_generator invoked in the PagesGenerator.__init__
|
- notify/ping search engines with an updated sitemap.
|
||||||
========================= ============================ =========================================
|
article_generate_context article_generator, metadata
|
||||||
|
article_generator_init article_generator invoked in the ArticlesGenerator.__init__
|
||||||
|
article_generator_finalized article_generator invoked at the end of ArticlesGenerator.generate_context
|
||||||
|
get_generators generators invoked in Pelican.get_generator_classes,
|
||||||
|
can return a Generator, or several
|
||||||
|
generator in a tuple or in a list.
|
||||||
|
pages_generate_context pages_generator, metadata
|
||||||
|
pages_generator_init pages_generator invoked in the PagesGenerator.__init__
|
||||||
|
============================= ============================ ===========================================================================
|
||||||
|
|
||||||
The list is currently small, don't hesitate to add signals and make a pull
|
The list is currently small, don't hesitate to add signals and make a pull
|
||||||
request if you need them!
|
request if you need them!
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The signal ``content_object_init`` can send different type of object as
|
||||||
|
argument. If you want to register only one type of object then you will
|
||||||
|
need to specify the sender when you are connecting to the signal.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from pelican import signals
|
||||||
|
from pelican import contents
|
||||||
|
|
||||||
|
def test(sender, instance):
|
||||||
|
print "%s : %s content initialized !!" % (sender, instance)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
signals.content_object_init.connect(test, sender=contents.Article)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List of plugins
|
List of plugins
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Not all the list are described here, but a few of them have been extracted from
|
The following plugins are currently included with Pelican under ``pelican.plugins``:
|
||||||
the Pelican core and provided in ``pelican.plugins``. They are described here:
|
|
||||||
|
|
||||||
Tag cloud
|
* `GitHub activity`_
|
||||||
---------
|
* `Global license`_
|
||||||
|
* `Gravatar`_
|
||||||
|
* `HTML tags for reStructuredText`_
|
||||||
|
* `Related posts`_
|
||||||
|
* `Sitemap`_
|
||||||
|
|
||||||
Translation
|
Ideas for plugins that haven't been written yet:
|
||||||
-----------
|
|
||||||
|
* Tag cloud
|
||||||
|
* Translation
|
||||||
|
|
||||||
|
Plugin descriptions
|
||||||
|
===================
|
||||||
|
|
||||||
GitHub activity
|
GitHub activity
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -108,3 +144,131 @@ variable, as in the example::
|
||||||
|
|
||||||
``github_activity`` is a list of lists. The first element is the title
|
``github_activity`` is a list of lists. The first element is the title
|
||||||
and the second element is the raw HTML from GitHub.
|
and the second element is the raw HTML from GitHub.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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, it 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 informations 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Some history about Pelican
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
This page comes from a report the original author (Alexis Métaireau) wrote
|
This page comes from a report the original author (Alexis Métaireau) wrote
|
||||||
right after writing Pelican, in December 2010. The information may not be
|
right after writing Pelican, in December 2010. The information may not be
|
||||||
up-to-date.
|
up-to-date.
|
||||||
|
|
||||||
Pelican is a simple static blog generator. It parses markup files
|
Pelican is a simple static blog generator. It parses markup files
|
||||||
|
|
@ -113,7 +113,7 @@ concepts. Here is what happens when calling the ``generate_context``
|
||||||
method:
|
method:
|
||||||
|
|
||||||
* Read the folder “path”, looking for restructured text files, load
|
* Read the folder “path”, looking for restructured text files, load
|
||||||
each of them, and construct a content object (``Article``) with it. To do so,
|
each of them, and construct a content object (``Article``) with it. To do so,
|
||||||
use ``Reader`` objects.
|
use ``Reader`` objects.
|
||||||
* Update the ``context`` with all those articles.
|
* Update the ``context`` with all those articles.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ False, None, etc.), dictionaries, or tuples should *not* be enclosed in
|
||||||
quotation marks. All other values (i.e., strings) *must* be enclosed in
|
quotation marks. All other values (i.e., strings) *must* be enclosed in
|
||||||
quotation marks.
|
quotation marks.
|
||||||
|
|
||||||
|
Unless otherwise specified, settings that refer to paths can be either absolute or relative to the
|
||||||
|
configuration file.
|
||||||
|
|
||||||
The settings you define in the configuration file will be passed to the
|
The settings you define in the configuration file will be passed to the
|
||||||
templates, which allows you to use your settings to add site-wide content.
|
templates, which allows you to use your settings to add site-wide content.
|
||||||
|
|
||||||
|
|
@ -43,9 +46,12 @@ Setting name (default value) What doe
|
||||||
If tuple object, it will instead generate the
|
If tuple object, it will instead generate the
|
||||||
default datetime object by passing the tuple to
|
default datetime object by passing the tuple to
|
||||||
the datetime.datetime constructor.
|
the datetime.datetime constructor.
|
||||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
|
||||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
|
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
|
||||||
generating new files.
|
generating new files.
|
||||||
|
`FILES_TO_COPY` (``()``) A list of files 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.
|
||||||
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
||||||
here or a single string representing one locale.
|
here or a single string representing one locale.
|
||||||
When providing a list, all the locales will be tried
|
When providing a list, all the locales will be tried
|
||||||
|
|
@ -58,14 +64,20 @@ Setting name (default value) What doe
|
||||||
Python-Markdown documentation for a complete list of
|
Python-Markdown documentation for a complete list of
|
||||||
supported extensions.
|
supported extensions.
|
||||||
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
|
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
|
||||||
`PATH` (``None``) Path to look at for input files.
|
`PATH` (``None``) Path to content directory to be processed by Pelican.
|
||||||
`PAGE_DIR` (``'pages'``) Directory to look at for pages.
|
`PAGE_DIR` (``'pages'``) Directory to look at for pages, relative to `PATH`.
|
||||||
`PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages.
|
`PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages.
|
||||||
`ARTICLE_DIR` (``''``) Directory to look at for articles.
|
`ARTICLE_DIR` (``''``) Directory to look at for articles, relative to `PATH`.
|
||||||
`ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles.
|
`ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles.
|
||||||
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
||||||
of your documents. You will need to install
|
of your documents. You will need to install
|
||||||
`rst2pdf`.
|
`rst2pdf`.
|
||||||
|
`OUTPUT_SOURCES` (``False``) Set to True if you want to copy the articles and pages in their
|
||||||
|
original format (e.g. Markdown or ReStructeredText) to the
|
||||||
|
specified OUTPUT_PATH.
|
||||||
|
`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
|
`RELATIVE_URLS` (``True``) Defines whether Pelican should use document-relative URLs or
|
||||||
not. If set to ``False``, Pelican will use the SITEURL
|
not. If set to ``False``, Pelican will use the SITEURL
|
||||||
setting to construct absolute URLs.
|
setting to construct absolute URLs.
|
||||||
|
|
@ -100,7 +112,12 @@ Setting name (default value) What doe
|
||||||
This only applies if your content does not otherwise
|
This only applies if your content does not otherwise
|
||||||
specify a summary. Setting to None will cause the summary
|
specify a summary. Setting to None will cause the summary
|
||||||
to be a copy of the original content.
|
to be a copy of the original content.
|
||||||
|
`EXTRA_TEMPLATES_PATHS` (``[]``) A list of paths you want Jinja2 to look for the templates.
|
||||||
|
Can be used to separate templates from the theme.
|
||||||
|
Example: projects, resume, profile ...
|
||||||
|
This templates need to use ``DIRECT_TEMPLATES`` setting
|
||||||
|
|
||||||
|
`MARKDOWN_EXTENSIONS` (``['toc',]``) A list of any Markdown extensions you want to use.
|
||||||
===================================================================== =====================================================================
|
===================================================================== =====================================================================
|
||||||
|
|
||||||
.. [#] Default is the system locale.
|
.. [#] Default is the system locale.
|
||||||
|
|
@ -144,37 +161,37 @@ Also, you can use other file metadata attributes as well:
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
* ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'
|
* ARTICLE_URL = ``'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'``
|
||||||
* ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'
|
* ARTICLE_SAVE_AS = ``'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'``
|
||||||
|
|
||||||
This would save your articles in something like '/posts/2011/Aug/07/sample-post/index.html',
|
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/'.
|
and the URL to this would be '/posts/2011/Aug/07/sample-post/'.
|
||||||
|
|
||||||
================================================ =====================================================
|
==================================================== =====================================================
|
||||||
Setting name (default value) what does it do?
|
Setting name (default value) What does it do?
|
||||||
================================================ =====================================================
|
==================================================== =====================================================
|
||||||
`ARTICLE_URL` ('{slug}.html') The URL to refer to an ARTICLE.
|
`ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an ARTICLE.
|
||||||
`ARTICLE_SAVE_AS` ('{slug}.html') The place where we will save an article.
|
`ARTICLE_SAVE_AS` (``'{slug}.html'``) The place where we will save an article.
|
||||||
`ARTICLE_LANG_URL` ('{slug}-{lang}.html') The URL to refer to an ARTICLE which doesn't use the
|
`ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an ARTICLE which doesn't use the
|
||||||
default language.
|
default language.
|
||||||
`ARTICLE_LANG_SAVE_AS` ('{slug}-{lang}.html' The place where we will save an article which
|
`ARTICLE_LANG_SAVE_AS` (``'{slug}-{lang}.html'``) The place where we will save an article which
|
||||||
doesn't use the default language.
|
doesn't use the default language.
|
||||||
`PAGE_URL` ('pages/{slug}.html') The URL we will use to link to a page.
|
`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.
|
||||||
`PAGE_LANG_URL` ('pages/{slug}-{lang}.html') The URL we will use to link to a page which doesn't
|
`PAGE_LANG_URL` (``'pages/{slug}-{lang}.html'``) The URL we will use to link to a page which doesn't
|
||||||
use the default language.
|
use the default language.
|
||||||
`PAGE_LANG_SAVE_AS` ('pages/{slug}-{lang}.html') The location we will save the page which doesn't
|
`PAGE_LANG_SAVE_AS` (``'pages/{slug}-{lang}.html'``) The location we will save the page which doesn't
|
||||||
use the default language.
|
use the default language.
|
||||||
`AUTHOR_URL` ('author/{name}.html') The URL to use for an author.
|
`AUTHOR_URL` (``'author/{name}.html'``) The URL to use for an author.
|
||||||
`AUTHOR_SAVE_AS` ('author/{name}.html') The location to save an author.
|
`AUTHOR_SAVE_AS` (``'author/{name}.html'``) The location to save an author.
|
||||||
`CATEGORY_URL` ('category/{name}.html') The URL to use for a category.
|
`CATEGORY_URL` (``'category/{name}.html'``) The URL to use for a category.
|
||||||
`CATEGORY_SAVE_AS` ('category/{name}.html') The location to save a category.
|
`CATEGORY_SAVE_AS` (``'category/{name}.html'``) The location to save a category.
|
||||||
`TAG_URL` ('tag/{name}.html') The URL to use for a tag.
|
`TAG_URL` (``'tag/{name}.html'``) The URL to use for a tag.
|
||||||
`TAG_SAVE_AS` ('tag/{name}.html') The location to save the tag page.
|
`TAG_SAVE_AS` (``'tag/{name}.html'``) The location to save the tag page.
|
||||||
`<DIRECT_TEMPLATE_NAME>_SAVE_AS` The location to save content generated from direct
|
`<DIRECT_TEMPLATE_NAME>_SAVE_AS` The location to save content generated from direct
|
||||||
templates. Where <DIRECT_TEMPLATE_NAME> is the
|
templates. Where <DIRECT_TEMPLATE_NAME> is the
|
||||||
upper case template name.
|
upper case template name.
|
||||||
================================================ =====================================================
|
==================================================== =====================================================
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
@ -197,14 +214,14 @@ Have a look at `the wikipedia page`_ to get a list of valid timezone values.
|
||||||
Date format and locale
|
Date format and locale
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
If no DATE_FORMAT is set, fall back to DEFAULT_DATE_FORMAT. If you need to
|
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
|
maintain multiple languages with different date formats, you can set this dict
|
||||||
using language name (``lang`` in your posts) as key. Regarding available format
|
using language name (``lang`` in your posts) as key. Regarding available format
|
||||||
codes, see `strftime document of python`_ :
|
codes, see `strftime document of python`_ :
|
||||||
|
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
|
|
||||||
DATE_FORMAT = {
|
DATE_FORMATS = {
|
||||||
'en': '%a, %d %b %Y',
|
'en': '%a, %d %b %Y',
|
||||||
'jp': '%Y-%m-%d(%a)',
|
'jp': '%Y-%m-%d(%a)',
|
||||||
}
|
}
|
||||||
|
|
@ -223,13 +240,13 @@ above:
|
||||||
|
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
# On Unix/Linux
|
# On Unix/Linux
|
||||||
DATE_FORMAT = {
|
DATE_FORMATS = {
|
||||||
'en': ('en_US','%a, %d %b %Y'),
|
'en': ('en_US','%a, %d %b %Y'),
|
||||||
'jp': ('ja_JP','%Y-%m-%d(%a)'),
|
'jp': ('ja_JP','%Y-%m-%d(%a)'),
|
||||||
}
|
}
|
||||||
|
|
||||||
# On Windows
|
# On Windows
|
||||||
DATE_FORMAT = {
|
DATE_FORMATS = {
|
||||||
'en': ('usa','%a, %d %b %Y'),
|
'en': ('usa','%a, %d %b %Y'),
|
||||||
'jp': ('jpn','%Y-%m-%d(%a)'),
|
'jp': ('jpn','%Y-%m-%d(%a)'),
|
||||||
}
|
}
|
||||||
|
|
@ -313,10 +330,10 @@ You can use the following settings to configure the pagination.
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
Setting name (default value) What does it do?
|
Setting name (default value) What does it do?
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
`DEFAULT_ORPHANS` (0) The minimum number of articles allowed on the
|
`DEFAULT_ORPHANS` (``0``) The minimum number of articles allowed on the
|
||||||
last page. Use this when you don't want to
|
last page. Use this when you don't want to
|
||||||
have a last page with very few articles.
|
have a last page with very few articles.
|
||||||
`DEFAULT_PAGINATION` (False) The maximum number of articles to include on a
|
`DEFAULT_PAGINATION` (``False``) The maximum number of articles to include on a
|
||||||
page, not including orphans. False to disable
|
page, not including orphans. False to disable
|
||||||
pagination.
|
pagination.
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
|
|
@ -330,9 +347,9 @@ following settings.
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
Setting name (default value) What does it do?
|
Setting name (default value) What does it do?
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
`TAG_CLOUD_STEPS` (4) Count of different font sizes in the tag
|
`TAG_CLOUD_STEPS` (``4``) Count of different font sizes in the tag
|
||||||
cloud.
|
cloud.
|
||||||
`TAG_CLOUD_MAX_ITEMS` (100) Maximum number of tags in the cloud.
|
`TAG_CLOUD_MAX_ITEMS` (``100``) Maximum number of tags in the cloud.
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
|
|
||||||
The default theme does not support tag clouds, but it is pretty easy to add::
|
The default theme does not support tag clouds, but it is pretty easy to add::
|
||||||
|
|
@ -352,12 +369,13 @@ Translations
|
||||||
Pelican offers a way to translate articles. See the Getting Started section for
|
Pelican offers a way to translate articles. See the Getting Started section for
|
||||||
more information.
|
more information.
|
||||||
|
|
||||||
================================================ =====================================================
|
===================================================== =====================================================
|
||||||
Setting name (default value) What does it do?
|
Setting name (default value) What does it do?
|
||||||
================================================ =====================================================
|
===================================================== =====================================================
|
||||||
`DEFAULT_LANG` (``'en'``) The default language to use.
|
`DEFAULT_LANG` (``'en'``) The default language to use.
|
||||||
`TRANSLATION_FEED` ('feeds/all-%s.atom.xml'[3]_) Where to put the feed for translations.
|
`TRANSLATION_FEED_ATOM` ('feeds/all-%s.atom.xml'[3]_) Where to put the Atom feed for translations.
|
||||||
================================================ =====================================================
|
`TRANSLATION_FEED_RSS` (``None``, i.e. no RSS) Where to put the RSS feed for translations.
|
||||||
|
===================================================== =====================================================
|
||||||
|
|
||||||
.. [3] %s is the language
|
.. [3] %s is the language
|
||||||
|
|
||||||
|
|
@ -373,19 +391,19 @@ Setting name (default value) What does it do?
|
||||||
alphabetical order; default lists alphabetically.)
|
alphabetical order; default lists alphabetically.)
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
|
|
||||||
Theming
|
Themes
|
||||||
=======
|
======
|
||||||
|
|
||||||
Theming is addressed in a dedicated section (see :ref:`theming-pelican`).
|
Creating Pelican themes is addressed in a dedicated section (see :ref:`theming-pelican`).
|
||||||
However, here are the settings that are related to theming.
|
However, here are the settings that are related to themes.
|
||||||
|
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
Setting name (default value) What does it do?
|
Setting name (default value) What does it do?
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
`THEME` Theme to use to produce the output. Can be the
|
`THEME` Theme to use to produce the output. Can be a relative
|
||||||
complete static path to a theme folder, or
|
or absolute path to a theme folder, or the name of a
|
||||||
chosen between the list of default themes (see
|
default theme or a theme installed via
|
||||||
below)
|
``pelican-themes`` (see below).
|
||||||
`THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default
|
`THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default
|
||||||
value is `static`, but if your theme has
|
value is `static`, but if your theme has
|
||||||
other static paths, you can put them here.
|
other static paths, you can put them here.
|
||||||
|
|
@ -393,22 +411,32 @@ Setting name (default value) What does it do?
|
||||||
`WEBASSETS` (``False``) Asset management with `webassets` (see below)
|
`WEBASSETS` (``False``) Asset management with `webassets` (see below)
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
|
|
||||||
By default, two themes are available. You can specify them using the `-t` option:
|
|
||||||
|
By default, two themes are available. You can specify them using the `THEME` setting or by passing the
|
||||||
|
``-t`` option to the ``pelican`` command:
|
||||||
|
|
||||||
* notmyidea
|
* notmyidea
|
||||||
* simple (a synonym for "full text" :)
|
* simple (a synonym for "plain text" :)
|
||||||
|
|
||||||
You can define your own theme too, and specify its placement in the same
|
|
||||||
manner. (Be sure to specify the full absolute path to it.)
|
|
||||||
|
|
||||||
Here is :doc:`a guide on how to create your theme <themes>`
|
|
||||||
|
|
||||||
You can find a list of themes at http://github.com/getpelican/pelican-themes.
|
|
||||||
|
|
||||||
|
There are a number of other themes available at http://github.com/getpelican/pelican-themes.
|
||||||
Pelican comes with :doc:`pelican-themes`, a small script for managing themes.
|
Pelican comes with :doc:`pelican-themes`, a small script for managing themes.
|
||||||
|
|
||||||
The `notmyidea` theme can make good use of the following settings. I recommend
|
You can define your own theme, either by starting from scratch or by duplicating
|
||||||
using them in your themes as well.
|
and modifying a pre-existing theme. Here is :doc:`a guide on how to create your theme <themes>`.
|
||||||
|
|
||||||
|
Following are example ways to specify your preferred theme::
|
||||||
|
|
||||||
|
# Specify name of a built-in theme
|
||||||
|
THEME = "notmyidea"
|
||||||
|
# Specify name of a theme installed via the pelican-themes tool
|
||||||
|
THEME = "chunk"
|
||||||
|
# Specify a customized theme, via path relative to the settings file
|
||||||
|
THEME = "themes/mycustomtheme"
|
||||||
|
# Specify a customized theme, via absolute path
|
||||||
|
THEME = "~/projects/mysite/themes/mycustomtheme"
|
||||||
|
|
||||||
|
The built-in `notmyidea` theme can make good use of the following settings. Feel
|
||||||
|
free to use them in your themes as well.
|
||||||
|
|
||||||
======================= =======================================================
|
======================= =======================================================
|
||||||
Setting name What does it do ?
|
Setting name What does it do ?
|
||||||
|
|
@ -444,26 +472,27 @@ adding the following to your configuration::
|
||||||
Asset management
|
Asset management
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The `WEBASSETS` setting allows to use the `webassets`_ module to manage assets
|
The `WEBASSETS` setting allows you to use the `webassets`_ module to manage
|
||||||
(css, js). The module must first be installed::
|
assets such as CSS and JS files. The module must first be installed::
|
||||||
|
|
||||||
pip install webassets
|
pip install webassets
|
||||||
|
|
||||||
`webassets` allows to concatenate your assets and to use almost all of the
|
The `webassets` module allows you to perform a number of useful asset management
|
||||||
hype tools of the moment (see the `documentation`_):
|
functions, including:
|
||||||
|
|
||||||
* css minifier (`cssmin`, `yuicompressor`, ...)
|
* CSS minifier (`cssmin`, `yuicompressor`, ...)
|
||||||
* css compiler (`less`, `sass`, ...)
|
* CSS compiler (`less`, `sass`, ...)
|
||||||
* js minifier (`uglifyjs`, `yuicompressor`, `closure`, ...)
|
* JS minifier (`uglifyjs`, `yuicompressor`, `closure`, ...)
|
||||||
|
|
||||||
Others filters include gzip compression, integration of images in css with
|
Others filters include gzip compression, integration of images in CSS via data
|
||||||
`datauri` and more. Webassets also append a version identifier to your asset
|
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
|
URL to convince browsers to download new versions of your assets when you use
|
||||||
far future expires headers.
|
far-future expires headers. Please refer to the `webassets documentation`_ for
|
||||||
|
more information.
|
||||||
|
|
||||||
When using it with Pelican, `webassets` is configured to process assets in the
|
When using with Pelican, `webassets` is configured to process assets in the
|
||||||
``OUTPUT_PATH/theme`` directory. You can use it in your templates with a
|
``OUTPUT_PATH/theme`` directory. You can use `webassets` in your templates by
|
||||||
template tag, for example:
|
including one or more template tags. For example...
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: jinja
|
||||||
|
|
||||||
|
|
@ -471,43 +500,43 @@ template tag, for example:
|
||||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
|
|
||||||
will produce a minified css file with the version identifier:
|
... will produce a minified css file with a version identifier:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<link href="http://{SITEURL}/theme/css/style.min.css?b3a7c807" rel="stylesheet">
|
<link href="http://{SITEURL}/theme/css/style.min.css?b3a7c807" rel="stylesheet">
|
||||||
|
|
||||||
The filters can be combined, for example to use the `sass` compiler and minify
|
These filters can be combined. Here is an example that uses the SASS compiler
|
||||||
the output::
|
and minifies the output:
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: jinja
|
||||||
|
|
||||||
{% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %}
|
{% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %}
|
||||||
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
<link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
|
|
||||||
Another example for javascript:
|
Another example for Javascript:
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: jinja
|
||||||
|
|
||||||
{% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
|
{% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
|
||||||
<script src="{{ ASSETS_URL }}"></script>
|
<script src="{{ ASSET_URL }}"></script>
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
|
|
||||||
will produce a minified and gzipped js file:
|
The above will produce a minified and gzipped JS file:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<script src="http://{SITEURL}/theme/js/packed.js?00703b9d"></script>
|
<script src="http://{SITEURL}/theme/js/packed.js?00703b9d"></script>
|
||||||
|
|
||||||
Pelican's debug mode is propagated to webassets to disable asset packaging,
|
Pelican's debug mode is propagated to `webassets` to disable asset packaging
|
||||||
and instead work with the uncompressed assets. However, this also means that
|
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
|
the LESS and SASS files are not compiled. This should be fixed in a future
|
||||||
version of webassets (cf. the related `bug report
|
version of `webassets` (cf. the related `bug report
|
||||||
<https://github.com/getpelican/pelican/issues/481>`_).
|
<https://github.com/getpelican/pelican/issues/481>`_).
|
||||||
|
|
||||||
.. _webassets: https://github.com/miracle2k/webassets
|
.. _webassets: https://github.com/miracle2k/webassets
|
||||||
.. _documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
|
.. _webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
|
||||||
|
|
||||||
Example settings
|
Example settings
|
||||||
================
|
================
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ To make your own theme, you must follow the following structure::
|
||||||
* `templates` contains all the templates that will be used to generate the content.
|
* `templates` contains all the templates that will be used to generate the content.
|
||||||
I've just put the mandatory templates here; you can define your own if it helps
|
I've just put the mandatory templates here; you can define your own if it helps
|
||||||
you keep things organized while creating your theme.
|
you keep things organized while creating your theme.
|
||||||
|
|
||||||
Templates and variables
|
Templates and variables
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ This document describes which templates should exist in a theme, and which
|
||||||
variables will be passed to each template at generation time.
|
variables will be passed to each template at generation time.
|
||||||
|
|
||||||
All templates will receive the variables defined in your settings file, if they
|
All templates will receive the variables defined in your settings file, if they
|
||||||
are in all-caps. You can access them directly.
|
are in all-caps. You can access them directly.
|
||||||
|
|
||||||
Common variables
|
Common variables
|
||||||
----------------
|
----------------
|
||||||
|
|
@ -55,14 +55,14 @@ All of these settings will be available to all templates.
|
||||||
Variable Description
|
Variable Description
|
||||||
============= ===================================================
|
============= ===================================================
|
||||||
articles The list of articles, ordered descending by date
|
articles The list of articles, ordered descending by date
|
||||||
All the elements are `Article` objects, so you can
|
All the elements are `Article` objects, so you can
|
||||||
access their attributes (e.g. title, summary, author
|
access their attributes (e.g. title, summary, author
|
||||||
etc.)
|
etc.)
|
||||||
dates The same list of articles, but ordered by date,
|
dates The same list of articles, but ordered by date,
|
||||||
ascending
|
ascending
|
||||||
tags A key-value dict containing the tags (the keys) and
|
tags A key-value dict containing the tags (the keys) and
|
||||||
the list of respective articles (the values)
|
the list of respective articles (the values)
|
||||||
categories A key-value dict containing the categories (keys)
|
categories A key-value dict containing the categories (keys)
|
||||||
and the list of respective articles (values)
|
and the list of respective articles (values)
|
||||||
pages The list of pages
|
pages The list of pages
|
||||||
============= ===================================================
|
============= ===================================================
|
||||||
|
|
@ -92,8 +92,8 @@ author.html
|
||||||
This template will be processed for each of the existing authors, with
|
This template will be processed for each of the existing authors, with
|
||||||
output generated at output/author/`author_name`.html.
|
output generated at output/author/`author_name`.html.
|
||||||
|
|
||||||
If pagination is active, subsequent pages will reside at
|
If pagination is active, subsequent pages will reside as defined by setting
|
||||||
output/author/`author_name``n`.html.
|
AUTHOR_SAVE_AS (`Default:` output/author/`author_name'n'`.html).
|
||||||
|
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
Variable Description
|
Variable Description
|
||||||
|
|
@ -108,8 +108,8 @@ dates_paginator A paginator object for the article list, ordered by
|
||||||
date, ascending.
|
date, ascending.
|
||||||
dates_page The current page of articles, ordered by date,
|
dates_page The current page of articles, ordered by date,
|
||||||
ascending.
|
ascending.
|
||||||
page_name 'author/`author_name`' -- useful for pagination
|
page_name AUTHOR_URL where everything after `{slug}` is
|
||||||
links
|
removed -- useful for pagination links
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
|
|
||||||
category.html
|
category.html
|
||||||
|
|
@ -118,8 +118,8 @@ category.html
|
||||||
This template will be processed for each of the existing categories, with
|
This template will be processed for each of the existing categories, with
|
||||||
output generated at output/category/`category_name`.html.
|
output generated at output/category/`category_name`.html.
|
||||||
|
|
||||||
If pagination is active, subsequent pages will reside at
|
If pagination is active, subsequent pages will reside as defined by setting
|
||||||
output/category/`category_name``n`.html.
|
CATEGORY_SAVE_AS (`Default:` output/category/`category_name'n'`.html).
|
||||||
|
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
Variable Description
|
Variable Description
|
||||||
|
|
@ -134,8 +134,8 @@ dates_paginator A paginator object for the list of articles,
|
||||||
ordered by date, ascending
|
ordered by date, ascending
|
||||||
dates_page The current page of articles, ordered by date,
|
dates_page The current page of articles, ordered by date,
|
||||||
ascending
|
ascending
|
||||||
page_name 'category/`category_name`' -- useful for pagination
|
page_name CATEGORY_URL where everything after `{slug}` is
|
||||||
links
|
removed -- useful for pagination links
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
|
|
||||||
article.html
|
article.html
|
||||||
|
|
@ -170,8 +170,8 @@ tag.html
|
||||||
This template will be processed for each tag, with corresponding .html files
|
This template will be processed for each tag, with corresponding .html files
|
||||||
saved as output/tag/`tag_name`.html.
|
saved as output/tag/`tag_name`.html.
|
||||||
|
|
||||||
If pagination is active, subsequent pages will reside at
|
If pagination is active, subsequent pages will reside as defined in setting
|
||||||
output/tag/`tag_name``n`.html.
|
TAG_SAVE_AS (`Default:` output/tag/`tag_name'n'`.html).
|
||||||
|
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
Variable Description
|
Variable Description
|
||||||
|
|
@ -182,11 +182,12 @@ dates Articles related to this tag, but ordered by date,
|
||||||
ascending
|
ascending
|
||||||
articles_paginator A paginator object for the list of articles
|
articles_paginator A paginator object for the list of articles
|
||||||
articles_page The current page of articles
|
articles_page The current page of articles
|
||||||
dates_paginator A paginator object for the list of articles,
|
dates_paginator A paginator object for the list of articles,
|
||||||
ordered by date, ascending
|
ordered by date, ascending
|
||||||
dates_page The current page of articles, ordered by date,
|
dates_page The current page of articles, ordered by date,
|
||||||
ascending
|
ascending
|
||||||
page_name 'tag/`tag_name`' -- useful for pagination links
|
page_name TAG_URL where everything after `{slug}` is removed
|
||||||
|
-- useful for pagination links
|
||||||
=================== ===================================================
|
=================== ===================================================
|
||||||
|
|
||||||
Feeds
|
Feeds
|
||||||
|
|
@ -202,7 +203,8 @@ Here is a complete list of the feed variables::
|
||||||
CATEGORY_FEED_RSS
|
CATEGORY_FEED_RSS
|
||||||
TAG_FEED_ATOM
|
TAG_FEED_ATOM
|
||||||
TAG_FEED_RSS
|
TAG_FEED_RSS
|
||||||
TRANSLATION_FEED
|
TRANSLATION_FEED_ATOM
|
||||||
|
TRANSLATION_FEED_RSS
|
||||||
|
|
||||||
|
|
||||||
Inheritance
|
Inheritance
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ file generator, we can take advantage of this.
|
||||||
|
|
||||||
User Pages
|
User Pages
|
||||||
----------
|
----------
|
||||||
GitHub allows you to create user pages in the form of ``username.github.com``.
|
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,
|
Whatever is created in the master branch will be published. For this purpose,
|
||||||
just the output generated by Pelican needs to pushed to GitHub.
|
just the output generated by Pelican needs to pushed to GitHub.
|
||||||
|
|
||||||
So given a repository containing your articles, just run Pelican over the posts
|
So given a repository containing your articles, just run Pelican over the posts
|
||||||
and deploy the master branch to GitHub::
|
and deploy the master branch to GitHub::
|
||||||
|
|
||||||
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
|
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
|
||||||
|
|
@ -35,7 +35,7 @@ really easy, which can be installed via::
|
||||||
|
|
||||||
$ pip install ghp-import
|
$ pip install ghp-import
|
||||||
|
|
||||||
Then, given a repository containing your articles, you would simply run
|
Then, given a repository containing your articles, you would simply run
|
||||||
Pelican and upload the output to GitHub::
|
Pelican and upload the output to GitHub::
|
||||||
|
|
||||||
$ pelican -s pelican.conf.py .
|
$ pelican -s pelican.conf.py .
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import copy
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -9,10 +8,12 @@ import argparse
|
||||||
from pelican import signals
|
from pelican import signals
|
||||||
|
|
||||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||||
StaticGenerator, PdfGenerator, LessCSSGenerator)
|
StaticGenerator, PdfGenerator,
|
||||||
|
LessCSSGenerator, SourceFileGenerator)
|
||||||
from pelican.log import init
|
from pelican.log import init
|
||||||
from pelican.settings import read_settings, _DEFAULT_CONFIG
|
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, files_changed, file_changed,
|
||||||
|
NoFilesError)
|
||||||
from pelican.writers import Writer
|
from pelican.writers import Writer
|
||||||
|
|
||||||
__major__ = 3
|
__major__ = 3
|
||||||
|
|
@ -24,42 +25,21 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Pelican(object):
|
class Pelican(object):
|
||||||
def __init__(self, settings=None, path=None, theme=None, output_path=None,
|
def __init__(self, settings):
|
||||||
markup=None, delete_outputdir=False, plugin_path=None):
|
"""
|
||||||
"""Read the settings, and performs some checks on the environment
|
Pelican initialisation, performs some checks on the environment before
|
||||||
before doing anything else.
|
doing anything else.
|
||||||
"""
|
"""
|
||||||
if settings is None:
|
|
||||||
settings = copy.deepcopy(_DEFAULT_CONFIG)
|
|
||||||
|
|
||||||
self.path = path or settings['PATH']
|
|
||||||
if not self.path:
|
|
||||||
raise Exception('You need to specify a path containing the content'
|
|
||||||
' (see pelican --help for more information)')
|
|
||||||
|
|
||||||
if self.path.endswith('/'):
|
|
||||||
self.path = self.path[:-1]
|
|
||||||
|
|
||||||
# define the default settings
|
# define the default settings
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
|
|
||||||
self._handle_deprecation()
|
self._handle_deprecation()
|
||||||
|
|
||||||
self.theme = theme or settings['THEME']
|
self.path = settings['PATH']
|
||||||
output_path = output_path or settings['OUTPUT_PATH']
|
self.theme = settings['THEME']
|
||||||
self.output_path = os.path.realpath(output_path)
|
self.output_path = settings['OUTPUT_PATH']
|
||||||
self.markup = markup or settings['MARKUP']
|
self.markup = settings['MARKUP']
|
||||||
self.delete_outputdir = delete_outputdir \
|
self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
|
||||||
or settings['DELETE_OUTPUT_DIRECTORY']
|
|
||||||
|
|
||||||
# find the theme in pelican.theme if the given one does not exists
|
|
||||||
if not os.path.exists(self.theme):
|
|
||||||
theme_path = os.sep.join([os.path.dirname(
|
|
||||||
os.path.abspath(__file__)), "themes/%s" % self.theme])
|
|
||||||
if os.path.exists(theme_path):
|
|
||||||
self.theme = theme_path
|
|
||||||
else:
|
|
||||||
raise Exception("Impossible to find the theme %s" % theme)
|
|
||||||
|
|
||||||
self.init_path()
|
self.init_path()
|
||||||
self.init_plugins()
|
self.init_plugins()
|
||||||
|
|
@ -78,7 +58,7 @@ class Pelican(object):
|
||||||
logger.debug("Loading plugin `{0}' ...".format(plugin))
|
logger.debug("Loading plugin `{0}' ...".format(plugin))
|
||||||
plugin = __import__(plugin, globals(), locals(), 'module')
|
plugin = __import__(plugin, globals(), locals(), 'module')
|
||||||
|
|
||||||
logger.debug("Registering plugin `{0}' ...".format(plugin.__name__))
|
logger.debug("Registering plugin `{0}'".format(plugin.__name__))
|
||||||
plugin.register()
|
plugin.register()
|
||||||
|
|
||||||
def _handle_deprecation(self):
|
def _handle_deprecation(self):
|
||||||
|
|
@ -139,8 +119,16 @@ class Pelican(object):
|
||||||
'Modify CATEGORY_FEED to CATEGORY_FEED_ATOM in your settings and '
|
'Modify CATEGORY_FEED to CATEGORY_FEED_ATOM in your settings and '
|
||||||
'theme for the same behavior. Temporarily setting '
|
'theme for the same behavior. Temporarily setting '
|
||||||
'CATEGORY_FEED_ATOM for backwards compatibility.')
|
'CATEGORY_FEED_ATOM for backwards compatibility.')
|
||||||
self.settings['CATEGORY_FEED_ATOM'] = self.settings['CATEGORY_FEED']
|
self.settings['CATEGORY_FEED_ATOM'] =\
|
||||||
|
self.settings['CATEGORY_FEED']
|
||||||
|
|
||||||
|
if self.settings.get('TRANSLATION_FEED', False):
|
||||||
|
logger.warning('Found deprecated `TRANSLATION_FEED` in settings. '
|
||||||
|
'Modify TRANSLATION_FEED to TRANSLATION_FEED_ATOM in your '
|
||||||
|
'settings and theme for the same behavior. Temporarily setting '
|
||||||
|
'TRANSLATION_FEED_ATOM for backwards compatibility.')
|
||||||
|
self.settings['TRANSLATION_FEED_ATOM'] =\
|
||||||
|
self.settings['TRANSLATION_FEED']
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the generators and return"""
|
"""Run the generators and return"""
|
||||||
|
|
@ -179,12 +167,28 @@ class Pelican(object):
|
||||||
if hasattr(p, 'generate_output'):
|
if hasattr(p, 'generate_output'):
|
||||||
p.generate_output(writer)
|
p.generate_output(writer)
|
||||||
|
|
||||||
|
signals.finalized.send(self)
|
||||||
|
|
||||||
def get_generator_classes(self):
|
def get_generator_classes(self):
|
||||||
generators = [StaticGenerator, ArticlesGenerator, PagesGenerator]
|
generators = [StaticGenerator, ArticlesGenerator, PagesGenerator]
|
||||||
if self.settings['PDF_GENERATOR']:
|
if self.settings['PDF_GENERATOR']:
|
||||||
generators.append(PdfGenerator)
|
generators.append(PdfGenerator)
|
||||||
if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc
|
if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc
|
||||||
generators.append(LessCSSGenerator)
|
generators.append(LessCSSGenerator)
|
||||||
|
if self.settings['OUTPUT_SOURCES']:
|
||||||
|
generators.append(SourceFileGenerator)
|
||||||
|
|
||||||
|
for pair in signals.get_generators.send(self):
|
||||||
|
(funct, value) = pair
|
||||||
|
|
||||||
|
if not isinstance(value, (tuple, list)):
|
||||||
|
value = (value, )
|
||||||
|
|
||||||
|
for v in value:
|
||||||
|
if isinstance(v, type):
|
||||||
|
logger.debug('Found generator: {0}'.format(v))
|
||||||
|
generators.append(v)
|
||||||
|
|
||||||
return generators
|
return generators
|
||||||
|
|
||||||
def get_writer(self):
|
def get_writer(self):
|
||||||
|
|
@ -241,11 +245,26 @@ def parse_arguments():
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def get_instance(args):
|
def get_config(args):
|
||||||
markup = [a.strip().lower() for a in args.markup.split(',')]\
|
config = {}
|
||||||
if args.markup else None
|
if args.path:
|
||||||
|
config['PATH'] = os.path.abspath(os.path.expanduser(args.path))
|
||||||
|
if args.output:
|
||||||
|
config['OUTPUT_PATH'] = \
|
||||||
|
os.path.abspath(os.path.expanduser(args.output))
|
||||||
|
if args.markup:
|
||||||
|
config['MARKUP'] = [a.strip().lower() for a in args.markup.split(',')]
|
||||||
|
if args.theme:
|
||||||
|
abstheme = os.path.abspath(os.path.expanduser(args.theme))
|
||||||
|
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
|
||||||
|
return config
|
||||||
|
|
||||||
settings = read_settings(args.settings)
|
|
||||||
|
def get_instance(args):
|
||||||
|
|
||||||
|
settings = read_settings(args.settings, override=get_config(args))
|
||||||
|
|
||||||
cls = settings.get('PELICAN_CLASS')
|
cls = settings.get('PELICAN_CLASS')
|
||||||
if isinstance(cls, basestring):
|
if isinstance(cls, basestring):
|
||||||
|
|
@ -253,15 +272,12 @@ def get_instance(args):
|
||||||
module = __import__(module)
|
module = __import__(module)
|
||||||
cls = getattr(module, cls_name)
|
cls = getattr(module, cls_name)
|
||||||
|
|
||||||
return cls(settings, args.path, args.theme, args.output, markup,
|
return cls(settings)
|
||||||
args.delete_outputdir)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parse_arguments()
|
args = parse_arguments()
|
||||||
init(args.verbosity)
|
init(args.verbosity)
|
||||||
# Split the markup languages only if some have been given. Otherwise,
|
|
||||||
# populate the variable with None.
|
|
||||||
pelican = get_instance(args)
|
pelican = get_instance(args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -276,7 +292,7 @@ def main():
|
||||||
# have.
|
# have.
|
||||||
if files_changed(pelican.path, pelican.markup) or \
|
if files_changed(pelican.path, pelican.markup) or \
|
||||||
files_changed(pelican.theme, ['']):
|
files_changed(pelican.theme, ['']):
|
||||||
if files_found_error == False:
|
if not files_found_error:
|
||||||
files_found_error = True
|
files_found_error = True
|
||||||
pelican.run()
|
pelican.run()
|
||||||
|
|
||||||
|
|
@ -292,9 +308,11 @@ def main():
|
||||||
logger.warning("Keyboard interrupt, quitting.")
|
logger.warning("Keyboard interrupt, quitting.")
|
||||||
break
|
break
|
||||||
except NoFilesError:
|
except NoFilesError:
|
||||||
if files_found_error == True:
|
if files_found_error:
|
||||||
logger.warning("No valid files found in content. Nothing to generate.")
|
logger.warning("No valid files found in content. "
|
||||||
|
"Nothing to generate.")
|
||||||
files_found_error = False
|
files_found_error = False
|
||||||
|
time.sleep(1) # sleep to avoid cpu load
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Caught exception \"{}\". Reloading.".format(e)
|
"Caught exception \"{}\". Reloading.".format(e)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from sys import platform, stdin
|
||||||
|
|
||||||
from pelican.settings import _DEFAULT_CONFIG
|
from pelican.settings import _DEFAULT_CONFIG
|
||||||
from pelican.utils import slugify, truncate_html_words
|
from pelican.utils import slugify, truncate_html_words
|
||||||
|
from pelican import signals
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -106,6 +106,8 @@ class Page(object):
|
||||||
if 'summary' in metadata:
|
if 'summary' in metadata:
|
||||||
self._summary = metadata['summary']
|
self._summary = metadata['summary']
|
||||||
|
|
||||||
|
signals.content_object_init.send(self.__class__, instance=self)
|
||||||
|
|
||||||
def check_properties(self):
|
def check_properties(self):
|
||||||
"""test that each mandatory property is set."""
|
"""test that each mandatory property is set."""
|
||||||
for prop in self.mandatory_properties:
|
for prop in self.mandatory_properties:
|
||||||
|
|
@ -195,15 +197,23 @@ class URLWrapper(object):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def _from_settings(self, key):
|
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)
|
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
|
||||||
value = self.settings[setting]
|
value = self.settings[setting]
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
logger.warning(u'%s is set to %s' % (setting, value))
|
logger.warning(u'%s is set to %s' % (setting, value))
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return unicode(value).format(**self.as_dict())
|
if get_page_name:
|
||||||
|
return unicode(value[:value.find('{slug}') + len('{slug}')]).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'))
|
url = property(functools.partial(_from_settings, key='URL'))
|
||||||
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
|
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import logging
|
||||||
import datetime
|
import datetime
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from codecs import open
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
@ -16,7 +17,7 @@ from jinja2.exceptions import TemplateNotFound
|
||||||
|
|
||||||
from pelican.contents import Article, Page, Category, is_valid_content
|
from pelican.contents import Article, Page, Category, is_valid_content
|
||||||
from pelican.readers import read_file
|
from pelican.readers import read_file
|
||||||
from pelican.utils import copy, process_translations, open
|
from pelican.utils import copy, process_translations
|
||||||
from pelican import signals
|
from pelican import signals
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,8 +37,11 @@ class Generator(object):
|
||||||
|
|
||||||
# templates cache
|
# templates cache
|
||||||
self._templates = {}
|
self._templates = {}
|
||||||
self._templates_path = os.path.expanduser(
|
self._templates_path = []
|
||||||
os.path.join(self.theme, 'templates'))
|
self._templates_path.append(os.path.expanduser(
|
||||||
|
os.path.join(self.theme, 'templates')))
|
||||||
|
self._templates_path += self.settings.get('EXTRA_TEMPLATES_PATHS', [])
|
||||||
|
|
||||||
|
|
||||||
theme_path = os.path.dirname(os.path.abspath(__file__))
|
theme_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
@ -124,7 +128,8 @@ class ArticlesGenerator(Generator):
|
||||||
|
|
||||||
def generate_feeds(self, writer):
|
def generate_feeds(self, writer):
|
||||||
"""Generate the feeds from the current context, and output files."""
|
"""Generate the feeds from the current context, and output files."""
|
||||||
if self.settings.get('FEED_ATOM') is None and self.settings.get('FEED_RSS') is None:
|
if self.settings.get('FEED_ATOM') is None \
|
||||||
|
and self.settings.get('FEED_RSS') is None:
|
||||||
return
|
return
|
||||||
elif self.settings.get('SITEURL') is '':
|
elif self.settings.get('SITEURL') is '':
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|
@ -150,7 +155,8 @@ class ArticlesGenerator(Generator):
|
||||||
self.settings['CATEGORY_FEED_RSS'] % cat,
|
self.settings['CATEGORY_FEED_RSS'] % cat,
|
||||||
feed_type='rss')
|
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():
|
for tag, arts in self.tags.items():
|
||||||
arts.sort(key=attrgetter('date'), reverse=True)
|
arts.sort(key=attrgetter('date'), reverse=True)
|
||||||
if self.settings.get('TAG_FEED_ATOM'):
|
if self.settings.get('TAG_FEED_ATOM'):
|
||||||
|
|
@ -162,15 +168,21 @@ class ArticlesGenerator(Generator):
|
||||||
self.settings['TAG_FEED_RSS'] % tag,
|
self.settings['TAG_FEED_RSS'] % tag,
|
||||||
feed_type='rss')
|
feed_type='rss')
|
||||||
|
|
||||||
if self.settings.get('TRANSLATION_FEED'):
|
if self.settings.get('TRANSLATION_FEED_ATOM') or \
|
||||||
|
self.settings.get('TRANSLATION_FEED_RSS'):
|
||||||
translations_feeds = defaultdict(list)
|
translations_feeds = defaultdict(list)
|
||||||
for article in chain(self.articles, self.translations):
|
for article in chain(self.articles, self.translations):
|
||||||
translations_feeds[article.lang].append(article)
|
translations_feeds[article.lang].append(article)
|
||||||
|
|
||||||
for lang, items in translations_feeds.items():
|
for lang, items in translations_feeds.items():
|
||||||
items.sort(key=attrgetter('date'), reverse=True)
|
items.sort(key=attrgetter('date'), reverse=True)
|
||||||
writer.write_feed(items, self.context,
|
if self.settings.get('TRANSLATION_FEED_ATOM'):
|
||||||
self.settings['TRANSLATION_FEED'] % lang)
|
writer.write_feed(items, self.context,
|
||||||
|
self.settings['TRANSLATION_FEED_ATOM'] % lang)
|
||||||
|
if self.settings.get('TRANSLATION_FEED_RSS'):
|
||||||
|
writer.write_feed(items, self.context,
|
||||||
|
self.settings['TRANSLATION_FEED_RSS'] % lang,
|
||||||
|
feed_type='rss')
|
||||||
|
|
||||||
def generate_articles(self, write):
|
def generate_articles(self, write):
|
||||||
"""Generate the articles."""
|
"""Generate the articles."""
|
||||||
|
|
@ -188,7 +200,7 @@ class ArticlesGenerator(Generator):
|
||||||
save_as = self.settings.get("%s_SAVE_AS" % template.upper(),
|
save_as = self.settings.get("%s_SAVE_AS" % template.upper(),
|
||||||
'%s.html' % template)
|
'%s.html' % template)
|
||||||
if not save_as:
|
if not save_as:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
write(save_as, self.get_template(template),
|
write(save_as, self.get_template(template),
|
||||||
self.context, blog=True, paginated=paginated,
|
self.context, blog=True, paginated=paginated,
|
||||||
|
|
@ -203,7 +215,7 @@ class ArticlesGenerator(Generator):
|
||||||
write(tag.save_as, tag_template, self.context, tag=tag,
|
write(tag.save_as, tag_template, self.context, tag=tag,
|
||||||
articles=articles, dates=dates,
|
articles=articles, dates=dates,
|
||||||
paginated={'articles': articles, 'dates': dates},
|
paginated={'articles': articles, 'dates': dates},
|
||||||
page_name=u'tag/%s' % tag)
|
page_name=tag.page_name)
|
||||||
|
|
||||||
def generate_categories(self, write):
|
def generate_categories(self, write):
|
||||||
"""Generate category pages."""
|
"""Generate category pages."""
|
||||||
|
|
@ -213,7 +225,7 @@ class ArticlesGenerator(Generator):
|
||||||
write(cat.save_as, category_template, self.context,
|
write(cat.save_as, category_template, self.context,
|
||||||
category=cat, articles=articles, dates=dates,
|
category=cat, articles=articles, dates=dates,
|
||||||
paginated={'articles': articles, 'dates': dates},
|
paginated={'articles': articles, 'dates': dates},
|
||||||
page_name=u'category/%s' % cat)
|
page_name=cat.page_name)
|
||||||
|
|
||||||
def generate_authors(self, write):
|
def generate_authors(self, write):
|
||||||
"""Generate Author pages."""
|
"""Generate Author pages."""
|
||||||
|
|
@ -223,7 +235,7 @@ class ArticlesGenerator(Generator):
|
||||||
write(aut.save_as, author_template, self.context,
|
write(aut.save_as, author_template, self.context,
|
||||||
author=aut, articles=articles, dates=dates,
|
author=aut, articles=articles, dates=dates,
|
||||||
paginated={'articles': articles, 'dates': dates},
|
paginated={'articles': articles, 'dates': dates},
|
||||||
page_name=u'author/%s' % aut)
|
page_name=aut.page_name)
|
||||||
|
|
||||||
def generate_drafts(self, write):
|
def generate_drafts(self, write):
|
||||||
"""Generate drafts pages."""
|
"""Generate drafts pages."""
|
||||||
|
|
@ -269,7 +281,7 @@ class ArticlesGenerator(Generator):
|
||||||
if 'category' not in metadata:
|
if 'category' not in metadata:
|
||||||
|
|
||||||
if os.path.dirname(f) == article_path: # if the article is not in a subdirectory
|
if os.path.dirname(f) == article_path: # if the article is not in a subdirectory
|
||||||
category = self.settings['DEFAULT_CATEGORY']
|
category = self.settings['DEFAULT_CATEGORY']
|
||||||
else:
|
else:
|
||||||
category = os.path.basename(os.path.dirname(f))\
|
category = os.path.basename(os.path.dirname(f))\
|
||||||
.decode('utf-8')
|
.decode('utf-8')
|
||||||
|
|
@ -352,10 +364,12 @@ class ArticlesGenerator(Generator):
|
||||||
|
|
||||||
self.authors = list(self.authors.items())
|
self.authors = list(self.authors.items())
|
||||||
self.authors.sort(key=lambda item: item[0].name)
|
self.authors.sort(key=lambda item: item[0].name)
|
||||||
|
|
||||||
self._update_context(('articles', 'dates', 'tags', 'categories',
|
self._update_context(('articles', 'dates', 'tags', 'categories',
|
||||||
'tag_cloud', 'authors', 'related_posts'))
|
'tag_cloud', 'authors', 'related_posts'))
|
||||||
|
|
||||||
|
signals.article_generator_finalized.send(self)
|
||||||
|
|
||||||
def generate_output(self, writer):
|
def generate_output(self, writer):
|
||||||
self.generate_feeds(writer)
|
self.generate_feeds(writer)
|
||||||
self.generate_pages(writer)
|
self.generate_pages(writer)
|
||||||
|
|
@ -370,7 +384,7 @@ class PagesGenerator(Generator):
|
||||||
self.hidden_translations = []
|
self.hidden_translations = []
|
||||||
super(PagesGenerator, self).__init__(*args, **kwargs)
|
super(PagesGenerator, self).__init__(*args, **kwargs)
|
||||||
signals.pages_generator_init.send(self)
|
signals.pages_generator_init.send(self)
|
||||||
|
|
||||||
def generate_context(self):
|
def generate_context(self):
|
||||||
all_pages = []
|
all_pages = []
|
||||||
hidden_pages = []
|
hidden_pages = []
|
||||||
|
|
@ -378,7 +392,7 @@ class PagesGenerator(Generator):
|
||||||
os.path.join(self.path, self.settings['PAGE_DIR']),
|
os.path.join(self.path, self.settings['PAGE_DIR']),
|
||||||
exclude=self.settings['PAGE_EXCLUDES']):
|
exclude=self.settings['PAGE_EXCLUDES']):
|
||||||
try:
|
try:
|
||||||
content, metadata = read_file(f)
|
content, metadata = read_file(f, settings=self.settings)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.warning(u'Could not process %s\n%s' % (f, str(e)))
|
logger.warning(u'Could not process %s\n%s' % (f, str(e)))
|
||||||
continue
|
continue
|
||||||
|
|
@ -429,7 +443,23 @@ class StaticGenerator(Generator):
|
||||||
# Define the assets environment that will be passed to the
|
# Define the assets environment that will be passed to the
|
||||||
# generators. The StaticGenerator must then be run first to have
|
# generators. The StaticGenerator must then be run first to have
|
||||||
# the assets in the output_path before generating the templates.
|
# the assets in the output_path before generating the templates.
|
||||||
assets_url = self.settings['SITEURL'] + '/theme/'
|
|
||||||
|
# Let ASSET_URL honor Pelican's RELATIVE_URLS setting.
|
||||||
|
# Hint for templates:
|
||||||
|
# Current version of webassets seem to remove any relative
|
||||||
|
# paths at the beginning of the URL. So, if RELATIVE_URLS
|
||||||
|
# is on, ASSET_URL will start with 'theme/', regardless if we
|
||||||
|
# set assets_url here to './theme/' or to 'theme/'.
|
||||||
|
# XXX However, this breaks the ASSET_URL if user navigates to
|
||||||
|
# a sub-URL, e.g. if he clicks on a category. To workaround this
|
||||||
|
# issue, I use
|
||||||
|
# <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
|
||||||
|
# instead of
|
||||||
|
# <link rel="stylesheet" href="{{ ASSET_URL }}">
|
||||||
|
if self.settings.get('RELATIVE_URLS'):
|
||||||
|
assets_url = './theme/'
|
||||||
|
else:
|
||||||
|
assets_url = self.settings['SITEURL'] + '/theme/'
|
||||||
assets_src = os.path.join(self.output_path, 'theme')
|
assets_src = os.path.join(self.output_path, 'theme')
|
||||||
self.assets_env = AssetsEnvironment(assets_src, assets_url)
|
self.assets_env = AssetsEnvironment(assets_src, assets_url)
|
||||||
|
|
||||||
|
|
@ -453,13 +483,20 @@ class PdfGenerator(Generator):
|
||||||
"""Generate PDFs on the output dir, for all articles and pages coming from
|
"""Generate PDFs on the output dir, for all articles and pages coming from
|
||||||
rst"""
|
rst"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PdfGenerator, self).__init__(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
from rst2pdf.createpdf import RstToPdf
|
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'
|
||||||
self.pdfcreator = RstToPdf(breakside=0,
|
self.pdfcreator = RstToPdf(breakside=0,
|
||||||
stylesheets=['twelvepoint'])
|
stylesheets=[pdf_style],
|
||||||
|
style_path=[pdf_style_path])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise Exception("unable to find rst2pdf")
|
raise Exception("unable to find rst2pdf")
|
||||||
super(PdfGenerator, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _create_pdf(self, obj, output_path):
|
def _create_pdf(self, obj, output_path):
|
||||||
if obj.filename.endswith(".rst"):
|
if obj.filename.endswith(".rst"):
|
||||||
|
|
@ -467,7 +504,7 @@ class PdfGenerator(Generator):
|
||||||
output_pdf = os.path.join(output_path, filename)
|
output_pdf = os.path.join(output_path, filename)
|
||||||
# print "Generating pdf for", obj.filename, " in ", output_pdf
|
# print "Generating pdf for", obj.filename, " in ", output_pdf
|
||||||
with open(obj.filename) as f:
|
with open(obj.filename) as f:
|
||||||
self.pdfcreator.createPdf(text=f, output=output_pdf)
|
self.pdfcreator.createPdf(text=f.read(), output=output_pdf)
|
||||||
logger.info(u' [ok] writing %s' % output_pdf)
|
logger.info(u' [ok] writing %s' % output_pdf)
|
||||||
|
|
||||||
def generate_context(self):
|
def generate_context(self):
|
||||||
|
|
@ -491,6 +528,19 @@ class PdfGenerator(Generator):
|
||||||
for page in self.context['pages']:
|
for page in self.context['pages']:
|
||||||
self._create_pdf(page, pdf_path)
|
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 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)
|
||||||
|
|
||||||
class LessCSSGenerator(Generator):
|
class LessCSSGenerator(Generator):
|
||||||
"""Compile less css files."""
|
"""Compile less css files."""
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ from pelican import signals
|
||||||
License plugin for Pelican
|
License plugin for Pelican
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Simply add license variable in article's context, which contain
|
This plugin allows you to define a LICENSE setting and adds the contents of that
|
||||||
the license text.
|
license variable to the article's context, making that variable available to use
|
||||||
|
from within your theme's templates.
|
||||||
|
|
||||||
Settings:
|
Settings:
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Add LICENSE to your settings file to define default license.
|
Define LICENSE in your settings file with the contents of your default license.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,22 @@ from pelican import signals
|
||||||
Gravatar plugin for Pelican
|
Gravatar plugin for Pelican
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
Simply add author_gravatar variable in article's context, which contains
|
This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
|
||||||
the gravatar url.
|
makes the variable available within the article's context.
|
||||||
|
|
||||||
Settings:
|
Settings:
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Add AUTHOR_EMAIL to your settings file to define default author email.
|
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:
|
Article metadata:
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
:email: article's author email
|
:email: article's author email
|
||||||
|
|
||||||
If one of them are defined, the author_gravatar variable is added to
|
If one of them are defined, the author_gravatar variable is added to the
|
||||||
article's context.
|
article's context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
59
pelican/plugins/multi_part.py
Normal file
59
pelican/plugins/multi_part.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# -*- 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)
|
||||||
190
pelican/plugins/sitemap.py
Normal file
190
pelican/plugins/sitemap.py
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
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)
|
||||||
|
|
@ -16,7 +16,7 @@ except ImportError:
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from pelican.contents import Category, Tag, Author
|
from pelican.contents import Category, Tag, Author
|
||||||
from pelican.utils import get_date, open
|
from pelican.utils import get_date, pelican_open
|
||||||
|
|
||||||
|
|
||||||
_METADATA_PROCESSORS = {
|
_METADATA_PROCESSORS = {
|
||||||
|
|
@ -102,7 +102,7 @@ class RstReader(Reader):
|
||||||
def _get_publisher(self, filename):
|
def _get_publisher(self, filename):
|
||||||
extra_params = {'initial_header_level': '2'}
|
extra_params = {'initial_header_level': '2'}
|
||||||
pub = docutils.core.Publisher(
|
pub = docutils.core.Publisher(
|
||||||
destination_class=docutils.io.StringOutput)
|
destination_class=docutils.io.StringOutput)
|
||||||
pub.set_components('standalone', 'restructuredtext', 'html')
|
pub.set_components('standalone', 'restructuredtext', 'html')
|
||||||
pub.writer.translator_class = PelicanHTMLTranslator
|
pub.writer.translator_class = PelicanHTMLTranslator
|
||||||
pub.process_programmatic_settings(None, extra_params, None)
|
pub.process_programmatic_settings(None, extra_params, None)
|
||||||
|
|
@ -129,8 +129,13 @@ class MarkdownReader(Reader):
|
||||||
|
|
||||||
def read(self, filename):
|
def read(self, filename):
|
||||||
"""Parse content and metadata of markdown files"""
|
"""Parse content and metadata of markdown files"""
|
||||||
text = open(filename)
|
markdown_extensions = self.settings.get('MARKDOWN_EXTENSIONS', [])
|
||||||
md = Markdown(extensions=set(self.extensions + ['meta']))
|
if isinstance(markdown_extensions, (str, unicode)):
|
||||||
|
markdown_extensions = [m.strip() for m in
|
||||||
|
markdown_extensions.split(',')]
|
||||||
|
text = pelican_open(filename)
|
||||||
|
md = Markdown(extensions=set(
|
||||||
|
self.extensions + markdown_extensions + ['meta']))
|
||||||
content = md.convert(text)
|
content = md.convert(text)
|
||||||
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
|
|
@ -146,7 +151,7 @@ class HtmlReader(Reader):
|
||||||
|
|
||||||
def read(self, filename):
|
def read(self, filename):
|
||||||
"""Parse content and metadata of (x)HTML files"""
|
"""Parse content and metadata of (x)HTML files"""
|
||||||
with open(filename) as content:
|
with pelican_open(filename) as content:
|
||||||
metadata = {'title': 'unnamed'}
|
metadata = {'title': 'unnamed'}
|
||||||
for i in self._re.findall(content):
|
for i in self._re.findall(content):
|
||||||
key = i.split(':')[0][5:].strip()
|
key = i.split(':')[0][5:].strip()
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,14 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
||||||
'THEME_STATIC_PATHS': ['static', ],
|
'THEME_STATIC_PATHS': ['static', ],
|
||||||
'FEED_ATOM': 'feeds/all.atom.xml',
|
'FEED_ATOM': 'feeds/all.atom.xml',
|
||||||
'CATEGORY_FEED_ATOM': 'feeds/%s.atom.xml',
|
'CATEGORY_FEED_ATOM': 'feeds/%s.atom.xml',
|
||||||
'TRANSLATION_FEED': 'feeds/all-%s.atom.xml',
|
'TRANSLATION_FEED_ATOM': 'feeds/all-%s.atom.xml',
|
||||||
'FEED_MAX_ITEMS': '',
|
'FEED_MAX_ITEMS': '',
|
||||||
'SITEURL': '',
|
'SITEURL': '',
|
||||||
'SITENAME': 'A Pelican Blog',
|
'SITENAME': 'A Pelican Blog',
|
||||||
'DISPLAY_PAGES_ON_MENU': True,
|
'DISPLAY_PAGES_ON_MENU': True,
|
||||||
'PDF_GENERATOR': False,
|
'PDF_GENERATOR': False,
|
||||||
|
'OUTPUT_SOURCES': False,
|
||||||
|
'OUTPUT_SOURCES_EXTENSION': '.text',
|
||||||
'DEFAULT_CATEGORY': 'misc',
|
'DEFAULT_CATEGORY': 'misc',
|
||||||
'DEFAULT_DATE': 'fs',
|
'DEFAULT_DATE': 'fs',
|
||||||
'WITH_FUTURE_DATES': True,
|
'WITH_FUTURE_DATES': True,
|
||||||
|
|
@ -58,6 +60,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
||||||
'TAG_CLOUD_STEPS': 4,
|
'TAG_CLOUD_STEPS': 4,
|
||||||
'TAG_CLOUD_MAX_ITEMS': 100,
|
'TAG_CLOUD_MAX_ITEMS': 100,
|
||||||
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
|
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
|
||||||
|
'EXTRA_TEMPLATES_PATHS' : [],
|
||||||
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
|
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
|
||||||
'PELICAN_CLASS': 'pelican.Pelican',
|
'PELICAN_CLASS': 'pelican.Pelican',
|
||||||
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
|
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
|
||||||
|
|
@ -75,16 +78,28 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
||||||
'SUMMARY_MAX_LENGTH': 50,
|
'SUMMARY_MAX_LENGTH': 50,
|
||||||
'WEBASSETS': False,
|
'WEBASSETS': False,
|
||||||
'PLUGINS': [],
|
'PLUGINS': [],
|
||||||
|
'MARKDOWN_EXTENSIONS': ['toc', ],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def read_settings(filename=None):
|
def read_settings(filename=None, override=None):
|
||||||
if filename:
|
if filename:
|
||||||
local_settings = get_settings_from_file(filename)
|
local_settings = get_settings_from_file(filename)
|
||||||
|
# Make the paths relative to the settings file
|
||||||
|
for p in ['PATH', 'OUTPUT_PATH', 'THEME']:
|
||||||
|
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):
|
||||||
|
local_settings[p] = absp
|
||||||
else:
|
else:
|
||||||
local_settings = copy.deepcopy(_DEFAULT_CONFIG)
|
local_settings = copy.deepcopy(_DEFAULT_CONFIG)
|
||||||
configured_settings = configure_settings(local_settings, None, filename)
|
|
||||||
return configured_settings
|
if override:
|
||||||
|
local_settings.update(override)
|
||||||
|
|
||||||
|
return configure_settings(local_settings)
|
||||||
|
|
||||||
|
|
||||||
def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
|
def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
|
||||||
|
|
@ -94,9 +109,8 @@ def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
|
||||||
|
|
||||||
context = copy.deepcopy(default_settings)
|
context = copy.deepcopy(default_settings)
|
||||||
if module is not None:
|
if module is not None:
|
||||||
context.update(
|
context.update(
|
||||||
(k, v) for k, v in inspect.getmembers(module) if k.isupper()
|
(k, v) for k, v in inspect.getmembers(module) if k.isupper())
|
||||||
)
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -111,19 +125,23 @@ def get_settings_from_file(filename, default_settings=_DEFAULT_CONFIG):
|
||||||
return get_settings_from_module(module, default_settings=default_settings)
|
return get_settings_from_module(module, default_settings=default_settings)
|
||||||
|
|
||||||
|
|
||||||
def configure_settings(settings, default_settings=None, filename=None):
|
def configure_settings(settings):
|
||||||
"""Provide optimizations, error checking, and warnings for loaded settings"""
|
"""
|
||||||
if default_settings is None:
|
Provide optimizations, error checking, and warnings for loaded settings
|
||||||
default_settings = copy.deepcopy(_DEFAULT_CONFIG)
|
"""
|
||||||
|
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)')
|
||||||
|
|
||||||
# Make the paths relative to the settings file
|
# find the theme in pelican.theme if the given one does not exists
|
||||||
if filename:
|
if not os.path.isdir(settings['THEME']):
|
||||||
for path in ['PATH', 'OUTPUT_PATH']:
|
theme_path = os.sep.join([os.path.dirname(
|
||||||
if path in settings:
|
os.path.abspath(__file__)), "themes/%s" % settings['THEME']])
|
||||||
if settings[path] is not None and not isabs(settings[path]):
|
if os.path.exists(theme_path):
|
||||||
settings[path] = os.path.abspath(os.path.normpath(
|
settings['THEME'] = theme_path
|
||||||
os.path.join(os.path.dirname(filename), settings[path]))
|
else:
|
||||||
)
|
raise Exception("Impossible to find the theme %s"
|
||||||
|
% settings['THEME'])
|
||||||
|
|
||||||
# if locales is not a list, make it one
|
# if locales is not a list, make it one
|
||||||
locales = settings['LOCALE']
|
locales = settings['LOCALE']
|
||||||
|
|
@ -174,4 +192,11 @@ def configure_settings(settings, default_settings=None, filename=None):
|
||||||
logger.warn("You must install the webassets module to use WEBASSETS.")
|
logger.warn("You must install the webassets module to use WEBASSETS.")
|
||||||
settings['WEBASSETS'] = False
|
settings['WEBASSETS'] = False
|
||||||
|
|
||||||
|
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'])
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
from blinker import signal
|
from blinker import signal
|
||||||
|
|
||||||
initialized = signal('pelican_initialized')
|
initialized = signal('pelican_initialized')
|
||||||
|
finalized = signal('pelican_finalized')
|
||||||
article_generate_context = signal('article_generate_context')
|
article_generate_context = signal('article_generate_context')
|
||||||
article_generator_init = signal('article_generator_init')
|
article_generator_init = signal('article_generator_init')
|
||||||
|
article_generator_finalized = signal('article_generate_finalized')
|
||||||
|
get_generators = signal('get_generators')
|
||||||
pages_generate_context = signal('pages_generate_context')
|
pages_generate_context = signal('pages_generate_context')
|
||||||
pages_generator_init = signal('pages_generator_init')
|
pages_generator_init = signal('pages_generator_init')
|
||||||
|
content_object_init = signal('content_object_init')
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ dl {margin: 0 0 1.5em 0;}
|
||||||
dt {font-weight: bold;}
|
dt {font-weight: bold;}
|
||||||
dd {margin-left: 1.5em;}
|
dd {margin-left: 1.5em;}
|
||||||
|
|
||||||
pre{background-color: #000; padding: 10px; color: #fff; margin: 10px; overflow: auto;}
|
pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
|
||||||
|
|
||||||
/* Quotes */
|
/* Quotes */
|
||||||
blockquote {
|
blockquote {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.hll {
|
.hll {
|
||||||
background-color:#FFFFCC;
|
background-color:#eee;
|
||||||
}
|
}
|
||||||
.c {
|
.c {
|
||||||
color:#408090;
|
color:#408090;
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,16 @@
|
||||||
<link href="{{ FEED_DOMAIN }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
|
<link href="{{ FEED_DOMAIN }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if CATEGORY_FEED_ATOM %}
|
{% if CATEGORY_FEED_ATOM %}
|
||||||
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Categories Atom Feed" />
|
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_ATOM|format(category) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Categories Atom Feed" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if CATEGORY_FEED_RSS %}
|
{% if CATEGORY_FEED_RSS %}
|
||||||
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Categories RSS Feed" />
|
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_RSS|format(category) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Categories RSS Feed" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if TAG_FEED_ATOM %}
|
{% if TAG_FEED_ATOM %}
|
||||||
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Tags Atom Feed" />
|
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_ATOM|format(tag) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Tags Atom Feed" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if TAG_FEED_RSS %}
|
{% if TAG_FEED_RSS %}
|
||||||
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Tags RSS Feed" />
|
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_RSS|format(tag) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Tags RSS Feed" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock head %}
|
{% endblock head %}
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,8 @@ def build_header(title, date, author, categories, tags):
|
||||||
header = '%s\n%s\n' % (title, '#' * len(title))
|
header = '%s\n%s\n' % (title, '#' * len(title))
|
||||||
if date:
|
if date:
|
||||||
header += ':date: %s\n' % date
|
header += ':date: %s\n' % date
|
||||||
|
if author:
|
||||||
|
header += ':author: %s\n' % author
|
||||||
if categories:
|
if categories:
|
||||||
header += ':category: %s\n' % ', '.join(categories)
|
header += ':category: %s\n' % ', '.join(categories)
|
||||||
if tags:
|
if tags:
|
||||||
|
|
@ -196,6 +198,8 @@ def build_markdown_header(title, date, author, categories, tags):
|
||||||
header = 'Title: %s\n' % title
|
header = 'Title: %s\n' % title
|
||||||
if date:
|
if date:
|
||||||
header += 'Date: %s\n' % date
|
header += 'Date: %s\n' % date
|
||||||
|
if author:
|
||||||
|
header += 'Author: %s\n' % author
|
||||||
if categories:
|
if categories:
|
||||||
header += 'Category: %s\n' % ', '.join(categories)
|
header += 'Category: %s\n' % ', '.join(categories)
|
||||||
if tags:
|
if tags:
|
||||||
|
|
@ -216,7 +220,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False, strip_raw=Fals
|
||||||
filename = os.path.basename(filename)
|
filename = os.path.basename(filename)
|
||||||
|
|
||||||
# option to put files in directories with categories names
|
# option to put files in directories with categories names
|
||||||
if dircat and (len(categories) == 1):
|
if dircat and (len(categories) > 0):
|
||||||
catname = slugify(categories[0])
|
catname = slugify(categories[0])
|
||||||
out_filename = os.path.join(output_path, catname, filename+ext)
|
out_filename = os.path.join(output_path, catname, filename+ext)
|
||||||
if not os.path.isdir(os.path.join(output_path, catname)):
|
if not os.path.isdir(os.path.join(output_path, catname)):
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,12 @@ import codecs
|
||||||
|
|
||||||
from pelican import __version__
|
from pelican import __version__
|
||||||
|
|
||||||
_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), \
|
_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
"templates")
|
"templates")
|
||||||
|
|
||||||
|
|
||||||
CONF = {
|
CONF = {
|
||||||
'pelican' : 'pelican',
|
'pelican': 'pelican',
|
||||||
'pelicanopts' : '',
|
'pelicanopts': '',
|
||||||
'basedir': '.',
|
'basedir': '.',
|
||||||
'ftp_host': 'localhost',
|
'ftp_host': 'localhost',
|
||||||
'ftp_user': 'anonymous',
|
'ftp_user': 'anonymous',
|
||||||
|
|
@ -24,8 +23,8 @@ CONF = {
|
||||||
'ssh_port': 22,
|
'ssh_port': 22,
|
||||||
'ssh_user': 'root',
|
'ssh_user': 'root',
|
||||||
'ssh_target_dir': '/var/www',
|
'ssh_target_dir': '/var/www',
|
||||||
'dropbox_dir' : '~/Dropbox/Public/',
|
'dropbox_dir': '~/Dropbox/Public/',
|
||||||
'default_pagination' : 10,
|
'default_pagination': 10,
|
||||||
'siteurl': '',
|
'siteurl': '',
|
||||||
'lang': 'en'
|
'lang': 'en'
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +76,7 @@ def ask(question, answer=str, default=None, l=None):
|
||||||
if l and len(r) != l:
|
if l and len(r) != l:
|
||||||
print('You must enter a {0} letters long string'.format(l))
|
print('You must enter a {0} letters long string'.format(l))
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
@ -148,14 +147,16 @@ def main():
|
||||||
|
|
||||||
This script will help you create a new Pelican-based website.
|
This script will help you create a new Pelican-based website.
|
||||||
|
|
||||||
Please answer the following questions so this script can generate the files needed by Pelican.
|
Please answer the following questions so this script can generate the files
|
||||||
|
needed by Pelican.
|
||||||
|
|
||||||
'''.format(v=__version__))
|
'''.format(v=__version__))
|
||||||
|
|
||||||
project = os.path.join(os.environ.get('VIRTUAL_ENV', '.'), '.project')
|
project = os.path.join(os.environ.get('VIRTUAL_ENV', '.'), '.project')
|
||||||
if os.path.isfile(project):
|
if os.path.isfile(project):
|
||||||
CONF['basedir'] = open(project, 'r').read().rstrip("\n")
|
CONF['basedir'] = open(project, 'r').read().rstrip("\n")
|
||||||
print('Using project associated with current virtual environment. Will save to:\n%s\n' % CONF['basedir'])
|
print('Using project associated with current virtual environment.'
|
||||||
|
'Will save to:\n%s\n' % CONF['basedir'])
|
||||||
else:
|
else:
|
||||||
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new web site?', answer=str, default=args.path))
|
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new web site?', answer=str, default=args.path))
|
||||||
|
|
||||||
|
|
@ -201,9 +202,13 @@ Please answer the following questions so this script can generate the files need
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w', 'utf-8') as fd:
|
with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w', 'utf-8') as fd:
|
||||||
|
conf_python = dict()
|
||||||
|
for key, value in CONF.iteritems():
|
||||||
|
conf_python[key] = repr(value)
|
||||||
|
|
||||||
for line in get_template('pelicanconf.py'):
|
for line in get_template('pelicanconf.py'):
|
||||||
template = string.Template(line)
|
template = string.Template(line)
|
||||||
fd.write(template.safe_substitute(CONF))
|
fd.write(template.safe_substitute(conf_python))
|
||||||
fd.close()
|
fd.close()
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
print('Error: {0}'.format(e))
|
print('Error: {0}'.format(e))
|
||||||
|
|
@ -228,11 +233,16 @@ Please answer the following questions so this script can generate the files need
|
||||||
print('Error: {0}'.format(e))
|
print('Error: {0}'.format(e))
|
||||||
|
|
||||||
if develop:
|
if develop:
|
||||||
|
conf_shell = dict()
|
||||||
|
for key, value in CONF.iteritems():
|
||||||
|
if isinstance(value, basestring) and ' ' in value:
|
||||||
|
value = '"' + value.replace('"', '\\"') + '"'
|
||||||
|
conf_shell[key] = value
|
||||||
try:
|
try:
|
||||||
with codecs.open(os.path.join(CONF['basedir'], 'develop_server.sh'), 'w', 'utf-8') as fd:
|
with codecs.open(os.path.join(CONF['basedir'], 'develop_server.sh'), 'w', 'utf-8') as fd:
|
||||||
for line in get_template('develop_server.sh'):
|
for line in get_template('develop_server.sh'):
|
||||||
template = string.Template(line)
|
template = string.Template(line)
|
||||||
fd.write(template.safe_substitute(CONF))
|
fd.write(template.safe_substitute(conf_shell))
|
||||||
fd.close()
|
fd.close()
|
||||||
os.chmod((os.path.join(CONF['basedir'], 'develop_server.sh')), 0755)
|
os.chmod((os.path.join(CONF['basedir'], 'develop_server.sh')), 0755)
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ ssh_upload: publish
|
||||||
scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||||
|
|
||||||
rsync_upload: publish
|
rsync_upload: publish
|
||||||
rsync -e "ssh -p $(SSH_PORT)" -P -rvz --delete $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
rsync -e "ssh -p $(SSH_PORT)" -P -rvz --delete $(OUTPUTDIR) $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
||||||
|
|
||||||
dropbox_upload: publish
|
dropbox_upload: publish
|
||||||
cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*- #
|
# -*- coding: utf-8 -*- #
|
||||||
|
|
||||||
AUTHOR = u"$author"
|
AUTHOR = $author
|
||||||
SITENAME = u"$sitename"
|
SITENAME = $sitename
|
||||||
SITEURL = ''
|
SITEURL = ''
|
||||||
|
|
||||||
TIMEZONE = 'Europe/Paris'
|
TIMEZONE = 'Europe/Paris'
|
||||||
|
|
||||||
DEFAULT_LANG = '$lang'
|
DEFAULT_LANG = $lang
|
||||||
|
|
||||||
# Blogroll
|
# Blogroll
|
||||||
LINKS = (('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
LINKS = (('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import shutil
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from codecs import open as _open
|
from codecs import open
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from jinja2 import Markup
|
from jinja2 import Markup
|
||||||
|
|
@ -14,6 +14,7 @@ from operator import attrgetter
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NoFilesError(Exception):
|
class NoFilesError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -37,9 +38,9 @@ def get_date(string):
|
||||||
raise ValueError("'%s' is not a valid date" % string)
|
raise ValueError("'%s' is not a valid date" % string)
|
||||||
|
|
||||||
|
|
||||||
def open(filename):
|
def pelican_open(filename):
|
||||||
"""Open a file and return it's content"""
|
"""Open a file and return it's content"""
|
||||||
return _open(filename, encoding='utf-8').read()
|
return open(filename, encoding='utf-8').read()
|
||||||
|
|
||||||
|
|
||||||
def slugify(value):
|
def slugify(value):
|
||||||
|
|
@ -86,16 +87,32 @@ def copy(path, source, destination, destination_path=None, overwrite=False):
|
||||||
if overwrite:
|
if overwrite:
|
||||||
shutil.rmtree(destination_)
|
shutil.rmtree(destination_)
|
||||||
shutil.copytree(source_, destination_)
|
shutil.copytree(source_, destination_)
|
||||||
logger.info('replacement of %s with %s' % (source_, destination_))
|
logger.info('replacement of %s with %s' % (source_,
|
||||||
|
destination_))
|
||||||
|
|
||||||
elif os.path.isfile(source_):
|
elif os.path.isfile(source_):
|
||||||
|
dest_dir = os.path.dirname(destination_)
|
||||||
|
if not os.path.exists(dest_dir):
|
||||||
|
os.makedirs(dest_dir)
|
||||||
shutil.copy(source_, destination_)
|
shutil.copy(source_, destination_)
|
||||||
logger.info('copying %s to %s' % (source_, destination_))
|
logger.info('copying %s to %s' % (source_, destination_))
|
||||||
|
else:
|
||||||
|
logger.warning('skipped copy %s to %s' % (source_, destination_))
|
||||||
|
|
||||||
def clean_output_dir(path):
|
def clean_output_dir(path):
|
||||||
"""Remove all the files from the output directory"""
|
"""Remove all the files from the output directory"""
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
logger.debug("Directory already removed: %s" % path)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Unable to delete file %s; %e" % path, e)
|
||||||
|
return
|
||||||
|
|
||||||
# remove all the existing content from the output folder
|
# remove all the existing content from the output folder
|
||||||
for filename in os.listdir(path):
|
for filename in os.listdir(path):
|
||||||
file = os.path.join(path, filename)
|
file = os.path.join(path, filename)
|
||||||
|
|
|
||||||
|
|
@ -148,9 +148,9 @@ class Writer(object):
|
||||||
paginators[key] = Paginator(object_list, len(object_list))
|
paginators[key] = Paginator(object_list, len(object_list))
|
||||||
|
|
||||||
# generated pages, and write
|
# generated pages, and write
|
||||||
|
name_root, ext = os.path.splitext(name)
|
||||||
for page_num in range(paginators.values()[0].num_pages):
|
for page_num in range(paginators.values()[0].num_pages):
|
||||||
paginated_localcontext = localcontext.copy()
|
paginated_localcontext = localcontext.copy()
|
||||||
paginated_name = name
|
|
||||||
for key in paginators.iterkeys():
|
for key in paginators.iterkeys():
|
||||||
paginator = paginators[key]
|
paginator = paginators[key]
|
||||||
page = paginator.page(page_num + 1)
|
page = paginator.page(page_num + 1)
|
||||||
|
|
@ -158,9 +158,10 @@ class Writer(object):
|
||||||
{'%s_paginator' % key: paginator,
|
{'%s_paginator' % key: paginator,
|
||||||
'%s_page' % key: page})
|
'%s_page' % key: page})
|
||||||
if page_num > 0:
|
if page_num > 0:
|
||||||
ext = '.' + paginated_name.rsplit('.')[-1]
|
paginated_name = '%s%s%s' % (
|
||||||
paginated_name = paginated_name.replace(ext,
|
name_root, page_num + 1, ext)
|
||||||
'%s%s' % (page_num + 1, ext))
|
else:
|
||||||
|
paginated_name = name
|
||||||
|
|
||||||
_write_file(template, paginated_localcontext, self.output_path,
|
_write_file(template, paginated_localcontext, self.output_path,
|
||||||
paginated_name)
|
paginated_name)
|
||||||
|
|
|
||||||
8
tests/content/article_with_markdown_markup_extensions.md
Normal file
8
tests/content/article_with_markdown_markup_extensions.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
Title: Test Markdown extensions
|
||||||
|
|
||||||
|
[TOC]
|
||||||
|
|
||||||
|
## Level1
|
||||||
|
|
||||||
|
### Level2
|
||||||
|
|
||||||
|
|
@ -55,8 +55,8 @@
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-07-26T22:04:43.481849">
|
<abbr class="published" title="2012-09-17T15:02:39.591671">
|
||||||
Thu 26 July 2012
|
Mon 17 September 2012
|
||||||
</abbr>
|
</abbr>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>A Pelican Blog</title><link href="/" rel="alternate"></link><link href="/feeds/all-fr.atom.xml" rel="self"></link><id>/</id><updated>2012-07-26T22:04:43Z</updated><entry><title>Trop bien !</title><link href="/oh-yeah-fr.html" rel="alternate"></link><updated>2012-07-26T22:04:43Z</updated><author><name>Dummy Author</name></author><id>tag:,2012-07-26:oh-yeah-fr.html</id><summary type="html"><p>Et voila du contenu en français</p>
|
<feed xmlns="http://www.w3.org/2005/Atom"><title>A Pelican Blog</title><link href="/" rel="alternate"></link><link href="/feeds/all-fr.atom.xml" rel="self"></link><id>/</id><updated>2012-09-17T15:02:39Z</updated><entry><title>Trop bien !</title><link href="/oh-yeah-fr.html" rel="alternate"></link><updated>2012-09-17T15:02:39Z</updated><author><name>Dummy Author</name></author><id>tag:,2012-09-17:oh-yeah-fr.html</id><summary type="html"><p>Et voila du contenu en français</p>
|
||||||
</summary></entry><entry><title>Deuxième article</title><link href="/second-article-fr.html" rel="alternate"></link><updated>2012-02-29T00:00:00Z</updated><author><name>Dummy Author</name></author><id>tag:,2012-02-29:second-article-fr.html</id><summary type="html"><p>Ceci est un article, en français.</p>
|
</summary></entry><entry><title>Deuxième article</title><link href="/second-article-fr.html" rel="alternate"></link><updated>2012-02-29T00:00:00Z</updated><author><name>Dummy Author</name></author><id>tag:,2012-02-29:second-article-fr.html</id><summary type="html"><p>Ceci est un article, en français.</p>
|
||||||
</summary><category term="foo"></category><category term="bar"></category><category term="baz"></category></entry></feed>
|
</summary><category term="foo"></category><category term="bar"></category><category term="baz"></category></entry></feed>
|
||||||
|
|
@ -55,8 +55,8 @@
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-07-26T22:04:43.481849">
|
<abbr class="published" title="2012-09-17T15:02:39.591671">
|
||||||
Thu 26 July 2012
|
Mon 17 September 2012
|
||||||
</abbr>
|
</abbr>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -68,10 +68,10 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -113,13 +113,13 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
|
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -68,10 +68,10 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -113,13 +113,13 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
|
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -68,10 +68,10 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -113,13 +113,13 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
|
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ dl {margin: 0 0 1.5em 0;}
|
||||||
dt {font-weight: bold;}
|
dt {font-weight: bold;}
|
||||||
dd {margin-left: 1.5em;}
|
dd {margin-left: 1.5em;}
|
||||||
|
|
||||||
pre{background-color: #000; padding: 10px; color: #fff; margin: 10px; overflow: auto;}
|
pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
|
||||||
|
|
||||||
/* Quotes */
|
/* Quotes */
|
||||||
blockquote {
|
blockquote {
|
||||||
|
|
@ -308,7 +308,8 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;}
|
||||||
.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
|
.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
|
||||||
.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
|
.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
|
||||||
.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
|
.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
|
||||||
.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.org');}
|
.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
|
||||||
|
.social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
About
|
About
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.hll {
|
.hll {
|
||||||
background-color:#FFFFCC;
|
background-color:#eee;
|
||||||
}
|
}
|
||||||
.c {
|
.c {
|
||||||
color:#408090;
|
color:#408090;
|
||||||
|
|
|
||||||
BIN
tests/output/basic/theme/images/icons/gittip.png
Normal file
BIN
tests/output/basic/theme/images/icons/gittip.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 671 B |
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -76,11 +76,11 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
</aside><!-- /#featured -->
|
</aside><!-- /#featured -->
|
||||||
|
|
@ -100,8 +100,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -121,14 +121,14 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -76,11 +76,11 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
</aside><!-- /#featured -->
|
</aside><!-- /#featured -->
|
||||||
|
|
@ -100,8 +100,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -121,14 +121,14 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<aside id="featured" class="body">
|
<aside id="featured" class="body">
|
||||||
<article>
|
<article>
|
||||||
<h1 class="entry-title"><a href=".././second-article.html">Second article</a></h1>
|
<h1 class="entry-title"><a href=".././second-article-fr.html">Deuxième article</a></h1>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="2012-02-29T00:00:00">
|
<abbr class="published" title="2012-02-29T00:00:00">
|
||||||
Wed 29 February 2012
|
Wed 29 February 2012
|
||||||
|
|
@ -76,11 +76,11 @@
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article-fr.html">fr</a>
|
<a href=".././second-article.html">en</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info --><p>This is some article, in english</p>
|
</footer><!-- /.post-info --><p>Ceci est un article, en français.</p>
|
||||||
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
</aside><!-- /#featured -->
|
</aside><!-- /#featured -->
|
||||||
|
|
@ -100,8 +100,8 @@ Translations:
|
||||||
|
|
||||||
<li><article class="hentry">
|
<li><article class="hentry">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href=".././second-article-fr.html" rel="bookmark"
|
<h1><a href=".././second-article.html" rel="bookmark"
|
||||||
title="Permalink to Deuxième article">Deuxième article</a></h1>
|
title="Permalink to Second article">Second article</a></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
|
|
@ -121,14 +121,14 @@ Translations:
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
|
|
||||||
<a href=".././second-article.html">en</a>
|
<a href=".././second-article-fr.html">fr</a>
|
||||||
|
|
||||||
|
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<p>Ceci est un article, en français.</p>
|
<p>This is some article, in english</p>
|
||||||
|
|
||||||
<a class="readmore" href=".././second-article-fr.html">read more</a>
|
<a class="readmore" href=".././second-article.html">read more</a>
|
||||||
<p>There are <a href=".././second-article-fr.html#disqus_thread">comments</a>.</p>
|
<p>There are <a href=".././second-article.html#disqus_thread">comments</a>.</p>
|
||||||
</div><!-- /.entry-content -->
|
</div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ dl {margin: 0 0 1.5em 0;}
|
||||||
dt {font-weight: bold;}
|
dt {font-weight: bold;}
|
||||||
dd {margin-left: 1.5em;}
|
dd {margin-left: 1.5em;}
|
||||||
|
|
||||||
pre{background-color: #000; padding: 10px; color: #fff; margin: 10px; overflow: auto;}
|
pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
|
||||||
|
|
||||||
/* Quotes */
|
/* Quotes */
|
||||||
blockquote {
|
blockquote {
|
||||||
|
|
@ -308,7 +308,8 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;}
|
||||||
.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
|
.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
|
||||||
.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
|
.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
|
||||||
.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
|
.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
|
||||||
.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.org');}
|
.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
|
||||||
|
.social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
About
|
About
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.hll {
|
.hll {
|
||||||
background-color:#FFFFCC;
|
background-color:#eee;
|
||||||
}
|
}
|
||||||
.c {
|
.c {
|
||||||
color:#408090;
|
color:#408090;
|
||||||
|
|
|
||||||
BIN
tests/output/custom/theme/images/icons/gittip.png
Normal file
BIN
tests/output/custom/theme/images/icons/gittip.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 671 B |
|
|
@ -5,7 +5,7 @@ from .support import unittest
|
||||||
from pelican.contents import Page, Article
|
from pelican.contents import Page, Article
|
||||||
from pelican.settings import _DEFAULT_CONFIG
|
from pelican.settings import _DEFAULT_CONFIG
|
||||||
from pelican.utils import truncate_html_words
|
from pelican.utils import truncate_html_words
|
||||||
|
from pelican.signals import content_object_init
|
||||||
from jinja2.utils import generate_lorem_ipsum
|
from jinja2.utils import generate_lorem_ipsum
|
||||||
|
|
||||||
# generate one paragraph, enclosed with <p>
|
# generate one paragraph, enclosed with <p>
|
||||||
|
|
@ -158,6 +158,17 @@ class TestPage(unittest.TestCase):
|
||||||
|
|
||||||
return page_kwargs
|
return page_kwargs
|
||||||
|
|
||||||
|
def test_signal(self):
|
||||||
|
"""If a title is given, it should be used to generate the slug."""
|
||||||
|
|
||||||
|
def receiver_test_function(sender,instance):
|
||||||
|
pass
|
||||||
|
|
||||||
|
content_object_init.connect(receiver_test_function ,sender=Page)
|
||||||
|
page = Page(**self.page_kwargs)
|
||||||
|
self.assertTrue(content_object_init.has_receivers_for(Page))
|
||||||
|
|
||||||
|
|
||||||
class TestArticle(TestPage):
|
class TestArticle(TestPage):
|
||||||
def test_template(self):
|
def test_template(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
||||||
[u'Article title', 'published', 'Default', 'article'],
|
[u'Article title', 'published', 'Default', 'article'],
|
||||||
[u'Article with template', 'published', 'Default', 'custom'],
|
[u'Article with template', 'published', 'Default', 'custom'],
|
||||||
[u'Test md File', 'published', 'test', 'article'],
|
[u'Test md File', 'published', 'test', 'article'],
|
||||||
|
[u'Test Markdown extensions', 'published', u'Default', 'article'],
|
||||||
[u'This is a super article !', 'published', 'Yeah', 'article'],
|
[u'This is a super article !', 'published', 'Yeah', 'article'],
|
||||||
[u'This is an article with category !', 'published', 'yeah', 'article'],
|
[u'This is an article with category !', 'published', 'yeah', 'article'],
|
||||||
[u'This is an article without category !', 'published', 'Default', 'article'],
|
[u'This is an article without category !', 'published', 'Default', 'article'],
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,11 @@ class TestPelican(unittest.TestCase):
|
||||||
with patch("pelican.contents.getenv") as mock_getenv:
|
with patch("pelican.contents.getenv") as mock_getenv:
|
||||||
# force getenv('USER') to always return the same value
|
# force getenv('USER') to always return the same value
|
||||||
mock_getenv.return_value = "Dummy Author"
|
mock_getenv.return_value = "Dummy Author"
|
||||||
pelican = Pelican(path=INPUT_PATH, output_path=self.temp_path)
|
settings = read_settings(filename=None, override={
|
||||||
|
'PATH': INPUT_PATH,
|
||||||
|
'OUTPUT_PATH': self.temp_path,
|
||||||
|
})
|
||||||
|
pelican = Pelican(settings=settings)
|
||||||
pelican.run()
|
pelican.run()
|
||||||
diff = dircmp(
|
diff = dircmp(
|
||||||
self.temp_path, os.sep.join((OUTPUT_PATH, "basic")))
|
self.temp_path, os.sep.join((OUTPUT_PATH, "basic")))
|
||||||
|
|
@ -63,8 +67,11 @@ class TestPelican(unittest.TestCase):
|
||||||
|
|
||||||
def test_custom_generation_works(self):
|
def test_custom_generation_works(self):
|
||||||
# the same thing with a specified set of settings should work
|
# the same thing with a specified set of settings should work
|
||||||
pelican = Pelican(path=INPUT_PATH, output_path=self.temp_path,
|
settings = read_settings(filename=SAMPLE_CONFIG, override={
|
||||||
settings=read_settings(SAMPLE_CONFIG))
|
'PATH': INPUT_PATH,
|
||||||
|
'OUTPUT_PATH': self.temp_path,
|
||||||
|
})
|
||||||
|
pelican = Pelican(settings=settings)
|
||||||
pelican.run()
|
pelican.run()
|
||||||
diff = dircmp(self.temp_path, os.sep.join((OUTPUT_PATH, "custom")))
|
diff = dircmp(self.temp_path, os.sep.join((OUTPUT_PATH, "custom")))
|
||||||
self.assertFilesEqual(diff)
|
self.assertFilesEqual(diff)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class RstReaderTest(unittest.TestCase):
|
||||||
class MdReaderTest(unittest.TestCase):
|
class MdReaderTest(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
|
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
|
||||||
def test_article_with_md_extention(self):
|
def test_article_with_md_extension(self):
|
||||||
# test to ensure the md extension is being processed by the correct reader
|
# test to ensure the md extension is being processed by the correct reader
|
||||||
reader = readers.MarkdownReader({})
|
reader = readers.MarkdownReader({})
|
||||||
content, metadata = reader.read(_filename('article_with_md_extension.md'))
|
content, metadata = reader.read(_filename('article_with_md_extension.md'))
|
||||||
|
|
@ -90,3 +90,22 @@ class MdReaderTest(unittest.TestCase):
|
||||||
"<p>This is another markdown test file. Uses the mkd extension.</p>"
|
"<p>This is another markdown test file. Uses the mkd extension.</p>"
|
||||||
|
|
||||||
self.assertEqual(content, expected)
|
self.assertEqual(content, expected)
|
||||||
|
|
||||||
|
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
|
||||||
|
def test_article_with_markdown_markup_extension(self):
|
||||||
|
# test to ensure the markdown markup extension is being processed as expected
|
||||||
|
reader = readers.MarkdownReader({})
|
||||||
|
reader.settings.update(dict(MARKDOWN_EXTENSIONS=['toc', ]))
|
||||||
|
content, metadata = reader.read(_filename('article_with_markdown_markup_extensions.md'))
|
||||||
|
expected = '<div class="toc">\n'\
|
||||||
|
'<ul>\n'\
|
||||||
|
'<li><a href="#level1">Level1</a><ul>\n'\
|
||||||
|
'<li><a href="#level2">Level2</a></li>\n'\
|
||||||
|
'</ul>\n'\
|
||||||
|
'</li>\n'\
|
||||||
|
'</ul>\n'\
|
||||||
|
'</div>\n'\
|
||||||
|
'<h2 id="level1">Level1</h2>\n'\
|
||||||
|
'<h3 id="level2">Level2</h3>'
|
||||||
|
|
||||||
|
self.assertEqual(content, expected)
|
||||||
|
|
|
||||||
|
|
@ -56,16 +56,19 @@ class TestSettingsConfiguration(unittest.TestCase):
|
||||||
def test_configure_settings(self):
|
def test_configure_settings(self):
|
||||||
"""Manipulations to settings should be applied correctly."""
|
"""Manipulations to settings should be applied correctly."""
|
||||||
|
|
||||||
# SITEURL should not have a trailing slash
|
settings = {
|
||||||
settings = {'SITEURL': 'http://blog.notmyidea.org/', 'LOCALE': ''}
|
'SITEURL': 'http://blog.notmyidea.org/',
|
||||||
|
'LOCALE': '',
|
||||||
|
'PATH': '.',
|
||||||
|
'THEME': DEFAULT_THEME,
|
||||||
|
}
|
||||||
configure_settings(settings)
|
configure_settings(settings)
|
||||||
|
# SITEURL should not have a trailing slash
|
||||||
self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org')
|
self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org')
|
||||||
|
|
||||||
# FEED_DOMAIN, if undefined, should default to SITEURL
|
# FEED_DOMAIN, if undefined, should default to SITEURL
|
||||||
settings = {'SITEURL': 'http://blog.notmyidea.org', 'LOCALE': ''}
|
|
||||||
configure_settings(settings)
|
|
||||||
self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org')
|
self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org')
|
||||||
|
|
||||||
settings = {'FEED_DOMAIN': 'http://feeds.example.com', 'LOCALE': ''}
|
settings['FEED_DOMAIN'] = 'http://feeds.example.com'
|
||||||
configure_settings(settings)
|
configure_settings(settings)
|
||||||
self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com')
|
self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com')
|
||||||
|
|
|
||||||
|
|
@ -112,3 +112,16 @@ class TestUtils(unittest.TestCase):
|
||||||
self.assertTrue(os.path.isdir(test_directory))
|
self.assertTrue(os.path.isdir(test_directory))
|
||||||
self.assertListEqual([], os.listdir(test_directory))
|
self.assertListEqual([], os.listdir(test_directory))
|
||||||
shutil.rmtree(test_directory)
|
shutil.rmtree(test_directory)
|
||||||
|
|
||||||
|
def test_clean_output_dir_not_there(self):
|
||||||
|
test_directory = os.path.join(os.path.dirname(__file__), 'does_not_exist')
|
||||||
|
utils.clean_output_dir(test_directory)
|
||||||
|
self.assertTrue(not os.path.exists(test_directory))
|
||||||
|
|
||||||
|
def test_clean_output_dir_is_file(self):
|
||||||
|
test_directory = os.path.join(os.path.dirname(__file__), 'this_is_a_file')
|
||||||
|
f = open(test_directory, 'w')
|
||||||
|
f.write('')
|
||||||
|
f.close()
|
||||||
|
utils.clean_output_dir(test_directory)
|
||||||
|
self.assertTrue(not os.path.exists(test_directory))
|
||||||
|
|
|
||||||
10
tox.ini
10
tox.ini
|
|
@ -2,14 +2,14 @@
|
||||||
envlist = py26,py27
|
envlist = py26,py27
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands = nosetests -s tests
|
commands =
|
||||||
|
nosetests -s tests
|
||||||
|
unit2 discover []
|
||||||
deps =
|
deps =
|
||||||
nose
|
nose
|
||||||
Jinja2
|
|
||||||
Pygments
|
|
||||||
docutils
|
|
||||||
feedgenerator
|
|
||||||
unittest2
|
unittest2
|
||||||
mock
|
mock
|
||||||
Markdown
|
Markdown
|
||||||
BeautifulSoup
|
BeautifulSoup
|
||||||
|
typogrify
|
||||||
|
webassets
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue