mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
merged from upstream, rm bat files
This commit is contained in:
commit
8c8ea52551
43 changed files with 920 additions and 704 deletions
26
CHANGELOG
26
CHANGELOG
|
|
@ -1,9 +1,27 @@
|
|||
3.0
|
||||
X.X
|
||||
|
||||
* Refactored the way URL are handled.
|
||||
* Improved the english documentation
|
||||
* Fixed packaging using setuptools entrypoints
|
||||
|
||||
2.8
|
||||
|
||||
* dotclear importer
|
||||
* Markdown extensions
|
||||
* Theme extensions
|
||||
* Plugins support
|
||||
* Allow the usage of markdown extensions
|
||||
* Themes are now easily extensible
|
||||
* Don't output pagination information if there is only one page.
|
||||
* Add a page per author, with all their articles
|
||||
* Improved the test suite
|
||||
* Made the themes more easy to extend
|
||||
* Removed Skribit support
|
||||
* Added a "pelican-quickstart" script
|
||||
* Fixed timezone-related issues
|
||||
* Add some scripts for windows support
|
||||
* Date can be specified in seconds
|
||||
* Never fail when generating posts (skip and continue)
|
||||
* Allow the use of future dates
|
||||
* Support having different timezones per languages.
|
||||
* Enhanced the documentation
|
||||
|
||||
2.7
|
||||
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ include *.rst
|
|||
global-include *.py
|
||||
recursive-include pelican *.html *.css *png
|
||||
include LICENSE
|
||||
global-include *.bat
|
||||
|
|
|
|||
50
README.rst
50
README.rst
|
|
@ -3,51 +3,55 @@ Pelican
|
|||
|
||||
Pelican is a simple weblog generator, written in `Python <http://www.python.org/>`_.
|
||||
|
||||
* Write your weblog entries directly with your editor of choice (vim!) and
|
||||
directly in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_, or `Markdown <http://daringfireball.net/projects/markdown/>`_.
|
||||
* A simple cli-tool to (re)generate the weblog.
|
||||
* Write your weblog entries directly with your editor of choice (vim!)
|
||||
in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ or `Markdown <http://daringfireball.net/projects/markdown/>`_
|
||||
* Includes a simple CLI tool to (re)generate the weblog
|
||||
* Easy to interface with DVCSes and web hooks
|
||||
* Completely static output, so easy to host anywhere !
|
||||
* Completely static output is easy to host anywhere
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Pelican currently supports:
|
||||
|
||||
* blog articles and pages
|
||||
* comments, via an external service (disqus). Please notice that while
|
||||
it's useful, it's an external service, and you'll not manage the
|
||||
comments by yourself. It could potentially eat your data.
|
||||
* theming support (themes are done using `jinja2 <http://jinjna.pocoo.org>`_)
|
||||
* PDF generation of the articles/pages (optional).
|
||||
* Translations
|
||||
* Syntactic recognition
|
||||
* Blog articles and pages
|
||||
* Comments, via an external service (Disqus). (Please note that while
|
||||
useful, Disqus is an external service, and thus the comment data will be
|
||||
somewhat outside of your control and potentially subject to data loss.)
|
||||
* Theming support (themes are created using `jinja2 <http://jinja.pocoo.org/>`_)
|
||||
* PDF generation of the articles/pages (optional)
|
||||
* Publication of articles in multiple languages
|
||||
* Atom/RSS feeds
|
||||
* Code syntax highlighting
|
||||
* Import from WordPress, Dotclear, or RSS feeds
|
||||
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
|
||||
|
||||
Have a look to `the documentation <http://alexis.notmyidea.org/pelican/>`_ for
|
||||
more informations.
|
||||
Have a look at `the documentation <http://pelican.notmyidea.org/en/latest/>`_ for
|
||||
more information.
|
||||
|
||||
Why the name "Pelican" ?
|
||||
Why the name "Pelican"?
|
||||
------------------------
|
||||
|
||||
Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
|
||||
Heh, you didn't notice? "Pelican" is an anagram for « Calepin » ;)
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
You can access the source code via git on http://github.com/ametaireau/pelican/
|
||||
You can access the source code via git at: https://github.com/ametaireau/pelican
|
||||
|
||||
If you feel hackish, have a look to the `pelican's internals explanations
|
||||
<http://alexis.notmyidea.org/pelican/internals.html>`_.
|
||||
If you feel hackish, have a look at the explanation of `Pelican's internals
|
||||
<http://pelican.notmyidea.org/en/latest/internals.html>`_.
|
||||
|
||||
Feedback / Contact us
|
||||
=====================
|
||||
---------------------
|
||||
|
||||
If you want to see new features in Pelican, dont hesitate to tell me, to clone
|
||||
the repository, etc. That's open source, dude!
|
||||
If you want to see new features in Pelican, don't hesitate to offer suggestions,
|
||||
clone the repository, etc. There are many ways to `contribute
|
||||
<http://pelican.notmyidea.org/en/latest/contribute.html>`_. That's open source, dude!
|
||||
|
||||
Contact me at "alexis at notmyidea dot org" for any request/feedback! You can
|
||||
also join the team at `#pelican on irc.freenode.org
|
||||
<irc://irc.freenode.net/pelican>`_
|
||||
(or if you don't have any IRC client, using `the webchat
|
||||
(or if you don't have any IRC client, use `the webchat
|
||||
<http://webchat.freenode.net/?channels=pelican&uio=d4>`_)
|
||||
for quick feedback.
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from pelican import main
|
||||
main()
|
||||
|
|
@ -1 +0,0 @@
|
|||
@pypy "%~dpn0" %*
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
How to contribute ?
|
||||
How to contribute?
|
||||
###################
|
||||
There are many ways to contribute to pelican. You can enhance the
|
||||
documentation, add missing features, fix bugs or just report them.
|
||||
There are many ways to contribute to Pelican. You can enhance the
|
||||
documentation, add missing features, and fix bugs (or just report them).
|
||||
|
||||
Don't hesitate to fork and make a pull request on github.
|
||||
Don't hesitate to fork and make a pull request on GitHub.
|
||||
|
||||
Set up the development environment
|
||||
==================================
|
||||
Setting up the development environment
|
||||
======================================
|
||||
|
||||
You're free to setup up the environment in any way you like. Here is a way
|
||||
using virtualenv and virtualenvwrapper. If you don't have them, you can install
|
||||
them using::
|
||||
You're free to set up your development environment any way you like. Here is a
|
||||
way using virtualenv and virtualenvwrapper. If you don't have them, you can
|
||||
install these packages via::
|
||||
|
||||
$ pip install virtualenvwrapper
|
||||
|
||||
Virtual environments allow you to work on an installation of python which is
|
||||
not the one installed on your system. Especially, it will install the different
|
||||
projects under a different location.
|
||||
Virtual environments allow you to work on Python projects which are isolated
|
||||
from one another so you can use different packages (and package versions) with
|
||||
different projects.
|
||||
|
||||
To create the virtualenv environment, you have to do::
|
||||
To create a virtual environment, use the following syntax::
|
||||
|
||||
$ mkvirtualenv pelican --no-site-package
|
||||
$ mkvirtualenv pelican
|
||||
|
||||
Then you would have to install all the dependencies::
|
||||
To manually install the dependencies::
|
||||
|
||||
$ pip install -r dev_requirements.txt
|
||||
$ python setup.py develop
|
||||
|
|
@ -31,10 +31,19 @@ Running the test suite
|
|||
======================
|
||||
|
||||
Each time you add a feature, there are two things to do regarding tests:
|
||||
checking that the tests run in a right way, and be sure that you add tests for
|
||||
the feature you are working on or the bug you're fixing.
|
||||
checking that the existing tests pass, and adding tests for your new feature
|
||||
or for the bug you're fixing.
|
||||
|
||||
The tests leaves under "pelican/tests" and you can run them using the
|
||||
The tests live in "pelican/tests" and you can run them using the
|
||||
"discover" feature of unittest2::
|
||||
|
||||
$ unit2 discover
|
||||
|
||||
Coding standards
|
||||
================
|
||||
|
||||
Try to respect what is described in the PEP8
|
||||
(http://www.python.org/dev/peps/pep-0008/) when providing patches. This can be
|
||||
eased by the pep8 tool (http://pypi.python.org/pypi/pep8) or by Flake8, which
|
||||
will give you some other cool hints about what's good or wrong
|
||||
(http://pypi.python.org/pypi/flake8/)
|
||||
|
|
|
|||
55
docs/faq.rst
55
docs/faq.rst
|
|
@ -1,50 +1,51 @@
|
|||
Frequently Asked Questions (FAQ)
|
||||
################################
|
||||
|
||||
Here is a summary of the frequently asked questions for pelican.
|
||||
Here is a summary of the frequently asked questions for Pelican.
|
||||
|
||||
Is it mandatory to have a configuration file ?
|
||||
==============================================
|
||||
Is it mandatory to have a configuration file?
|
||||
=============================================
|
||||
|
||||
No, it's not. Configurations files are just an easy way to configure pelican.
|
||||
For the basic operations, it's possible to specify options while invoking
|
||||
pelican with the command line (see `pelican --help` for more informations about
|
||||
that)
|
||||
No, it's not. Configuration files are just an easy way to configure Pelican.
|
||||
For basic operations, it's possible to specify options while invoking Pelican
|
||||
via the command line. See `pelican --help` for more information.
|
||||
|
||||
I'm creating my own theme, how to use pygments ?
|
||||
================================================
|
||||
I'm creating my own theme. How do I use Pygments for syntax highlighting?
|
||||
=========================================================================
|
||||
|
||||
Pygment add some classes to the generated content, so the theming of your theme
|
||||
will be done thanks to a css file. You can have a look to the one proposed by
|
||||
default `on the project website <http://pygments.org/demo/15101/>`_
|
||||
Pygments adds some classes to the generated content. These classes are used by
|
||||
themes to style code syntax highlighting via CSS. Specifically, you can
|
||||
customize the appearance of your syntax highlighting via the `.codehilite pre`
|
||||
class in your theme's CSS file. To see how various styles can be used to render
|
||||
Django code, for example, you can use the demo `on the project website
|
||||
<http://pygments.org/demo/15101/>`_.
|
||||
|
||||
How do I create my own theme ?
|
||||
How do I create my own theme?
|
||||
==============================
|
||||
|
||||
Please refer yourself to :ref:`theming-pelican`.
|
||||
Please refer to :ref:`theming-pelican`.
|
||||
|
||||
How can I help ?
|
||||
How can I help?
|
||||
================
|
||||
|
||||
You have different options to help. First, you can use pelican, and report any
|
||||
idea or problem you have on `the bugtracker
|
||||
There are several ways to help out. First, you can use Pelican and report any
|
||||
suggestions or problems you might have on `the bugtracker
|
||||
<http://github.com/ametaireau/pelican/issues>`_.
|
||||
|
||||
If you want to contribute, please have a look to `the git repository
|
||||
<https://github.com/ametaireau/pelican/>`_, fork it, add your changes and do
|
||||
a pull request, I'll review them as soon as possible.
|
||||
If you want to contribute, please fork `the git repository
|
||||
<https://github.com/ametaireau/pelican/>`_, make your changes, and issue
|
||||
a pull request. I'll review your changes as soon as possible.
|
||||
|
||||
You can also contribute by creating themes, and making the documentation
|
||||
better.
|
||||
You can also contribute by creating themes and improving the documentation.
|
||||
|
||||
I want to use markdown, but I got an error
|
||||
==========================================
|
||||
I want to use Markdown, but I got an error.
|
||||
===========================================
|
||||
|
||||
Markdown is not a hard dependency for pelican, so you will need to install it
|
||||
by yourself. You can do so by typing::
|
||||
Markdown is not a hard dependency for Pelican, so you will need to explicitly
|
||||
install it. You can do so by typing::
|
||||
|
||||
$ (sudo) pip install markdown
|
||||
|
||||
In case you don't have pip installed, consider installing it by doing::
|
||||
In case you don't have pip installed, consider installing it via::
|
||||
|
||||
$ (sudo) easy_install pip
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ Getting started
|
|||
Installing
|
||||
==========
|
||||
|
||||
You're ready? Let's go ! You can install pelican in a lot of different ways,
|
||||
the simpler one is via `pip <http://pip.openplans.org/>`_::
|
||||
You're ready? Let's go! You can install Pelican via several different methods. The simplest is via `pip <http://pip.openplans.org/>`_::
|
||||
|
||||
$ pip install pelican
|
||||
|
||||
If you have the sources, you can install pelican using the distutils command
|
||||
install. I recommend to do so in a virtualenv::
|
||||
If you have the project source, you can install Pelican using the distutils
|
||||
method. I recommend doing so in a virtualenv::
|
||||
|
||||
$ virtualenv pelican_venv
|
||||
$ source bin/activate
|
||||
|
|
@ -19,30 +18,30 @@ install. I recommend to do so in a virtualenv::
|
|||
Dependencies
|
||||
------------
|
||||
|
||||
At this time, pelican is dependent of the following python packages:
|
||||
At this time, Pelican is dependent on the following Python packages:
|
||||
|
||||
* feedgenerator, to generate the ATOM feeds.
|
||||
* jinja2, for templating support.
|
||||
* feedgenerator, to generate the Atom feeds
|
||||
* jinja2, for templating support
|
||||
|
||||
If you're not using python 2.7, you will also need `argparse`.
|
||||
If you're not using Python 2.7, you will also need `argparse`.
|
||||
|
||||
Optionally:
|
||||
|
||||
* docutils, for reST support
|
||||
* pygments, to have syntactic colorization with resT input
|
||||
* Markdown, for Markdown as an input format
|
||||
* pygments, for syntax highlighting
|
||||
* docutils, for supporting reStructuredText as an input format
|
||||
* Markdown, for supporting Markdown as an input format
|
||||
|
||||
Writing articles using pelican
|
||||
Writing articles using Pelican
|
||||
==============================
|
||||
|
||||
Files metadata
|
||||
File metadata
|
||||
--------------
|
||||
|
||||
Pelican tries to be smart enough to get the informations it needs from the
|
||||
file system (for instance, about the category of your articles), but you need to
|
||||
provide by hand some of those informations in your files.
|
||||
Pelican tries to be smart enough to get the information it needs from the
|
||||
file system (for instance, about the category of your articles), but some
|
||||
information you need to provide in the form of metadata inside your files.
|
||||
|
||||
You could provide the metadata in the restructured text files, using the
|
||||
You can provide this metadata in reStructuredText text files via the
|
||||
following syntax (give your file the `.rst` extension)::
|
||||
|
||||
My super title
|
||||
|
|
@ -54,31 +53,35 @@ following syntax (give your file the `.rst` extension)::
|
|||
:author: Alexis Metaireau
|
||||
|
||||
|
||||
You can also use a markdown syntax (with a file ending in `.md`)::
|
||||
You can also use Markdown syntax (with a file ending in `.md`)::
|
||||
|
||||
Date: 2010-12-03
|
||||
Title: My super title
|
||||
Tags: thats, awesome
|
||||
Slug: my-super-post
|
||||
|
||||
Put you content here.
|
||||
This is the content of my super blog post.
|
||||
|
||||
Note that none of those are mandatory: if the date is not specified, pelican will
|
||||
rely on the mtime of your file, and the category can also be determined by the
|
||||
directory where the rst file is. For instance, the category of
|
||||
`python/foobar/myfoobar.rst` is `foobar`.
|
||||
Note that, aside from the title, none of this metadata is mandatory: if the date
|
||||
is not specified, Pelican will rely on the file's "mtime" timestamp, and the
|
||||
category can be determined by the directory in which the file resides. For
|
||||
example, a file located at `python/foobar/myfoobar.rst` will have a category of
|
||||
`foobar`.
|
||||
|
||||
Generate your blog
|
||||
------------------
|
||||
|
||||
To launch pelican, just use the `pelican` command::
|
||||
To launch Pelican, just use the `pelican` command::
|
||||
|
||||
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
|
||||
|
||||
And… that's all! You can see your weblog generated on the `content/` folder.
|
||||
And… that's all! Your weblog will be generated and saved in the `content/`
|
||||
folder.
|
||||
|
||||
This one will just generate a simple output, with the default theme. It's not
|
||||
really sexy, as it's a simple HTML output (without any style).
|
||||
The above command will use the default theme to produce a simple site. It's not
|
||||
very sexy, as it's just simple HTML output (without any style).
|
||||
|
||||
You can create your own style if you want, have a look to the help to see all
|
||||
You can create your own style if you want. Have a look at the help to see all
|
||||
the options you can use::
|
||||
|
||||
$ pelican --help
|
||||
|
|
@ -88,7 +91,7 @@ Kickstart a blog
|
|||
|
||||
You also can use the `pelican-quickstart` script to start a new blog in
|
||||
seconds, by just answering few questions. Just run `pelican-quickstart` and
|
||||
you're done! (Added in pelican 3)
|
||||
you're done! (Added in Pelican 3.0)
|
||||
|
||||
Pages
|
||||
-----
|
||||
|
|
@ -102,26 +105,26 @@ the menu.
|
|||
Importing an existing blog
|
||||
--------------------------
|
||||
|
||||
It is possible to import your blog from dotclear, wordpress and an RSS feed using
|
||||
It is possible to import your blog from Dotclear, WordPress, and RSS feeds using
|
||||
a simple script. See :ref:`import`.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
It is possible to translate articles. To do so, you need to add a `lang` meta
|
||||
in your articles/pages, and to set a `DEFAULT_LANG` setting (which is en by
|
||||
default).
|
||||
Then, only articles with this default language will be listed, and
|
||||
each article will have a translation list.
|
||||
attribute to your articles/pages and set a `DEFAULT_LANG` setting (which is
|
||||
English [en] by default). With those settings in place, only articles with the
|
||||
default language will be listed, and each article will be accompanied by a list
|
||||
of available translations for that article.
|
||||
|
||||
Pelican uses the "slug" of two articles to compare if they are translations of
|
||||
each others. So it's possible to define (in restructured text) the slug
|
||||
directly.
|
||||
Pelican uses the article's URL "slug" to determine if two or more articles are
|
||||
translations of one another. The slug can be set manually in the file's
|
||||
metadata; if not set explicitly, Pelican will auto-generate the slug from the
|
||||
title of the article.
|
||||
|
||||
Here is an exemple of two articles (one in english and the other one in
|
||||
french).
|
||||
Here is an example of two articles, one in English and the other in French.
|
||||
|
||||
The english one::
|
||||
The English article::
|
||||
|
||||
Foobar is not dead
|
||||
##################
|
||||
|
|
@ -129,9 +132,9 @@ The english one::
|
|||
:slug: foobar-is-not-dead
|
||||
:lang: en
|
||||
|
||||
That's true, foobar is still alive !
|
||||
That's true, foobar is still alive!
|
||||
|
||||
And the french one::
|
||||
And the French version::
|
||||
|
||||
Foobar n'est pas mort !
|
||||
#######################
|
||||
|
|
@ -141,56 +144,67 @@ And the french one::
|
|||
|
||||
Oui oui, foobar est toujours vivant !
|
||||
|
||||
Despite the text quality, you can see that only the slug is the same here.
|
||||
You're not forced to define the slug that way, and it's completely possible to
|
||||
have two translations with the same title (which defines the slug)
|
||||
Post content quality notwithstanding, you can see that only item in common
|
||||
between the two articles is the slug, which is functioning here as an
|
||||
identifier. If you'd rather not explicitly define the slug this way, you must
|
||||
then instead ensure that the translated article titles are identical, since the
|
||||
slug will be auto-generated from the article title.
|
||||
|
||||
Syntactic recognition
|
||||
Syntax highlighting
|
||||
---------------------
|
||||
|
||||
Pelican is able to regognise the syntax you are using, and to colorize the
|
||||
right way your block codes. To do so, you have to use the following syntax::
|
||||
Pelican is able to provide colorized syntax highlighting for your code blocks.
|
||||
To do so, you have to use the following convention for reStructuredText::
|
||||
|
||||
.. code-block:: identifier
|
||||
|
||||
your code goes here
|
||||
|
||||
The identifier is one of the lexers available `here
|
||||
<http://pygments.org/docs/lexers/>`_.
|
||||
For Markdown, format your code blocks thusly:
|
||||
|
||||
You also can use the default `::` syntax::
|
||||
::identifier
|
||||
your code goes here
|
||||
|
||||
The specified identifier should be one that appears on the
|
||||
`list of available lexers <http://pygments.org/docs/lexers/>`_.
|
||||
|
||||
You also can use the default `::` syntax, in which case it will be assumed
|
||||
that your code is written in Python. For reStructuredText::
|
||||
|
||||
::
|
||||
|
||||
your code goes here
|
||||
|
||||
It will be assumed that your code is witten in python.
|
||||
For Markdown:
|
||||
|
||||
::
|
||||
your code goes here
|
||||
|
||||
Autoreload
|
||||
----------
|
||||
|
||||
It's possible to tell pelican to watch for your modifications, instead of
|
||||
manually launching it each time you need. Use the `-r` option, or
|
||||
`--autoreload`.
|
||||
It's possible to tell Pelican to watch for your modifications, instead of
|
||||
manually launching it every time you want to see your changes. To enable this,
|
||||
run the `pelican` command with the `-r` or `--autoreload` options.
|
||||
|
||||
Publishing drafts
|
||||
-----------------
|
||||
|
||||
If you want to publish an article as a draft, for friends to review it for
|
||||
instance, you can add a ``status: draft`` to its metadata, it will then be
|
||||
available under the ``drafts`` folder, and not be listed under the index page nor
|
||||
any category page.
|
||||
If you want to publish an article as a draft (for friends to review before
|
||||
publishing, for example), you can add a ``status: draft`` attribute to its
|
||||
metadata. That article will then be output to the ``drafts`` folder and not
|
||||
listed on the index page nor on any category page.
|
||||
|
||||
Viewing the generated files
|
||||
---------------------------
|
||||
|
||||
The files generated by pelican are static files, so you don't actually need
|
||||
something special to see what's hapenning with the generated files.
|
||||
The files generated by Pelican are static files, so you don't actually need
|
||||
anything special to see what's happening with the generated files.
|
||||
|
||||
You can either run your browser on the files on your disk::
|
||||
You can either use your browser to open the files on your disk::
|
||||
|
||||
$ firefox output/index.html
|
||||
|
||||
Or run a simple web server using python::
|
||||
Or run a simple web server using Python::
|
||||
|
||||
cd output && python -m SimpleHTTPServer
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ Description
|
|||
``pelican-import`` is a command line tool for converting articles from other
|
||||
software to ReStructuredText. The supported formats are:
|
||||
|
||||
- Wordpress XML export
|
||||
- WordPress XML export
|
||||
- Dotclear export
|
||||
- RSS/ATOM feed
|
||||
- RSS/Atom feed
|
||||
|
||||
The conversion from HTML to ReStructuredText relies on `pandoc
|
||||
The conversion from HTML to reStructuredText relies on `pandoc
|
||||
<http://johnmacfarlane.net/pandoc/>`_. For Dotclear, if the source posts are
|
||||
written with Markdown syntax, they will not be converted (as Pelican also
|
||||
supports Markdown).
|
||||
|
|
@ -40,7 +40,7 @@ Optional arguments:
|
|||
Examples
|
||||
========
|
||||
|
||||
for Wordpress::
|
||||
for WordPress::
|
||||
|
||||
$ pelican-import --wpfile -o ~/output ~/posts.xml
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +1,58 @@
|
|||
Pelican
|
||||
#######
|
||||
|
||||
Pelican is a simple weblog generator, written in python.
|
||||
Pelican is a simple weblog generator, written in Python.
|
||||
|
||||
* Write your weblog entries directly with your editor of choice (vim!) and
|
||||
directly in restructured text, or markdown.
|
||||
* A simple cli-tool to (re)generate the weblog.
|
||||
* Write your weblog entries directly with your editor of choice (vim!) in
|
||||
reStructuredText or Markdown
|
||||
* A simple CLI tool to (re)generate the weblog
|
||||
* Easy to interface with DVCSes and web hooks
|
||||
* Completely static output, so easy to host anywhere !
|
||||
* Completely static output is easy to host anywhere
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
Pelican currently supports:
|
||||
|
||||
* blog articles and simple pages
|
||||
* comments, via an external service (disqus). Please notice that while
|
||||
it's useful, it's an external service, and you'll not manage the
|
||||
comments by yourself. It could potentially eat your data. (optional)
|
||||
* easy theming (themes are done using `jinja2 <http://jinja.pocoo.org>`_)
|
||||
* PDF generation of the articles/pages (optional).
|
||||
* publication of articles in various languages
|
||||
* RSS/Atom feeds
|
||||
* wordpress/dotclear or RSS imports
|
||||
* integration with various tools: twitter/google analytics (optional)
|
||||
* Blog articles and pages
|
||||
* Comments, via an external service (Disqus). (Please note that while
|
||||
useful, Disqus is an external service, and thus the comment data will be
|
||||
somewhat outside of your control and potentially subject to data loss.)
|
||||
* Theming support (themes are created using `jinja2 <http://jinja.pocoo.org/>`_)
|
||||
* PDF generation of the articles/pages (optional)
|
||||
* Publication of articles in multiple languages
|
||||
* Atom/RSS feeds
|
||||
* Code syntax highlighting
|
||||
* Import from WordPress, Dotclear, or RSS feeds
|
||||
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
|
||||
|
||||
Why the name "Pelican" ?
|
||||
========================
|
||||
|
||||
Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
|
||||
Heh, you didn't notice? "Pelican" is an anagram for « Calepin » ;)
|
||||
|
||||
Source code
|
||||
===========
|
||||
|
||||
You can access the source code via git on http://github.com/ametaireau/pelican/
|
||||
You can access the source code via git at http://github.com/ametaireau/pelican/
|
||||
|
||||
Feedback / Contact us
|
||||
=====================
|
||||
|
||||
If you want to see new features in Pelican, dont hesitate to tell me, to clone
|
||||
If you want to see new features in Pelican, don't hesitate to tell me, to clone
|
||||
the repository, etc. That's open source, dude!
|
||||
|
||||
Contact me at "alexis at notmyidea dot org" for any request/feedback! You can
|
||||
also join the team at `#pelican on irc.freenode.org
|
||||
<irc://irc.freenode.net/pelican>`_
|
||||
(or if you don't have any IRC client, using `the webchat
|
||||
(or if you don't have any IRC client, use `the webchat
|
||||
<http://webchat.freenode.net/?channels=pelican&uio=d4>`_)
|
||||
for quick feedback.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
A french version of the documentation is available at :doc:`fr/index`.
|
||||
A French version of the documentation is available at :doc:`fr/index`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
|
|||
|
|
@ -1,49 +1,48 @@
|
|||
Pelican internals
|
||||
#################
|
||||
|
||||
This section describe how pelican is working internally. As you'll see, it's
|
||||
quite simple, but a bit of documentation doesn't hurt :)
|
||||
This section describe how Pelican works internally. As you'll see, it's
|
||||
quite simple, but a bit of documentation doesn't hurt. :)
|
||||
|
||||
You can also find in :doc:`report` an excerpt of a report the original author
|
||||
wrote, with some software design information.
|
||||
You can also find in the :doc:`report` section an excerpt of a report the
|
||||
original author wrote with some software design information.
|
||||
|
||||
.. _report: :doc:`report`
|
||||
|
||||
Overall structure
|
||||
=================
|
||||
|
||||
What `pelican` does, is taking a list of files, and processing them, to some
|
||||
sort of output. Usually, the files are restructured text and markdown files,
|
||||
and the output is a blog, but it can be anything you want.
|
||||
What `pelican` does is take a list of files and process them into some
|
||||
sort of output. Usually, the input files are reStructuredText and Markdown
|
||||
files, and the output is a blog, but both input and output can be anything you
|
||||
want.
|
||||
|
||||
I've separated the logic in different classes and concepts:
|
||||
The logic is separated into different classes and concepts:
|
||||
|
||||
* `writers` are responsible of all the writing process of the
|
||||
files. It's writing .html files, RSS feeds and so on. Since those operations
|
||||
are commonly used, the object is created once, and then passed to the
|
||||
generators.
|
||||
* `writers` are responsible for writing files: .html files, RSS feeds, and so
|
||||
on. Since those operations are commonly used, the object is created once and
|
||||
then passed to the generators.
|
||||
|
||||
* `readers` are used to read from various formats (Markdown, and Restructured
|
||||
Text for now, but the system is extensible). Given a file, they return
|
||||
metadata (author, tags, category etc) and content (HTML formated)
|
||||
* `readers` are used to read from various formats (Markdown and
|
||||
reStructuredText for now, but the system is extensible). Given a file, they return
|
||||
metadata (author, tags, category, etc.) and content (HTML-formatted).
|
||||
|
||||
* `generators` generate the different outputs. For instance, pelican comes with
|
||||
`ArticlesGenerator` and `PageGenerator`, into others. Given
|
||||
a configurations, they can do whatever they want. Most of the time it's
|
||||
generating files from inputs.
|
||||
* `generators` generate the different outputs. For instance, Pelican comes with
|
||||
`ArticlesGenerator` and `PageGenerator`. Given a configuration, they can do
|
||||
whatever they want. Most of the time, it's generating files from inputs.
|
||||
|
||||
* `pelican` also uses `templates`, so it's easy to write you own theme. The
|
||||
* `pelican` also uses `templates`, so it's easy to write your own theme. The
|
||||
syntax is `jinja2`, and, trust me, really easy to learn, so don't hesitate
|
||||
a second.
|
||||
to jump in and build your own theme.
|
||||
|
||||
How to implement a new reader ?
|
||||
===============================
|
||||
How to implement a new reader?
|
||||
==============================
|
||||
|
||||
There is an awesome markup language you want to add to pelican ?
|
||||
Well, the only thing you have to do is to create a class that have a `read`
|
||||
method, that is returning an HTML content and some metadata.
|
||||
Is there an awesome markup language you want to add to Pelican?
|
||||
Well, the only thing you have to do is to create a class with a `read`
|
||||
method that returns HTML content and some metadata.
|
||||
|
||||
Take a look to the Markdown reader::
|
||||
Take a look at the Markdown reader::
|
||||
|
||||
class MarkdownReader(Reader):
|
||||
enabled = bool(Markdown)
|
||||
|
|
@ -63,31 +62,31 @@ Take a look to the Markdown reader::
|
|||
metadata[name.lower()] = meta
|
||||
return content, metadata
|
||||
|
||||
Simple isn't it ?
|
||||
Simple, isn't it?
|
||||
|
||||
If your new reader requires additional Python dependencies then you should wrap
|
||||
their `import` statements in `try...except`. Then inside the reader's class
|
||||
set the `enabled` class attribute to mark import success or failure. This makes
|
||||
it possible for users to continue using their favourite markup method without
|
||||
needing to install modules for all the additional formats they don't use.
|
||||
If your new reader requires additional Python dependencies, then you should wrap
|
||||
their `import` statements in a `try...except` block. Then inside the reader's
|
||||
class, set the `enabled` class attribute to mark import success or failure.
|
||||
This makes it possible for users to continue using their favourite markup method
|
||||
without needing to install modules for formats they don't use.
|
||||
|
||||
How to implement a new generator ?
|
||||
==================================
|
||||
How to implement a new generator?
|
||||
=================================
|
||||
|
||||
Generators have basically two important methods. You're not forced to create
|
||||
both, only the existing ones will be called.
|
||||
Generators have two important methods. You're not forced to create
|
||||
both; only the existing ones will be called.
|
||||
|
||||
* `generate_context`, that is called in a first place, for all the generators.
|
||||
* `generate_context`, that is called first, for all the generators.
|
||||
Do whatever you have to do, and update the global context if needed. This
|
||||
context is shared between all generators, and will be passed to the
|
||||
templates. For instance, the `PageGenerator` `generate_context` method find
|
||||
all the pages, transform them into objects, and populate the context with
|
||||
them. Be careful to *not* output anything using this context at this stage,
|
||||
as it is likely to change by the effect of others generators.
|
||||
templates. For instance, the `PageGenerator` `generate_context` method 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 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,
|
||||
generating the output :) That's here that you may want to look at the context
|
||||
and call the methods of the `writer` object, that is passed at the first
|
||||
* `generate_output` is then called. And guess what is it made for? Oh,
|
||||
generating the output. :) It's here that you may want to look at the context
|
||||
and call the methods of the `writer` object that is passed as the first
|
||||
argument of this function. In the `PageGenerator` example, this method will
|
||||
look at all the pages recorded in the global context, and output a file on
|
||||
look at all the pages recorded in the global context and output a file on
|
||||
the disk (using the writer method `write_file`) for each page encountered.
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ With ``pelican-themes``, you can see the available themes by using the ``-l`` or
|
|||
two-column@
|
||||
simple
|
||||
|
||||
In this example, we can see there is 3 themes available: ``notmyidea``, ``simple`` and ``two-column``.
|
||||
In this example, we can see there are three themes available: ``notmyidea``, ``simple``, and ``two-column``.
|
||||
|
||||
``two-column`` is prefixed with an ``@`` because this theme is not copied to the Pelican theme path, but just linked to it (see `Creating symbolic links`_ for details about creating symbolic links).
|
||||
``two-column`` is prefixed with an ``@`` because this theme is not copied to the Pelican theme path, but is instead just linked to it (see `Creating symbolic links`_ for details about creating symbolic links).
|
||||
|
||||
Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get a 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
|
||||
|
||||
|
|
@ -95,8 +95,8 @@ This option takes as argument the path(s) of the theme(s) you want to install, a
|
|||
Removing themes
|
||||
"""""""""""""""
|
||||
|
||||
Pelican themes can also removes themes from the Pelican themes path.
|
||||
The ``-r`` or ``--remove`` takes as argument the name(s) of the theme(s) you want to remove, and can be combined with the ``--verbose`` option.
|
||||
The ``pelican-themes`` command can also remove themes from the Pelican themes path.
|
||||
The ``-r`` or ``--remove`` option takes as argument the name(s) of the theme(s) you want to remove, and can be combined with the ``--verbose`` option.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ The ``-r`` or ``--remove`` takes as argument the name(s) of the theme(s) you wan
|
|||
Creating symbolic links
|
||||
"""""""""""""""""""""""
|
||||
|
||||
``pelican-themes`` can also install themes by creating symbolic links instead of copying the whole themes in the Pelican themes path.
|
||||
``pelican-themes`` can also install themes by creating symbolic links instead of copying entire themes into the Pelican themes path.
|
||||
|
||||
To symbolically link a theme, you can use the ``-s`` or ``--symlink``, which works exactly as the ``--install`` option:
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ The ``--install``, ``--remove`` and ``--symlink`` option are not mutually exclus
|
|||
--symlink ~/Dev/Python/pelican-themes/two-column \
|
||||
--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``
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,117 +6,139 @@ the command line::
|
|||
|
||||
$ pelican -s path/to/your/settingsfile.py path
|
||||
|
||||
Settings are given as the form of a python module (a file). You can have an
|
||||
Settings are configured in the form of a Python module (a file). You can see an
|
||||
example by looking at `/samples/pelican.conf.py
|
||||
<https://github.com/ametaireau/pelican/raw/master/samples/pelican.conf.py>`_
|
||||
|
||||
All the settings identifiers must be set in caps, otherwise they will not be
|
||||
All the setting identifiers must be set in all-caps, otherwise they will not be
|
||||
processed.
|
||||
|
||||
The settings you define in the configuration file will be passed to the
|
||||
templates, it allows you to use them to add site-wide contents if you need.
|
||||
templates, which allows you to use your settings to add site-wide content.
|
||||
|
||||
Here is a list of settings for pelican, regarding the different features.
|
||||
Here is a list of settings for Pelican:
|
||||
|
||||
Basic settings
|
||||
==============
|
||||
|
||||
================================================ =====================================================
|
||||
Setting name (default value) what does it do?
|
||||
Setting name (default value) What does it do?
|
||||
================================================ =====================================================
|
||||
`ARTICLE_PERMALINK_STRUCTURE` (``''``) Empty by default. Allows to render URLs in a
|
||||
particular way, see below.
|
||||
`AUTHOR` Default author (put your name)
|
||||
`CLEAN_URLS` (``False``) If set to `True`, the URLs will not be suffixed by
|
||||
`.html`, so you will have to setup URL rewriting on
|
||||
your web server.
|
||||
`DATE_FORMATS` (``{}``) If you do manage multiple languages, you can
|
||||
set the date formatting here. See "Date format and locales"
|
||||
section below for details.
|
||||
`DEFAULT_CATEGORY` (``'misc'``) The default category to fallback on.
|
||||
`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on.
|
||||
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
|
||||
`DISPLAY_PAGES_ON_MENU` (``True``) Display or not the pages on the menu of the
|
||||
template. Templates can follow or not this
|
||||
settings.
|
||||
`FALLBACK_ON_FS_DATE` (``True``) If True, pelican will use the file system
|
||||
dates infos (mtime) if it can't get
|
||||
informations from the metadata
|
||||
`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the
|
||||
template. Templates may or not honor this
|
||||
setting.
|
||||
`FALLBACK_ON_FS_DATE` (``True``) If True, Pelican will use the file system
|
||||
timestamp information (mtime) if it can't get
|
||||
date information from the metadata.
|
||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory and just
|
||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory as well as
|
||||
the generated files.
|
||||
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
||||
here or a single string representing one locale.
|
||||
When providing a list, all the locales will be tried
|
||||
until one works.
|
||||
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
|
||||
to use. For the moment, only available values
|
||||
to use. For the moment, the only available values
|
||||
are `rst` and `md`.
|
||||
`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the markdown processor
|
||||
`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the Markdown processor
|
||||
will use. Refer to the extensions chapter in the
|
||||
Python-Markdown documentation for a complete list of
|
||||
supported extensions.
|
||||
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
|
||||
`PATH` (``None``) path to look at for input files.
|
||||
`PATH` (``None``) Path to look at for input files.
|
||||
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
||||
of your documents. You will need to install
|
||||
`rst2pdf`.
|
||||
`RELATIVE_URLS` (``True``) Defines if pelican should use relative urls or
|
||||
`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or
|
||||
not.
|
||||
`SITENAME` (``'A Pelican Blog'``) Your site name
|
||||
`SITEURL` base URL of your website. Note that this is
|
||||
not a way to tell pelican to use relative urls
|
||||
or static ones. You should rather use the
|
||||
`RELATIVE_URL` setting for such use.
|
||||
`SITEURL` Base URL of your website. Note that this is
|
||||
not a way to tell Pelican whether to use relative URLs
|
||||
or static ones. You should instead use the
|
||||
`RELATIVE_URL` setting for that purpose.
|
||||
`STATIC_PATHS` (``['images']``) The static paths you want to have accessible
|
||||
on the output path "static". By default,
|
||||
pelican will copy the 'images' folder to the
|
||||
Pelican will copy the 'images' folder to the
|
||||
output folder.
|
||||
`TIMEZONE` The timezone used in the date information, to
|
||||
generate atom and rss feeds. See the "timezone"
|
||||
generate Atom and RSS feeds. See the "timezone"
|
||||
section below for more info.
|
||||
================================================ =====================================================
|
||||
|
||||
.. [#] Default is the system locale. Default is to delete the output directory.
|
||||
.. [#] Default is the system locale.
|
||||
|
||||
Article permalink structure
|
||||
---------------------------
|
||||
Allow to render articles sorted by date, in case you specify a format as
|
||||
specified in the example. It follows the python datetime directives:
|
||||
|
||||
* %Y: Year with century as a decimal number.
|
||||
* %m: Month as a decimal number [01,12].
|
||||
* %d: Day of the month as a decimal number [01,31].
|
||||
URL Settings
|
||||
------------
|
||||
|
||||
Note: if you specify a datetime directive, it will be substituted using the
|
||||
date metadata field into the rest file. if the date is not specified, pelican
|
||||
will rely on the mtime of your file.
|
||||
You can customize the URL's and locations where files will be saved. The URL's and
|
||||
SAVE_AS variables use python's format strings. These variables allow you to place
|
||||
your articles in a location such as '{slug}/index.html' and link to then as
|
||||
'{slug}' for clean urls. These settings give you the flexibility to place your
|
||||
articles and pages anywhere you want.
|
||||
|
||||
Check the python datetime documentation at http://bit.ly/cNcJUC for more
|
||||
Note: If you specify a datetime directive, it will be substituted using the
|
||||
input files' date metadata attribute. If the date is not specified for a
|
||||
particular file, Pelican will rely on the file's mtime timestamp.
|
||||
|
||||
Check the Python datetime documentation at http://bit.ly/cNcJUC for more
|
||||
information.
|
||||
|
||||
Also, you can use any metadata in the restructured text files:
|
||||
Also, you can use other file metadata attributes as well:
|
||||
|
||||
* category: '%(category)s'
|
||||
* author: '%(author)s'
|
||||
* tags: '%(tags)s'
|
||||
* date: '%(date)s'
|
||||
* slug
|
||||
* date
|
||||
* lang
|
||||
* author
|
||||
* category
|
||||
|
||||
Example usage:
|
||||
|
||||
* '/%Y/%m/' it will be something like '/2011/07/sample-post.html'.
|
||||
* '/%Y/%(category)s/' it will be something like '/2011/life/sample-post.html'.
|
||||
* ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'
|
||||
* 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',
|
||||
and the URL to this would be '/posts/2011/Aug/07/sample-post/'.
|
||||
|
||||
================================================ =====================================================
|
||||
Setting name (default value) what does it do?
|
||||
================================================ =====================================================
|
||||
`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_LANG_URL` ('{slug}-{lang}.html') The URL to refer to an ARTICLE which doesn't use the
|
||||
default language.
|
||||
`ARTICLE_LANG_SAVE_AS` ('{slug}-{lang}.html' The place where we will save an article which
|
||||
doesn't use the default language.
|
||||
`PAGE_URL` ('pages/{slug}.html') The URL we will use to link to a page.
|
||||
`PAGE_SAVE_AS` ('pages/{slug}.html') The location we will save the page.
|
||||
`PAGE_LANG_URL` ('pages/{slug}-{lang}.html') The URL we will use to link to a page which doesn't
|
||||
use the default language.
|
||||
`PAGE_LANG_SAVE_AS` ('pages/{slug}-{lang}.html') The location we will save the page which doesn't
|
||||
use the default language.
|
||||
`AUTHOR_URL` ('author/{name}.html') The URL to use for 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_SAVE_AS` ('category/{name}.html') The location to save a category.
|
||||
`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.
|
||||
================================================ =====================================================
|
||||
|
||||
Timezone
|
||||
--------
|
||||
|
||||
If no timezone is defined, UTC is assumed. This means that the generated atom
|
||||
and rss feeds will have wrong date information if your locale is not UTC.
|
||||
If no timezone is defined, UTC is assumed. This means that the generated Atom
|
||||
and RSS feeds will contain incorrect date information if your locale is not UTC.
|
||||
|
||||
Pelican issues a warning in case this setting is not defined, as it was not
|
||||
mandatory in old versions.
|
||||
mandatory in previous versions.
|
||||
|
||||
Have a look at `the wikipedia page`_ to get a list of values to set your
|
||||
timezone.
|
||||
Have a look at `the wikipedia page`_ to get a list of valid timezone values.
|
||||
|
||||
.. _the wikipedia page: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
|
||||
|
|
@ -124,9 +146,9 @@ timezone.
|
|||
Date format and locale
|
||||
----------------------
|
||||
|
||||
If no DATE_FORMAT is set, fallback to DEFAULT_DATE_FORMAT. If you need to
|
||||
maintain multiple languages with different date format, you can set this dict
|
||||
using language name (``lang`` in your posts) as key. About available format
|
||||
If no DATE_FORMAT is set, fall back to DEFAULT_DATE_FORMAT. If you need to
|
||||
maintain multiple languages with different date formats, you can set this dict
|
||||
using language name (``lang`` in your posts) as key. Regarding available format
|
||||
codes, see `strftime document of python`_ :
|
||||
|
||||
DATE_FORMAT = {
|
||||
|
|
@ -140,8 +162,8 @@ You can set locale to further control date format:
|
|||
'en_US', 'ja_JP' # On Unix/Linux
|
||||
)
|
||||
|
||||
Also, it is possible to set different locale settings for each language, if you
|
||||
put (locale, format) tuple in dict, and this will override the LOCALE setting
|
||||
Also, it is possible to set different locale settings for each language. If you
|
||||
put (locale, format) tuples in the dict, this will override the LOCALE setting
|
||||
above:
|
||||
|
||||
# On Unix/Linux
|
||||
|
|
@ -156,8 +178,9 @@ above:
|
|||
'jp': ('jpn','%Y-%m-%d(%a)'),
|
||||
}
|
||||
|
||||
For available list of `locales on Windows`_ . On Unix/Linux usually you can get
|
||||
a list of available locales with command ``locale -a``, see manpage `locale(1)`_ for help.
|
||||
This is a list of available `locales on Windows`_ . On Unix/Linux, usually you
|
||||
can get a list of available locales via the ``locale -a`` command; see manpage
|
||||
`locale(1)`_ for more information.
|
||||
|
||||
|
||||
.. _strftime document of python: http://docs.python.org/library/datetime.html#strftime-strptime-behavior
|
||||
|
|
@ -169,26 +192,26 @@ a list of available locales with command ``locale -a``, see manpage `locale(1)`_
|
|||
Feed settings
|
||||
=============
|
||||
|
||||
By default, pelican uses atom feeds. However, it is possible to use RSS feeds
|
||||
instead, at your covenience.
|
||||
By default, Pelican uses Atom feeds. However, it is also possible to use RSS
|
||||
feeds if you prefer.
|
||||
|
||||
Pelican generates category feeds as well as feeds for all your articles. It does
|
||||
not generate feeds for tags per default, but it is possible to do so using
|
||||
not generate feeds for tags by default, but it is possible to do so using
|
||||
the ``TAG_FEED`` and ``TAG_FEED_RSS`` settings:
|
||||
|
||||
|
||||
================================================ =====================================================
|
||||
Setting name (default value) what does it do?
|
||||
Setting name (default value) What does it do?
|
||||
================================================ =====================================================
|
||||
`CATEGORY_FEED` ('feeds/%s.atom.xml'[2]_) Where to put the atom categories feeds.
|
||||
`CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the categories rss feeds.
|
||||
`FEED` (``'feeds/all.atom.xml'``) relative url to output the atom feed.
|
||||
`FEED_RSS` (``None``, i.e. no RSS) relative url to output the rss feed.
|
||||
`TAG_FEED` (``None``, ie no tag feed) relative url to output the tags atom feed. It should
|
||||
be defined using a "%s" matchin the tag name
|
||||
`TAG_FEED_RSS` (``None``, ie no RSS tag feed) relative url to output the tag RSS feed
|
||||
`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feeds are
|
||||
unrestricted by default.
|
||||
`CATEGORY_FEED` ('feeds/%s.atom.xml'[2]_) Where to put the category Atom feeds.
|
||||
`CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the category RSS feeds.
|
||||
`FEED` (``'feeds/all.atom.xml'``) Relative URL to output the Atom feed.
|
||||
`FEED_RSS` (``None``, i.e. no RSS) Relative URL to output the RSS feed.
|
||||
`TAG_FEED` (``None``, ie no tag feed) Relative URL to output the tag Atom feed. It should
|
||||
be defined using a "%s" match in the tag name.
|
||||
`TAG_FEED_RSS` (``None``, ie no RSS tag feed) Relative URL to output the tag RSS feed
|
||||
`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feed item
|
||||
quantity is unrestricted by default.
|
||||
================================================ =====================================================
|
||||
|
||||
.. [2] %s is the name of the category.
|
||||
|
|
@ -196,14 +219,15 @@ Setting name (default value) what does it do?
|
|||
Pagination
|
||||
==========
|
||||
|
||||
The default behaviour of pelican is to list all the articles titles alongside
|
||||
with a short description of them on the index page. While it works pretty well
|
||||
for little to medium blogs, it is convenient to have a way to paginate this.
|
||||
The default behaviour of Pelican is to list all the article titles along
|
||||
with a short description on the index page. While it works pretty well
|
||||
for small-to-medium blogs, for sites with large quantity of articles it would
|
||||
be convenient to have a way to paginate the list.
|
||||
|
||||
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
|
||||
last page. Use this when you don't want to
|
||||
|
|
@ -220,11 +244,11 @@ If you want to generate a tag cloud with all your tags, you can do so using the
|
|||
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
|
||||
cloud.
|
||||
`TAG_CLOUD_MAX_ITEMS` (100) Maximum tags count 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::
|
||||
|
|
@ -235,34 +259,34 @@ The default theme does not support tag clouds, but it is pretty easy to add::
|
|||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
You should then also define a CSS with the appropriate classes (tag-0 to tag-N, where
|
||||
N matches `TAG_CLOUD_STEPS` -1.
|
||||
You should then also define a CSS style with the appropriate classes (tag-0 to tag-N, where
|
||||
N matches `TAG_CLOUD_STEPS` -1).
|
||||
|
||||
Translations
|
||||
============
|
||||
|
||||
Pelican offers a way to translate articles. See the section on getting started for
|
||||
more information about that.
|
||||
Pelican offers a way to translate articles. See the Getting Started section for
|
||||
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.
|
||||
`TRANSLATION_FEED` ('feeds/all-%s.atom.xml'[3]_) Where to put the RSS feed for translations.
|
||||
`TRANSLATION_FEED` ('feeds/all-%s.atom.xml'[3]_) Where to put the feed for translations.
|
||||
================================================ =====================================================
|
||||
|
||||
.. [3] %s is the language
|
||||
|
||||
Ordering contents
|
||||
Ordering content
|
||||
=================
|
||||
|
||||
================================================ =====================================================
|
||||
Setting name (default value) what does it do?
|
||||
Setting name (default value) What does it do?
|
||||
================================================ =====================================================
|
||||
`REVERSE_ARCHIVE_ORDER` (``False``) Reverse the archives order. (True makes it in
|
||||
descending order: the newer first)
|
||||
`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True makes it in
|
||||
descending order, default is alphabetically)
|
||||
`REVERSE_ARCHIVE_ORDER` (``False``) Reverse the archives list order. (True: orders by date
|
||||
in descending order, with newer articles first.)
|
||||
`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True: lists by reverse
|
||||
alphabetical order; default lists alphabetically.)
|
||||
================================================ =====================================================
|
||||
|
||||
Theming
|
||||
|
|
@ -272,45 +296,45 @@ Theming is addressed in a dedicated section (see :ref:`theming-pelican`).
|
|||
However, here are the settings that are related to theming.
|
||||
|
||||
================================================ =====================================================
|
||||
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 the
|
||||
complete static path to a theme folder, or
|
||||
chosen between the list of default themes (see
|
||||
below)
|
||||
`THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default
|
||||
values is `static`, but if your theme has
|
||||
value is `static`, but if your theme has
|
||||
other static paths, you can put them here.
|
||||
`CSS_FILE` (``'main.css'``) specify the CSS file you want to load
|
||||
`CSS_FILE` (``'main.css'``) Specify the CSS file you want to load.
|
||||
================================================ =====================================================
|
||||
|
||||
By default, two themes are availablee. You can specify them using the `-t` option:
|
||||
By default, two themes are available. You can specify them using the `-t` option:
|
||||
|
||||
* notmyidea
|
||||
* simple (a synonym for "full text" :)
|
||||
|
||||
You can define your own theme too, and specify it's emplacement in the same
|
||||
way (be sure to specify the full absolute path to it).
|
||||
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 `a guide on how to create your theme
|
||||
<http://alexis.notmyidea.org/pelican/themes.html>`_
|
||||
<http://pelican.notmyidea.org/en/latest/themes.html>`_
|
||||
|
||||
You can find a list of themes at http://github.com/ametaireau/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
|
||||
to use them too in your themes.
|
||||
using them in your themes as well.
|
||||
|
||||
======================= =======================================================
|
||||
Setting name what does it do ?
|
||||
Setting name What does it do ?
|
||||
======================= =======================================================
|
||||
`DISQUS_SITENAME` Pelican can handle disqus comments, specify the
|
||||
sitename you've filled in on disqus
|
||||
`GITHUB_URL` Your github URL (if you have one), it will then
|
||||
use it to create a github ribbon.
|
||||
`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
|
||||
`MENUITEMS` A list of tuples (Title, Url) for additional menu
|
||||
`DISQUS_SITENAME` Pelican can handle Disqus comments. Specify the
|
||||
Disqus sitename identifier here.
|
||||
`GITHUB_URL` Your GitHub URL (if you have one). It will then
|
||||
use this information to create a GitHub ribbon.
|
||||
`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate Google Analytics.
|
||||
`MENUITEMS` A list of tuples (Title, URL) for additional menu
|
||||
items to appear at the beginning of the main menu.
|
||||
`PIWIK_URL` URL to your Piwik server - without 'http://' at the
|
||||
beginning.
|
||||
|
|
@ -318,17 +342,17 @@ Setting name what does it do ?
|
|||
you have to include this setting too. (optional)
|
||||
`PIWIK_SITE_ID` ID for the monitored website. You can find the ID
|
||||
in the Piwik admin interface > settings > websites.
|
||||
`LINKS` A list of tuples (Title, Url) for links to appear on
|
||||
`LINKS` A list of tuples (Title, URL) for links to appear on
|
||||
the header.
|
||||
`SOCIAL` A list of tuples (Title, Url) to appear in the
|
||||
`SOCIAL` A list of tuples (Title, URL) to appear in the
|
||||
"social" section.
|
||||
`TWITTER_USERNAME` Allows to add a button on the articles to tweet about
|
||||
them. Add you twitter username if you want this
|
||||
button to appear.
|
||||
`TWITTER_USERNAME` Allows for adding a button to articles to encourage
|
||||
others to tweet about them. Add your Twitter username
|
||||
if you want this button to appear.
|
||||
======================= =======================================================
|
||||
|
||||
In addition, you can use the "wide" version of the `notmyidea` theme, by
|
||||
adding that in your configuration::
|
||||
In addition, you can use the "wide" version of the `notmyidea` theme by
|
||||
adding the following to your configuration::
|
||||
|
||||
CSS_FILE = "wide.css"
|
||||
|
||||
|
|
|
|||
170
docs/themes.rst
170
docs/themes.rst
|
|
@ -1,10 +1,10 @@
|
|||
.. _theming-pelican:
|
||||
|
||||
How to create themes for pelican
|
||||
How to create themes for Pelican
|
||||
################################
|
||||
|
||||
Pelican uses the great `jinja2 <http://jinja.pocoo.org>`_ templating engine to
|
||||
generate it's HTML output. The jinja2 syntax is really simple. If you want to
|
||||
generate its HTML output. The jinja2 syntax is really simple. If you want to
|
||||
create your own theme, feel free to take inspiration from the "simple" theme,
|
||||
which is available `here
|
||||
<https://github.com/ametaireau/pelican/tree/master/pelican/themes/simple/templates>`_
|
||||
|
|
@ -29,179 +29,183 @@ To make your own theme, you must follow the following structure::
|
|||
├── tag.html // processed for each tag
|
||||
└── tags.html // must list all the tags. Can be a tag cloud.
|
||||
|
||||
* `static` contains all the static content. It will be copied on the output
|
||||
`theme/static` folder then. I've put the css and image folders, but they are
|
||||
* `static` contains all the static assets, which will be copied to the output
|
||||
`theme/static` folder. I've put the CSS and image folders here, but they are
|
||||
just examples. Put what you need here.
|
||||
|
||||
* `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
|
||||
you to organize yourself while doing the theme.
|
||||
I've just put the mandatory templates here; you can define your own if it helps
|
||||
you keep things organized while creating your theme.
|
||||
|
||||
Templates and variables
|
||||
=======================
|
||||
|
||||
It's using a simple syntax, that you can embbed into your html pages.
|
||||
This document describes which templates should exist on a theme, and which
|
||||
variables will be passed to each template, while generating it.
|
||||
The idea is to use a simple syntax that you can embed into your HTML pages.
|
||||
This document describes which templates should exist in a theme, and which
|
||||
variables will be passed to each template at generation time.
|
||||
|
||||
All templates will receive the variables defined in your settings file, if they
|
||||
are in caps. You can access them directly.
|
||||
are in all-caps. You can access them directly.
|
||||
|
||||
Common variables
|
||||
----------------
|
||||
|
||||
All of those settings will be given to all templates.
|
||||
All of these settings will be available to all templates.
|
||||
|
||||
============= ===================================================
|
||||
Variable Description
|
||||
============= ===================================================
|
||||
articles That's the list of articles, ordered desc. by date
|
||||
all the elements are `Article` objects, so you can
|
||||
access their properties (e.g. title, summary, author
|
||||
etc.).
|
||||
dates The same list of article, but ordered by date,
|
||||
ascending.
|
||||
tags A dict containing each tags (keys), and the list of
|
||||
relative articles.
|
||||
categories A dict containing each category (keys), and the
|
||||
list of relative articles.
|
||||
pages The list of pages.
|
||||
articles The list of articles, ordered descending by date
|
||||
All the elements are `Article` objects, so you can
|
||||
access their attributes (e.g. title, summary, author
|
||||
etc.)
|
||||
dates The same list of articles, but ordered by date,
|
||||
ascending
|
||||
tags A key-value dict containing the tags (the keys) and
|
||||
the list of respective articles (the values)
|
||||
categories A key-value dict containing the categories (keys)
|
||||
and the list of respective articles (values)
|
||||
pages The list of pages
|
||||
============= ===================================================
|
||||
|
||||
index.html
|
||||
----------
|
||||
|
||||
Home page of your blog, will finally remain at output/index.html.
|
||||
This is the home page of your blog, generated at output/index.html.
|
||||
|
||||
If pagination is active, next pages will remain at output/index`n`.html.
|
||||
If pagination is active, subsequent pages will reside in output/index`n`.html.
|
||||
|
||||
=================== ===================================================
|
||||
Variable Description
|
||||
=================== ===================================================
|
||||
articles_paginator A paginator object of article list.
|
||||
articles_page The current page of articles.
|
||||
dates_paginator A paginator object of article list, ordered by date,
|
||||
ascending.
|
||||
articles_paginator A paginator object for the list of articles
|
||||
articles_page The current page of articles
|
||||
dates_paginator A paginator object for the article list, ordered by
|
||||
date, ascending.
|
||||
dates_page The current page of articles, ordered by date,
|
||||
ascending.
|
||||
page_name 'index'. Useful for pagination links.
|
||||
page_name 'index' -- useful for pagination links
|
||||
=================== ===================================================
|
||||
|
||||
author.html
|
||||
-------------
|
||||
|
||||
This template will be processed for each of the existing authors, and will
|
||||
finally remain at output/author/`author_name`.html.
|
||||
This template will be processed for each of the existing authors, with
|
||||
output generated at output/author/`author_name`.html.
|
||||
|
||||
If pagination is active, next pages will remain at
|
||||
If pagination is active, subsequent pages will reside at
|
||||
output/author/`author_name``n`.html.
|
||||
|
||||
=================== ===================================================
|
||||
Variable Description
|
||||
=================== ===================================================
|
||||
author The name of the author being processed.
|
||||
articles Articles of this author.
|
||||
dates Articles of this author, but ordered by date,
|
||||
ascending.
|
||||
articles_paginator A paginator object of article list.
|
||||
articles_page The current page of articles.
|
||||
dates_paginator A paginator object of article list, ordered by date,
|
||||
ascending.
|
||||
author The name of the author being processed
|
||||
articles Articles by this author
|
||||
dates Articles by this author, but ordered by date,
|
||||
ascending
|
||||
articles_paginator A paginator object for the list of articles
|
||||
articles_page The current page of articles
|
||||
dates_paginator A paginator object for the article list, ordered by
|
||||
date, ascending.
|
||||
dates_page The current page of articles, ordered by date,
|
||||
ascending.
|
||||
page_name 'author/`author_name`'. Useful for pagination
|
||||
links.
|
||||
page_name 'author/`author_name`' -- useful for pagination
|
||||
links
|
||||
=================== ===================================================
|
||||
|
||||
category.html
|
||||
-------------
|
||||
|
||||
This template will be processed for each of the existing categories, and will
|
||||
finally remain at output/category/`category_name`.html.
|
||||
This template will be processed for each of the existing categories, with
|
||||
output generated at output/category/`category_name`.html.
|
||||
|
||||
If pagination is active, next pages will remain at
|
||||
If pagination is active, subsequent pages will reside at
|
||||
output/category/`category_name``n`.html.
|
||||
|
||||
=================== ===================================================
|
||||
Variable Description
|
||||
=================== ===================================================
|
||||
category The name of the category being processed.
|
||||
articles Articles of this category.
|
||||
dates Articles of this category, but ordered by date,
|
||||
ascending.
|
||||
articles_paginator A paginator object of article list.
|
||||
articles_page The current page of articles.
|
||||
dates_paginator A paginator object of article list, ordered by date,
|
||||
ascending.
|
||||
category The name of the category being processed
|
||||
articles Articles for this category
|
||||
dates Articles for this category, but ordered by date,
|
||||
ascending
|
||||
articles_paginator A paginator object for the list of articles
|
||||
articles_page The current page of articles
|
||||
dates_paginator A paginator object for the list of articles,
|
||||
ordered by date, ascending
|
||||
dates_page The current page of articles, ordered by date,
|
||||
ascending.
|
||||
page_name 'category/`category_name`'. Useful for pagination
|
||||
links.
|
||||
ascending
|
||||
page_name 'category/`category_name`' -- useful for pagination
|
||||
links
|
||||
=================== ===================================================
|
||||
|
||||
article.html
|
||||
-------------
|
||||
|
||||
This template will be processed for each article. .html files will be output
|
||||
in output/`article_name`.html. Here are the specific variables it gets.
|
||||
This template will be processed for each article, with .html files saved
|
||||
as output/`article_name`.html. Here are the specific variables it gets.
|
||||
|
||||
============= ===================================================
|
||||
Variable Description
|
||||
============= ===================================================
|
||||
article The article object to be displayed.
|
||||
category The name of the category of the current article.
|
||||
article The article object to be displayed
|
||||
category The name of the category for the current article
|
||||
============= ===================================================
|
||||
|
||||
page.html
|
||||
---------
|
||||
|
||||
For each page, this template will be processed. It will create .html files in
|
||||
output/`page_name`.html.
|
||||
This template will be processed for each page, with corresponding .html files
|
||||
saved as output/`page_name`.html.
|
||||
|
||||
============= ===================================================
|
||||
Variable Description
|
||||
============= ===================================================
|
||||
page The page object to be displayed. You can access to
|
||||
its title, slug and content.
|
||||
page The page object to be displayed. You can access its
|
||||
title, slug, and content.
|
||||
============= ===================================================
|
||||
|
||||
tag.html
|
||||
--------
|
||||
|
||||
For each tag, this template will be processed. It will create .html files in
|
||||
output/tag/`tag_name`.html.
|
||||
This template will be processed for each tag, with corresponding .html files
|
||||
saved as output/tag/`tag_name`.html.
|
||||
|
||||
If pagination is active, next pages will remain at
|
||||
If pagination is active, subsequent pages will reside at
|
||||
output/tag/`tag_name``n`.html.
|
||||
|
||||
=================== ===================================================
|
||||
Variable Description
|
||||
=================== ===================================================
|
||||
tag The name of the tag being processed.
|
||||
articles Articles related to this tag.
|
||||
tag The name of the tag being processed
|
||||
articles Articles related to this tag
|
||||
dates Articles related to this tag, but ordered by date,
|
||||
ascending.
|
||||
articles_paginator A paginator object of article list.
|
||||
articles_page The current page of articles.
|
||||
dates_paginator A paginator object of article list, ordered by date,
|
||||
ascending.
|
||||
ascending
|
||||
articles_paginator A paginator object for the list of articles
|
||||
articles_page The current page of articles
|
||||
dates_paginator A paginator object for the list of articles,
|
||||
ordered by date, ascending
|
||||
dates_page The current page of articles, ordered by date,
|
||||
ascending.
|
||||
page_name 'tag/`tag_name`'. Useful for pagination links.
|
||||
ascending
|
||||
page_name 'tag/`tag_name`' -- useful for pagination links
|
||||
=================== ===================================================
|
||||
|
||||
Inheritance
|
||||
===========
|
||||
|
||||
Since version 3, pelican supports inheritance from the ``simple`` theme, so you can reuse the templates of the ``simple`` theme in your own themes:
|
||||
Since version 3.0, Pelican supports inheritance from the ``simple`` theme, so
|
||||
you can re-use the ``simple`` theme templates in your own themes.
|
||||
|
||||
If one of the mandatory files in the ``templates/`` directory of your theme is missing, it will be replaced by the matching template from the ``simple`` theme, so if the HTML structure of a template of the ``simple`` theme is right for you, you don't have to rewrite it from scratch.
|
||||
If one of the mandatory files in the ``templates/`` directory of your theme is
|
||||
missing, it will be replaced by the matching template from the ``simple`` theme.
|
||||
So if the HTML structure of a template in the ``simple`` theme is right for you,
|
||||
you don't have to write a new template from scratch.
|
||||
|
||||
You can also extend templates of the ``simple`` themes in your own themes by using the ``{% extends %}`` directive as in the following example:
|
||||
You can also extend templates from the ``simple`` themes in your own themes by using the ``{% extends %}`` directive as in the following example:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends "!simple/index.html" %} <!-- extends the ``index.html`` template of the ``simple`` theme -->
|
||||
{% extends "!simple/index.html" %} <!-- extends the ``index.html`` template from the ``simple`` theme -->
|
||||
|
||||
{% extends "index.html" %} <!-- "regular" extending -->
|
||||
|
||||
|
|
@ -226,10 +230,10 @@ The first file is the ``templates/base.html`` template:
|
|||
{% endblock %}
|
||||
|
||||
|
||||
1. On the first line, we extend the ``base.html`` template of the ``simple`` theme, so we don't have to rewrite the entire file.
|
||||
2. On the third line, we open the ``head`` block which has already been defined in the ``simple`` theme
|
||||
1. On the first line, we extend the ``base.html`` template from the ``simple`` theme, so we don't have to rewrite the entire file.
|
||||
2. On the third line, we open the ``head`` block which has already been defined in the ``simple`` theme.
|
||||
3. On the fourth line, the function ``super()`` keeps the content previously inserted in the ``head`` block.
|
||||
4. On the fifth line, we append a stylesheet to the page
|
||||
4. On the fifth line, we append a stylesheet to the page.
|
||||
5. On the last line, we close the ``head`` block.
|
||||
|
||||
This file will be extended by all the other templates, so the stylesheet will be linked from all pages.
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
Tips
|
||||
####
|
||||
|
||||
Here are some tips about pelican, which you might find useful.
|
||||
Here are some tips about Pelican that you might find useful.
|
||||
|
||||
Publishing to github
|
||||
Publishing to GitHub
|
||||
====================
|
||||
|
||||
Github comes with an interesting "pages" feature: you can upload things there
|
||||
and it will be available directly from their servers. As pelican is a static
|
||||
GitHub comes with an interesting "pages" feature: you can upload things there
|
||||
and it will be available directly from their servers. As Pelican is a static
|
||||
file generator, we can take advantage of this.
|
||||
|
||||
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_ makes this
|
||||
eally easy. You would have to install it::
|
||||
really easy. You will have to install it::
|
||||
|
||||
$ pip install ghp-import
|
||||
|
||||
Then, considering a repository containing your articles, you would simply have
|
||||
to run pelican and upload the output to github::
|
||||
Then, given a repository containing your articles, you would simply have
|
||||
to run Pelican and upload the output to GitHub::
|
||||
|
||||
$ pelican -s pelican.conf.py .
|
||||
$ ghp-import output
|
||||
|
|
@ -24,8 +24,8 @@ to run pelican and upload the output to github::
|
|||
|
||||
And that's it.
|
||||
|
||||
If you want you can put that directly into a post commit hook, so each time you
|
||||
commit, your blog is up to date on github!
|
||||
If you want, you can put that directly into a post-commit hook, so each time you
|
||||
commit, your blog is up to date on GitHub!
|
||||
|
||||
Put the following into `.git/hooks/post-commit`::
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import argparse
|
||||
import os, sys
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
|
||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||
|
|
@ -26,13 +28,50 @@ class Pelican(object):
|
|||
if self.path.endswith('/'):
|
||||
self.path = self.path[:-1]
|
||||
|
||||
if settings.get('CLEAN_URLS', False):
|
||||
log.warning('Found deprecated `CLEAN_URLS` in settings. Modifing'
|
||||
' the following settings for the same behaviour.')
|
||||
|
||||
settings['ARTICLE_URL'] = '{slug}/'
|
||||
settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
|
||||
settings['PAGE_URL'] = 'pages/{slug}/'
|
||||
settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/'
|
||||
|
||||
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
|
||||
'PAGE_LANG_URL'):
|
||||
log.warning("%s = '%s'" % (setting, settings[setting]))
|
||||
|
||||
if settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
|
||||
log.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
|
||||
' settings. Modifing the following settings for'
|
||||
' the same behaviour.')
|
||||
|
||||
structure = settings['ARTICLE_PERMALINK_STRUCTURE']
|
||||
|
||||
# Convert %(variable) into {variable}.
|
||||
structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure)
|
||||
|
||||
# Convert %x into {date:%x} for strftime
|
||||
structure = re.sub('(%[A-z])', '{date:\g<1>}', structure)
|
||||
|
||||
# Strip a / prefix
|
||||
structure = re.sub('^/', '', structure)
|
||||
|
||||
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
|
||||
'PAGE_LANG_URL', 'ARTICLE_SAVE_AS',
|
||||
'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS',
|
||||
'PAGE_LANG_SAVE_AS'):
|
||||
settings[setting] = os.path.join(structure, settings[setting])
|
||||
log.warning("%s = '%s'" % (setting, settings[setting]))
|
||||
|
||||
# define the default settings
|
||||
self.settings = settings
|
||||
self.theme = theme or settings['THEME']
|
||||
output_path = output_path or settings['OUTPUT_PATH']
|
||||
self.output_path = os.path.realpath(output_path)
|
||||
self.markup = markup or settings['MARKUP']
|
||||
self.delete_outputdir = delete_outputdir or settings['DELETE_OUTPUT_DIRECTORY']
|
||||
self.delete_outputdir = delete_outputdir \
|
||||
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):
|
||||
|
|
@ -75,7 +114,6 @@ class Pelican(object):
|
|||
if hasattr(p, 'generate_output'):
|
||||
p.generate_output(writer)
|
||||
|
||||
|
||||
def get_generator_classes(self):
|
||||
generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
|
||||
if self.settings['PDF_GENERATOR']:
|
||||
|
|
@ -86,7 +124,6 @@ class Pelican(object):
|
|||
return Writer(self.output_path, settings=self.settings)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="""A tool to generate a
|
||||
static blog, with restructured text input files.""")
|
||||
|
|
@ -97,32 +134,38 @@ def main():
|
|||
help='Path where to find the theme templates. If not specified, it'
|
||||
'will use the default one included with pelican.')
|
||||
parser.add_argument('-o', '--output', dest='output',
|
||||
help='Where to output the generated files. If not specified, a directory'
|
||||
' will be created, named "output" in the current path.')
|
||||
help='Where to output the generated files. If not specified, a '
|
||||
'directory will be created, named "output" in the current path.')
|
||||
parser.add_argument('-m', '--markup', default=None, dest='markup',
|
||||
help='the list of markup language to use (rst or md). Please indicate '
|
||||
'them separated by commas')
|
||||
parser.add_argument('-s', '--settings', dest='settings', default='',
|
||||
help='the settings of the application. Default to False.')
|
||||
parser.add_argument('-d', '--delete-output-directory', dest='delete_outputdir',
|
||||
parser.add_argument('-d', '--delete-output-directory',
|
||||
dest='delete_outputdir',
|
||||
action='store_true', help='Delete the output directory.')
|
||||
parser.add_argument('-v', '--verbose', action='store_const', const=log.INFO, dest='verbosity',
|
||||
help='Show all messages')
|
||||
parser.add_argument('-q', '--quiet', action='store_const', const=log.CRITICAL, dest='verbosity',
|
||||
help='Show only critical errors')
|
||||
parser.add_argument('-D', '--debug', action='store_const', const=log.DEBUG, dest='verbosity',
|
||||
help='Show all message, including debug messages')
|
||||
parser.add_argument('-v', '--verbose', action='store_const',
|
||||
const=log.INFO, dest='verbosity',
|
||||
help='Show all messages')
|
||||
parser.add_argument('-q', '--quiet', action='store_const',
|
||||
const=log.CRITICAL, dest='verbosity',
|
||||
help='Show only critical errors')
|
||||
parser.add_argument('-D', '--debug', action='store_const',
|
||||
const=log.DEBUG, dest='verbosity',
|
||||
help='Show all message, including debug messages')
|
||||
parser.add_argument('--version', action='version', version=__version__,
|
||||
help='Print the pelican version and exit')
|
||||
parser.add_argument('-r', '--autoreload', dest='autoreload', action='store_true',
|
||||
help="Relaunch pelican each time a modification occurs on the content"
|
||||
"files")
|
||||
parser.add_argument('-r', '--autoreload', dest='autoreload',
|
||||
action='store_true',
|
||||
help="Relaunch pelican each time a modification occurs"
|
||||
" on the content files")
|
||||
args = parser.parse_args()
|
||||
|
||||
log.init(args.verbosity)
|
||||
# Split the markup languages only if some have been given. Otherwise, populate
|
||||
# the variable with None.
|
||||
markup = [a.strip().lower() for a in args.markup.split(',')] if args.markup else None
|
||||
# Split the markup languages only if some have been given. Otherwise,
|
||||
# populate the variable with None.
|
||||
markup = [a.strip().lower() for a in args.markup.split(',')]\
|
||||
if args.markup else None
|
||||
|
||||
settings = read_settings(args.settings)
|
||||
|
||||
|
|
@ -158,7 +201,3 @@ def main():
|
|||
raise
|
||||
else:
|
||||
sys.exit(getattr(e, 'exitcode', 1))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from datetime import datetime
|
||||
from os import getenv
|
||||
from sys import platform, stdin
|
||||
import locale
|
||||
|
||||
from pelican.log import *
|
||||
from pelican.log import warning, error
|
||||
from pelican.settings import _DEFAULT_CONFIG
|
||||
from pelican.utils import slugify, truncate_html_words
|
||||
|
||||
|
||||
class Page(object):
|
||||
"""Represents a page
|
||||
Given a content, and metadata, create an adequate object.
|
||||
|
|
@ -24,6 +25,7 @@ class Page(object):
|
|||
if not settings:
|
||||
settings = _DEFAULT_CONFIG
|
||||
|
||||
self.settings = settings
|
||||
self._content = content
|
||||
self.translations = []
|
||||
|
||||
|
|
@ -37,10 +39,11 @@ class Page(object):
|
|||
# default author to the one in settings if not defined
|
||||
if not hasattr(self, 'author'):
|
||||
if 'AUTHOR' in settings:
|
||||
self.author = settings['AUTHOR']
|
||||
self.author = Author(settings['AUTHOR'], settings)
|
||||
else:
|
||||
self.author = getenv('USER', 'John Doe')
|
||||
warning(u"Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author))
|
||||
self.author = Author(getenv('USER', 'John Doe'), settings)
|
||||
warning(u"Author of `{0}' unknow, assuming that his name is "
|
||||
"`{1}'".format(filename or self.title, self.author))
|
||||
|
||||
# manage languages
|
||||
self.in_default_lang = True
|
||||
|
|
@ -55,29 +58,6 @@ class Page(object):
|
|||
if not hasattr(self, 'slug') and hasattr(self, 'title'):
|
||||
self.slug = slugify(self.title)
|
||||
|
||||
# create save_as from the slug (+lang)
|
||||
if not hasattr(self, 'save_as') and hasattr(self, 'slug'):
|
||||
if self.in_default_lang:
|
||||
if settings.get('CLEAN_URLS', False):
|
||||
self.save_as = '%s/index.html' % self.slug
|
||||
else:
|
||||
self.save_as = '%s.html' % self.slug
|
||||
|
||||
clean_url = '%s/' % self.slug
|
||||
else:
|
||||
if settings.get('CLEAN_URLS', False):
|
||||
self.save_as = '%s-%s/index.html' % (self.slug, self.lang)
|
||||
else:
|
||||
self.save_as = '%s-%s.html' % (self.slug, self.lang)
|
||||
|
||||
clean_url = '%s-%s/' % (self.slug, self.lang)
|
||||
|
||||
# change the save_as regarding the settings
|
||||
if settings.get('CLEAN_URLS', False):
|
||||
self.url = clean_url
|
||||
elif hasattr(self, 'save_as'):
|
||||
self.url = self.save_as
|
||||
|
||||
if filename:
|
||||
self.filename = filename
|
||||
|
||||
|
|
@ -93,16 +73,19 @@ class Page(object):
|
|||
self.date_format = self.date_format[1]
|
||||
|
||||
if hasattr(self, 'date'):
|
||||
encoded_date = self.date.strftime(
|
||||
self.date_format.encode('ascii', 'xmlcharrefreplace'))
|
||||
|
||||
if platform == 'win32':
|
||||
self.locale_date = self.date.strftime(self.date_format.encode('ascii','xmlcharrefreplace')).decode(stdin.encoding)
|
||||
self.locale_date = encoded_date.decode(stdin.encoding)
|
||||
else:
|
||||
self.locale_date = self.date.strftime(self.date_format.encode('ascii','xmlcharrefreplace')).decode('utf')
|
||||
self.locale_date = encoded_date.decode('utf')
|
||||
|
||||
# manage status
|
||||
if not hasattr(self, 'status'):
|
||||
self.status = settings['DEFAULT_STATUS']
|
||||
if not settings['WITH_FUTURE_DATES']:
|
||||
if hasattr(self, 'date') and self.date > datetime.datetime.now():
|
||||
if hasattr(self, 'date') and self.date > datetime.now():
|
||||
self.status = 'draft'
|
||||
|
||||
# set summary
|
||||
|
|
@ -115,6 +98,28 @@ class Page(object):
|
|||
if not hasattr(self, prop):
|
||||
raise NameError(prop)
|
||||
|
||||
@property
|
||||
def url_format(self):
|
||||
return {
|
||||
'slug': getattr(self, 'slug', ''),
|
||||
'lang': getattr(self, 'lang', 'en'),
|
||||
'date': getattr(self, 'date', datetime.now()),
|
||||
'author': self.author,
|
||||
'category': getattr(self, 'category', 'misc'),
|
||||
}
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
if self.in_default_lang:
|
||||
return self.settings['PAGE_URL'].format(**self.url_format)
|
||||
return self.settings['PAGE_LANG_URL'].format(**self.url_format)
|
||||
|
||||
@property
|
||||
def save_as(self):
|
||||
if self.in_default_lang:
|
||||
return self.settings['PAGE_SAVE_AS'].format(**self.url_format)
|
||||
return self.settings['PAGE_LANG_SAVE_AS'].format(**self.url_format)
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
if hasattr(self, "_get_content"):
|
||||
|
|
@ -131,22 +136,90 @@ class Page(object):
|
|||
"""Dummy function"""
|
||||
pass
|
||||
|
||||
summary = property(_get_summary, _set_summary,
|
||||
"Summary of the article. Based on the content. Can't be set")
|
||||
summary = property(_get_summary, _set_summary, "Summary of the article."
|
||||
"Based on the content. Can't be set")
|
||||
|
||||
|
||||
class Article(Page):
|
||||
mandatory_properties = ('title', 'date', 'category')
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
if self.in_default_lang:
|
||||
return self.settings['ARTICLE_URL'].format(**self.url_format)
|
||||
return self.settings['ARTICLE_LANG_URL'].format(**self.url_format)
|
||||
|
||||
@property
|
||||
def save_as(self):
|
||||
if self.in_default_lang:
|
||||
return self.settings['ARTICLE_SAVE_AS'].format(**self.url_format)
|
||||
return self.settings['ARTICLE_LANG_SAVE_AS'].format(**self.url_format)
|
||||
|
||||
|
||||
class Quote(Page):
|
||||
base_properties = ('author', 'date')
|
||||
|
||||
|
||||
class URLWrapper(object):
|
||||
def __init__(self, name, settings):
|
||||
self.name = unicode(name)
|
||||
self.settings = settings
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == unicode(other)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return '%s.html' % self.name
|
||||
|
||||
|
||||
class Category(URLWrapper):
|
||||
@property
|
||||
def url(self):
|
||||
return self.settings['CATEGORY_URL'].format(name=self.name)
|
||||
|
||||
@property
|
||||
def save_as(self):
|
||||
return self.settings['CATEGORY_SAVE_AS'].format(name=self.name)
|
||||
|
||||
|
||||
class Tag(URLWrapper):
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super(Tag, self).__init__(unicode.strip(name), *args, **kwargs)
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.settings['TAG_URL'].format(name=self.name)
|
||||
|
||||
@property
|
||||
def save_as(self):
|
||||
return self.settings['TAG_SAVE_AS'].format(name=self.name)
|
||||
|
||||
|
||||
class Author(URLWrapper):
|
||||
@property
|
||||
def url(self):
|
||||
return self.settings['AUTHOR_URL'].format(name=self.name)
|
||||
|
||||
@property
|
||||
def save_as(self):
|
||||
return self.settings['AUTHOR_SAVE_AS'].format(name=self.name)
|
||||
|
||||
|
||||
def is_valid_content(content, f):
|
||||
try:
|
||||
content.check_properties()
|
||||
return True
|
||||
except NameError, e:
|
||||
error(u"Skipping %s: impossible to find informations about '%s'" % (f, e))
|
||||
error(u"Skipping %s: impossible to find informations about '%s'"\
|
||||
% (f, e))
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import os
|
|||
import datetime
|
||||
import math
|
||||
import random
|
||||
import urlparse
|
||||
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
|
|
@ -13,11 +12,10 @@ from operator import attrgetter, itemgetter
|
|||
from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
|
||||
from pelican.contents import Article, Page, is_valid_content
|
||||
from pelican.log import *
|
||||
from pelican.contents import Article, Page, Category, is_valid_content
|
||||
from pelican.log import warning, error, debug, info
|
||||
from pelican.readers import read_file
|
||||
from pelican.utils import copy, process_translations, open
|
||||
from pelican.utils import slugify
|
||||
|
||||
|
||||
class Generator(object):
|
||||
|
|
@ -33,17 +31,23 @@ class Generator(object):
|
|||
|
||||
# templates cache
|
||||
self._templates = {}
|
||||
self._templates_path = os.path.expanduser(os.path.join(self.theme, 'templates'))
|
||||
simple_loader = FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), "themes", "simple", "templates"))
|
||||
self._templates_path = os.path.expanduser(
|
||||
os.path.join(self.theme, 'templates'))
|
||||
|
||||
theme_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
simple_loader = FileSystemLoader(os.path.join(theme_path,
|
||||
"themes", "simple", "templates"))
|
||||
self._env = Environment(
|
||||
loader=ChoiceLoader([
|
||||
FileSystemLoader(self._templates_path),
|
||||
simple_loader, # implicit inheritance
|
||||
PrefixLoader({'!simple' : simple_loader}) # explicit inheritance
|
||||
simple_loader, # implicit inheritance
|
||||
PrefixLoader({'!simple': simple_loader}) # explicit one
|
||||
]),
|
||||
extensions=self.settings.get('JINJA_EXTENSIONS', []),
|
||||
)
|
||||
debug('self._env.list_templates(): {0}'.format(self._env.list_templates()))
|
||||
|
||||
debug('template list: {0}'.format(self._env.list_templates()))
|
||||
|
||||
# get custom Jinja filters from user settings
|
||||
custom_filters = self.settings.get('JINJA_FILTERS', {})
|
||||
|
|
@ -58,8 +62,8 @@ class Generator(object):
|
|||
try:
|
||||
self._templates[name] = self._env.get_template(name + '.html')
|
||||
except TemplateNotFound:
|
||||
raise Exception('[templates] unable to load %s.html from %s' % (
|
||||
name, self._templates_path))
|
||||
raise Exception('[templates] unable to load %s.html from %s' \
|
||||
% (name, self._templates_path))
|
||||
return self._templates[name]
|
||||
|
||||
def get_files(self, path, exclude=[], extensions=None):
|
||||
|
|
@ -75,7 +79,7 @@ class Generator(object):
|
|||
|
||||
try:
|
||||
iter = os.walk(path, followlinks=True)
|
||||
except TypeError: # python 2.5 does not support followlinks
|
||||
except TypeError: # python 2.5 does not support followlinks
|
||||
iter = os.walk(path)
|
||||
|
||||
for root, dirs, temp_files in iter:
|
||||
|
|
@ -102,7 +106,7 @@ class ArticlesGenerator(Generator):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""initialize properties"""
|
||||
self.articles = [] # only articles in default language
|
||||
self.articles = [] # only articles in default language
|
||||
self.translations = []
|
||||
self.dates = {}
|
||||
self.tags = defaultdict(list)
|
||||
|
|
@ -138,7 +142,8 @@ class ArticlesGenerator(Generator):
|
|||
|
||||
if 'TAG_FEED_RSS' in self.settings:
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['TAG_FEED_RSS'] % tag, feed_type='rss')
|
||||
self.settings['TAG_FEED_RSS'] % tag,
|
||||
feed_type='rss')
|
||||
|
||||
translations_feeds = defaultdict(list)
|
||||
for article in chain(self.articles, self.translations):
|
||||
|
|
@ -149,14 +154,11 @@ class ArticlesGenerator(Generator):
|
|||
writer.write_feed(items, self.context,
|
||||
self.settings['TRANSLATION_FEED'] % lang)
|
||||
|
||||
|
||||
def generate_pages(self, writer):
|
||||
"""Generate the pages on the disk"""
|
||||
|
||||
write = partial(
|
||||
writer.write_file,
|
||||
relative_urls = self.settings.get('RELATIVE_URLS')
|
||||
)
|
||||
write = partial(writer.write_file,
|
||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||
|
||||
# to minimize the number of relative path stuff modification
|
||||
# in writer, articles pass first
|
||||
|
|
@ -171,48 +173,48 @@ class ArticlesGenerator(Generator):
|
|||
paginated = {}
|
||||
if template in PAGINATED_TEMPLATES:
|
||||
paginated = {'articles': self.articles, 'dates': self.dates}
|
||||
write('%s.html' % template, self.get_template(template), self.context,
|
||||
blog=True, paginated=paginated, page_name=template)
|
||||
|
||||
write('%s.html' % template, self.get_template(template),
|
||||
self.context, blog=True, paginated=paginated,
|
||||
page_name=template)
|
||||
|
||||
# and subfolders after that
|
||||
tag_template = self.get_template('tag')
|
||||
for tag, articles in self.tags.items():
|
||||
articles.sort(key=attrgetter('date'), reverse=True)
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
write('tag/%s.html' % tag, tag_template, self.context, tag=tag,
|
||||
write(tag.save_as, tag_template, self.context, tag=tag,
|
||||
articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name='tag/%s' % tag)
|
||||
page_name=u'tag/%s' % tag)
|
||||
|
||||
category_template = self.get_template('category')
|
||||
for cat, articles in self.categories:
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
write('category/%s.html' % cat, category_template, self.context,
|
||||
write(cat.save_as, category_template, self.context,
|
||||
category=cat, articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name='category/%s' % cat)
|
||||
page_name=u'category/%s' % cat)
|
||||
|
||||
author_template = self.get_template('author')
|
||||
for aut, articles in self.authors:
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
write('author/%s.html' % aut, author_template, self.context,
|
||||
write(aut.save_as, author_template, self.context,
|
||||
author=aut, articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name='author/%s' % aut)
|
||||
page_name=u'author/%s' % aut)
|
||||
|
||||
for article in self.drafts:
|
||||
write('drafts/%s.html' % article.slug, article_template, self.context,
|
||||
article=article, category=article.category)
|
||||
|
||||
write('drafts/%s.html' % article.slug, article_template,
|
||||
self.context, article=article, category=article.category)
|
||||
|
||||
def generate_context(self):
|
||||
"""change the context"""
|
||||
|
||||
# return the list of files to use
|
||||
files = self.get_files(self.path, exclude=['pages',])
|
||||
files = self.get_files(self.path, exclude=['pages', ])
|
||||
all_articles = []
|
||||
for f in files:
|
||||
|
||||
try:
|
||||
content, metadata = read_file(f, settings=self.settings)
|
||||
except Exception, e:
|
||||
|
|
@ -225,35 +227,22 @@ class ArticlesGenerator(Generator):
|
|||
if os.path.dirname(f) == self.path:
|
||||
category = self.settings['DEFAULT_CATEGORY']
|
||||
else:
|
||||
category = os.path.basename(os.path.dirname(f)).decode('utf-8')
|
||||
category = os.path.basename(os.path.dirname(f))\
|
||||
.decode('utf-8')
|
||||
|
||||
if category != '':
|
||||
metadata['category'] = unicode(category)
|
||||
metadata['category'] = Category(category, self.settings)
|
||||
|
||||
if 'date' not in metadata.keys()\
|
||||
and self.settings['FALLBACK_ON_FS_DATE']:
|
||||
metadata['date'] = datetime.datetime.fromtimestamp(os.stat(f).st_ctime)
|
||||
metadata['date'] = datetime.datetime.fromtimestamp(
|
||||
os.stat(f).st_ctime)
|
||||
|
||||
article = Article(content, metadata, settings=self.settings,
|
||||
filename=f)
|
||||
if not is_valid_content(article, f):
|
||||
continue
|
||||
|
||||
add_to_url = u''
|
||||
if 'ARTICLE_PERMALINK_STRUCTURE' in self.settings:
|
||||
article_permalink_structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
|
||||
article_permalink_structure = article_permalink_structure.lstrip('/').replace('%(', "%%(")
|
||||
|
||||
# try to substitute any python datetime directive
|
||||
add_to_url = article.date.strftime(article_permalink_structure)
|
||||
# try to substitute any article metadata in rest file
|
||||
add_to_url = add_to_url % article.__dict__
|
||||
add_to_url = [slugify(i) for i in add_to_url.split('/')]
|
||||
add_to_url = os.path.join(*add_to_url)
|
||||
|
||||
article.url = urlparse.urljoin(add_to_url, article.url)
|
||||
article.save_as = urlparse.urljoin(add_to_url, article.save_as)
|
||||
|
||||
if article.status == "published":
|
||||
if hasattr(article, 'tags'):
|
||||
for tag in article.tags:
|
||||
|
|
@ -281,7 +270,7 @@ class ArticlesGenerator(Generator):
|
|||
for tag in getattr(article, 'tags', []):
|
||||
tag_cloud[tag] += 1
|
||||
|
||||
tag_cloud = sorted(tag_cloud.items(), key = itemgetter(1), reverse = True)
|
||||
tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True)
|
||||
tag_cloud = tag_cloud[:self.settings.get('TAG_CLOUD_MAX_ITEMS')]
|
||||
|
||||
tags = map(itemgetter(1), tag_cloud)
|
||||
|
|
@ -293,9 +282,8 @@ class ArticlesGenerator(Generator):
|
|||
self.tag_cloud = [
|
||||
(
|
||||
tag,
|
||||
int(
|
||||
math.floor(steps - (steps - 1) * math.log(count) / (math.log(max_count)or 1))
|
||||
)
|
||||
int(math.floor(steps - (steps - 1) * math.log(count)
|
||||
/ (math.log(max_count)or 1)))
|
||||
)
|
||||
for tag, count in tag_cloud
|
||||
]
|
||||
|
|
@ -306,14 +294,13 @@ class ArticlesGenerator(Generator):
|
|||
|
||||
# order the categories per name
|
||||
self.categories = list(self.categories.items())
|
||||
self.categories.sort(reverse=self.settings.get('REVERSE_CATEGORY_ORDER'))
|
||||
self.categories.sort(reverse=self.settings['REVERSE_CATEGORY_ORDER'])
|
||||
|
||||
self.authors = list(self.authors.items())
|
||||
self.authors.sort()
|
||||
|
||||
self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud', 'authors'))
|
||||
|
||||
|
||||
self._update_context(('articles', 'dates', 'tags', 'categories',
|
||||
'tag_cloud', 'authors'))
|
||||
|
||||
def generate_output(self, writer):
|
||||
self.generate_feeds(writer)
|
||||
|
|
@ -348,9 +335,9 @@ class PagesGenerator(Generator):
|
|||
|
||||
def generate_output(self, writer):
|
||||
for page in chain(self.translations, self.pages):
|
||||
writer.write_file('pages/%s' % page.save_as, self.get_template('page'),
|
||||
writer.write_file(page.save_as, self.get_template('page'),
|
||||
self.context, page=page,
|
||||
relative_urls = self.settings.get('RELATIVE_URLS'))
|
||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||
|
||||
|
||||
class StaticGenerator(Generator):
|
||||
|
|
@ -361,8 +348,8 @@ class StaticGenerator(Generator):
|
|||
final_path=None):
|
||||
"""Copy all the paths from source to destination"""
|
||||
for path in paths:
|
||||
copy(path, source, os.path.join(output_path, destination), final_path,
|
||||
overwrite=True)
|
||||
copy(path, source, os.path.join(output_path, destination),
|
||||
final_path, overwrite=True)
|
||||
|
||||
def generate_output(self, writer):
|
||||
self._copy_paths(self.settings['STATIC_PATHS'], self.path,
|
||||
|
|
@ -372,7 +359,8 @@ class StaticGenerator(Generator):
|
|||
|
||||
# copy all the files needed
|
||||
for source, destination in self.settings['FILES_TO_COPY']:
|
||||
copy(source, self.path, self.output_path, destination, overwrite=True)
|
||||
copy(source, self.path, self.output_path, destination,
|
||||
overwrite=True)
|
||||
|
||||
|
||||
class PdfGenerator(Generator):
|
||||
|
|
@ -381,7 +369,8 @@ class PdfGenerator(Generator):
|
|||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
from rst2pdf.createpdf import RstToPdf
|
||||
self.pdfcreator = RstToPdf(breakside=0, stylesheets=['twelvepoint'])
|
||||
self.pdfcreator = RstToPdf(breakside=0,
|
||||
stylesheets=['twelvepoint'])
|
||||
except ImportError:
|
||||
raise Exception("unable to find rst2pdf")
|
||||
super(PdfGenerator, self).__init__(*args, **kwargs)
|
||||
|
|
@ -389,9 +378,10 @@ class PdfGenerator(Generator):
|
|||
def _create_pdf(self, obj, output_path):
|
||||
if obj.filename.endswith(".rst"):
|
||||
filename = obj.slug + ".pdf"
|
||||
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
|
||||
self.pdfcreator.createPdf(text=open(obj.filename), output=output_pdf)
|
||||
with open(obj.filename) as f:
|
||||
self.pdfcreator.createPdf(text=f, output=output_pdf)
|
||||
info(u' [ok] writing %s' % output_pdf)
|
||||
|
||||
def generate_context(self):
|
||||
|
|
|
|||
|
|
@ -4,27 +4,36 @@ from logging import CRITICAL, ERROR, WARN, INFO, DEBUG
|
|||
from logging import critical, error, info, warning, warn, debug
|
||||
from logging import Formatter, getLogger, StreamHandler
|
||||
|
||||
global ANSI
|
||||
ANSI = {
|
||||
'gray' : lambda(text) : u'\033[1;30m' + unicode(text) + u'\033[1;m',
|
||||
'red' : lambda(text) : u'\033[1;31m' + unicode(text) + u'\033[1;m',
|
||||
'green' : lambda(text) : u'\033[1;32m' + unicode(text) + u'\033[1;m',
|
||||
'yellow' : lambda(text) : u'\033[1;33m' + unicode(text) + u'\033[1;m',
|
||||
'blue' : lambda(text) : u'\033[1;34m' + unicode(text) + u'\033[1;m',
|
||||
'magenta' : lambda(text) : u'\033[1;35m' + unicode(text) + u'\033[1;m',
|
||||
'cyan' : lambda(text) : u'\033[1;36m' + unicode(text) + u'\033[1;m',
|
||||
'white' : lambda(text) : u'\033[1;37m' + unicode(text) + u'\033[1;m',
|
||||
'bgred' : lambda(text) : u'\033[1;41m' + unicode(text) + u'\033[1;m',
|
||||
'bggreen' : lambda(text) : u'\033[1;42m' + unicode(text) + u'\033[1;m',
|
||||
'bgbrown' : lambda(text) : u'\033[1;43m' + unicode(text) + u'\033[1;m',
|
||||
'bgblue' : lambda(text) : u'\033[1;44m' + unicode(text) + u'\033[1;m',
|
||||
'bgmagenta' : lambda(text) : u'\033[1;45m' + unicode(text) + u'\033[1;m',
|
||||
'bgcyan' : lambda(text) : u'\033[1;46m' + unicode(text) + u'\033[1;m',
|
||||
'bggray' : lambda(text) : u'\033[1;47m' + unicode(text) + u'\033[1;m',
|
||||
'bgyellow' : lambda(text) : u'\033[1;43m' + unicode(text) + u'\033[1;m',
|
||||
'bggrey' : lambda(text) : u'\033[1;100m' + unicode(text) + u'\033[1;m'
|
||||
|
||||
RESET_TERM = u'\033[1;m'
|
||||
|
||||
|
||||
def term_color(code):
|
||||
return lambda text: code + unicode(text) + RESET_TERM
|
||||
|
||||
|
||||
COLOR_CODES = {
|
||||
'gray': u'\033[1;30m',
|
||||
'red': u'\033[1;31m',
|
||||
'green': u'\033[1;32m',
|
||||
'yellow': u'\033[1;33m',
|
||||
'blue': u'\033[1;34m',
|
||||
'magenta': u'\033[1;35m',
|
||||
'cyan': u'\033[1;36m',
|
||||
'white': u'\033[1;37m',
|
||||
'bgred': u'\033[1;41m',
|
||||
'bggreen': u'\033[1;42m',
|
||||
'bgbrown': u'\033[1;43m',
|
||||
'bgblue': u'\033[1;44m',
|
||||
'bgmagenta': u'\033[1;45m',
|
||||
'bgcyan': u'\033[1;46m',
|
||||
'bggray': u'\033[1;47m',
|
||||
'bgyellow': u'\033[1;43m',
|
||||
'bggrey': u'\033[1;100m',
|
||||
}
|
||||
|
||||
ANSI = dict((col, term_color(code)) for col, code in COLOR_CODES.items())
|
||||
|
||||
|
||||
class ANSIFormatter(Formatter):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# From django.core.paginator
|
||||
from math import ceil
|
||||
|
||||
|
||||
class Paginator(object):
|
||||
def __init__(self, object_list, per_page, orphans=0):
|
||||
self.object_list = object_list
|
||||
|
|
@ -39,6 +40,7 @@ class Paginator(object):
|
|||
return range(1, self.num_pages + 1)
|
||||
page_range = property(_get_page_range)
|
||||
|
||||
|
||||
class Page(object):
|
||||
def __init__(self, object_list, number, paginator):
|
||||
self.object_list = object_list
|
||||
|
|
@ -82,4 +84,3 @@ class Page(object):
|
|||
if self.number == self.paginator.num_pages:
|
||||
return self.paginator.count
|
||||
return self.number * self.paginator.per_page
|
||||
|
||||
|
|
|
|||
|
|
@ -6,34 +6,41 @@ try:
|
|||
from docutils.writers.html4css1 import HTMLTranslator
|
||||
|
||||
# import the directives to have pygments support
|
||||
from pelican import rstdirectives
|
||||
from pelican import rstdirectives # NOQA
|
||||
except ImportError:
|
||||
core = False
|
||||
try:
|
||||
from markdown import Markdown
|
||||
except ImportError:
|
||||
Markdown = False
|
||||
Markdown = False # NOQA
|
||||
import re
|
||||
|
||||
from pelican.contents import Category, Tag, Author
|
||||
from pelican.utils import get_date, open
|
||||
|
||||
|
||||
_METADATA_PROCESSORS = {
|
||||
'tags': lambda x: map(unicode.strip, unicode(x).split(',')),
|
||||
'date': lambda x: get_date(x),
|
||||
'status': unicode.strip,
|
||||
'tags': lambda x, y: [Tag(tag, y) for tag in unicode(x).split(',')],
|
||||
'date': lambda x, y: get_date(x),
|
||||
'status': lambda x, y: unicode.strip(x),
|
||||
'category': Category,
|
||||
'author': Author,
|
||||
}
|
||||
|
||||
def _process_metadata(name, value):
|
||||
if name.lower() in _METADATA_PROCESSORS:
|
||||
return _METADATA_PROCESSORS[name.lower()](value)
|
||||
return value
|
||||
|
||||
|
||||
class Reader(object):
|
||||
enabled = True
|
||||
extensions = None
|
||||
|
||||
def __init__(self, settings):
|
||||
self.settings = settings
|
||||
|
||||
def process_metadata(self, name, value):
|
||||
if name.lower() in _METADATA_PROCESSORS:
|
||||
return _METADATA_PROCESSORS[name.lower()](value, self.settings)
|
||||
return value
|
||||
|
||||
|
||||
class _FieldBodyTranslator(HTMLTranslator):
|
||||
|
||||
def astext(self):
|
||||
|
|
@ -51,33 +58,31 @@ def render_node_to_html(document, node):
|
|||
node.walkabout(visitor)
|
||||
return visitor.astext()
|
||||
|
||||
def get_metadata(document):
|
||||
"""Return the dict containing document metadata"""
|
||||
output = {}
|
||||
for docinfo in document.traverse(docutils.nodes.docinfo):
|
||||
for element in docinfo.children:
|
||||
if element.tagname == 'field': # custom fields (e.g. summary)
|
||||
name_elem, body_elem = element.children
|
||||
name = name_elem.astext()
|
||||
value = render_node_to_html(document, body_elem)
|
||||
else: # standard fields (e.g. address)
|
||||
name = element.tagname
|
||||
value = element.astext()
|
||||
|
||||
output[name] = _process_metadata(name, value)
|
||||
return output
|
||||
|
||||
|
||||
class RstReader(Reader):
|
||||
enabled = bool(docutils)
|
||||
extension = "rst"
|
||||
|
||||
def _parse_metadata(self, document):
|
||||
return get_metadata(document)
|
||||
"""Return the dict containing document metadata"""
|
||||
output = {}
|
||||
for docinfo in document.traverse(docutils.nodes.docinfo):
|
||||
for element in docinfo.children:
|
||||
if element.tagname == 'field': # custom fields (e.g. summary)
|
||||
name_elem, body_elem = element.children
|
||||
name = name_elem.astext()
|
||||
value = render_node_to_html(document, body_elem)
|
||||
else: # standard fields (e.g. address)
|
||||
name = element.tagname
|
||||
value = element.astext()
|
||||
|
||||
output[name] = self.process_metadata(name, value)
|
||||
return output
|
||||
|
||||
def _get_publisher(self, filename):
|
||||
extra_params = {'initial_header_level': '2'}
|
||||
pub = docutils.core.Publisher(destination_class=docutils.io.StringOutput)
|
||||
pub = docutils.core.Publisher(
|
||||
destination_class=docutils.io.StringOutput)
|
||||
pub.set_components('standalone', 'restructuredtext', 'html')
|
||||
pub.process_programmatic_settings(None, extra_params, None)
|
||||
pub.set_source(source_path=filename)
|
||||
|
|
@ -110,7 +115,7 @@ class MarkdownReader(Reader):
|
|||
metadata = {}
|
||||
for name, value in md.Meta.items():
|
||||
name = name.lower()
|
||||
metadata[name] = _process_metadata(name, value[0])
|
||||
metadata[name] = self.process_metadata(name, value[0])
|
||||
return content, metadata
|
||||
|
||||
|
||||
|
|
@ -120,27 +125,27 @@ class HtmlReader(Reader):
|
|||
|
||||
def read(self, filename):
|
||||
"""Parse content and metadata of (x)HTML files"""
|
||||
content = open(filename)
|
||||
metadata = {'title':'unnamed'}
|
||||
for i in self._re.findall(content):
|
||||
key = i.split(':')[0][5:].strip()
|
||||
value = i.split(':')[-1][:-3].strip()
|
||||
name = key.lower()
|
||||
metadata[name] = _process_metadata(name, value)
|
||||
|
||||
return content, metadata
|
||||
with open(filename) as content:
|
||||
metadata = {'title': 'unnamed'}
|
||||
for i in self._re.findall(content):
|
||||
key = i.split(':')[0][5:].strip()
|
||||
value = i.split(':')[-1][:-3].strip()
|
||||
name = key.lower()
|
||||
metadata[name] = self.process_metadata(name, value)
|
||||
|
||||
return content, metadata
|
||||
|
||||
|
||||
_EXTENSIONS = dict((cls.extension, cls) for cls in Reader.__subclasses__())
|
||||
|
||||
|
||||
def read_file(filename, fmt=None, settings=None):
|
||||
"""Return a reader object using the given format."""
|
||||
if not fmt:
|
||||
fmt = filename.split('.')[-1]
|
||||
if fmt not in _EXTENSIONS.keys():
|
||||
raise TypeError('Pelican does not know how to parse %s' % filename)
|
||||
reader = _EXTENSIONS[fmt]()
|
||||
reader = _EXTENSIONS[fmt](settings)
|
||||
settings_key = '%s_EXTENSIONS' % fmt.upper()
|
||||
if settings and settings_key in settings:
|
||||
reader.extensions = settings[settings_key]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from os.path import isabs
|
||||
import locale
|
||||
|
||||
from pelican import log
|
||||
|
|
@ -10,8 +11,8 @@ _DEFAULT_CONFIG = {'PATH': None,
|
|||
'THEME': DEFAULT_THEME,
|
||||
'OUTPUT_PATH': 'output/',
|
||||
'MARKUP': ('rst', 'md'),
|
||||
'STATIC_PATHS': ['images',],
|
||||
'THEME_STATIC_PATHS': ['static',],
|
||||
'STATIC_PATHS': ['images', ],
|
||||
'THEME_STATIC_PATHS': ['static', ],
|
||||
'FEED': 'feeds/all.atom.xml',
|
||||
'CATEGORY_FEED': 'feeds/%s.atom.xml',
|
||||
'TRANSLATION_FEED': 'feeds/all-%s.atom.xml',
|
||||
|
|
@ -26,7 +27,20 @@ _DEFAULT_CONFIG = {'PATH': None,
|
|||
'REVERSE_ARCHIVE_ORDER': False,
|
||||
'REVERSE_CATEGORY_ORDER': False,
|
||||
'DELETE_OUTPUT_DIRECTORY': False,
|
||||
'CLEAN_URLS': False, # use /blah/ instead /blah.html in urls
|
||||
'ARTICLE_URL': '{slug}.html',
|
||||
'ARTICLE_SAVE_AS': '{slug}.html',
|
||||
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
|
||||
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
|
||||
'PAGE_URL': 'pages/{slug}.html',
|
||||
'PAGE_SAVE_AS': 'pages/{slug}.html',
|
||||
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
|
||||
'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html',
|
||||
'CATEGORY_URL': 'category/{name}.html',
|
||||
'CATEGORY_SAVE_AS': 'category/{name}.html',
|
||||
'TAG_URL': 'tag/{name}.html',
|
||||
'TAG_SAVE_AS': 'tag/{name}.html',
|
||||
'AUTHOR_URL': u'author/{name}.html',
|
||||
'AUTHOR_SAVE_AS': u'author/{name}.html',
|
||||
'RELATIVE_URLS': True,
|
||||
'DEFAULT_LANG': 'en',
|
||||
'TAG_CLOUD_STEPS': 4,
|
||||
|
|
@ -37,7 +51,7 @@ _DEFAULT_CONFIG = {'PATH': None,
|
|||
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
|
||||
'DATE_FORMATS': {},
|
||||
'JINJA_EXTENSIONS': [],
|
||||
'LOCALE': '', # default to user locale
|
||||
'LOCALE': '', # default to user locale
|
||||
'DEFAULT_PAGINATION': False,
|
||||
'DEFAULT_ORPHANS': 0,
|
||||
'DEFAULT_METADATA': (),
|
||||
|
|
@ -46,6 +60,7 @@ _DEFAULT_CONFIG = {'PATH': None,
|
|||
'ARTICLE_PERMALINK_STRUCTURE': ''
|
||||
}
|
||||
|
||||
|
||||
def read_settings(filename):
|
||||
"""Load a Python file into a dictionary.
|
||||
"""
|
||||
|
|
@ -60,9 +75,10 @@ def read_settings(filename):
|
|||
# Make the paths relative to the settings file
|
||||
for path in ['PATH', 'OUTPUT_PATH']:
|
||||
if path in context:
|
||||
if context[path] is not None and not os.path.isabs(context[path]):
|
||||
# FIXME:
|
||||
context[path] = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(filename), context[path])))
|
||||
if context[path] is not None and not isabs(context[path]):
|
||||
context[path] = os.path.abspath(os.path.normpath(
|
||||
os.path.join(os.path.dirname(filename), context[path]))
|
||||
)
|
||||
|
||||
# if locales is not a list, make it one
|
||||
locales = context['LOCALE']
|
||||
|
|
@ -77,17 +93,17 @@ def read_settings(filename):
|
|||
for locale_ in locales:
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, locale_)
|
||||
break # break if it is successfull
|
||||
break # break if it is successfull
|
||||
except locale.Error:
|
||||
pass
|
||||
else:
|
||||
log.warn("LOCALE option doesn't contain a correct value")
|
||||
|
||||
if not 'TIMEZONE' in context:
|
||||
log.warn("No timezone information specified in the settings. Assuming your "\
|
||||
"timezone is UTC for feed generation. "\
|
||||
"Check http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "\
|
||||
"for more information")
|
||||
log.warn("No timezone information specified in the settings. Assuming"
|
||||
" your timezone is UTC for feed generation. Check "
|
||||
"http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "
|
||||
"for more information")
|
||||
|
||||
# set the locale
|
||||
return context
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
{% if article.author %}
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="{{ SITEURL }}/author/{{ article.author }}.html">{{ article.author }}</a>
|
||||
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||
</address>
|
||||
{% endif %}
|
||||
<p>In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
|
||||
<p>In <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
|
||||
{% include 'taglist.html' %}
|
||||
{% include 'translations.html' %}
|
||||
</footer><!-- /.post-info -->
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
{% endfor %}
|
||||
{% if DISPLAY_PAGES_ON_MENU %}
|
||||
{% for page in PAGES %}
|
||||
<li><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></li>
|
||||
<li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for cat, null in categories %}
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://alexis.notmyidea.org/pelican/">pelican</a>, which takes great advantages of <a href="http://python.org">python</a>.
|
||||
Proudly powered by <a href="http://pelican.notmyidea.org/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
|
||||
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{% block content %}
|
||||
<ul>
|
||||
{% for category, articles in categories %}
|
||||
<li>{{ category }}</li>
|
||||
<li><a href="{{ category.url }}">{{ category }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
<section id="content" class="body">
|
||||
<h2>Pages</h2>
|
||||
{% for page in PAGES %}
|
||||
<li><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></li>
|
||||
<li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
{% if article.tags %}<p>tags: {% for tag in article.tags %}<a href="{{ SITEURL }}/tag/{{ tag }}.html">{{ tag }}</a>{% endfor %}</p>{% endif %}
|
||||
{% if article.tags %}<p>tags: {% for tag in article.tags %}<a href="{{ SITEURL }}/{{ tag.url }}">{{ tag }}</a>{% endfor %}</p>{% endif %}
|
||||
{% if PDF_PROCESSOR %}<p><a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a></p>{% endif %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</abbr>
|
||||
{% if article.author %}
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="#">{{ article.author }}</a>
|
||||
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||
</address>
|
||||
{% endif %}
|
||||
</footer><!-- /.post-info -->
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
{% endfor %}
|
||||
{% if DISPLAY_PAGES_ON_MENU %}
|
||||
{% for p in PAGES %}
|
||||
<li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/pages/{{ p.url }}">{{ p.title }}</a></li>
|
||||
<li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for cat, null in categories %}
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
{% endblock %}
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://docs.notmyidea.org/alexis/pelican/">pelican</a>,
|
||||
and obviously <a href="http://python.org">python</a>!
|
||||
Proudly powered by <a href="http://pelican.notmyidea.org/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{% block content %}
|
||||
<ul>
|
||||
{% for category, articles in categories %}
|
||||
<li>{{ category }}</li>
|
||||
<li><a href="{{ SITEURL }}/{{ category.url }}">{{ category }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<header> <h2 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.locale_date }} </abbr>
|
||||
{% if article.author %}<address class="vcard author">By <a class="url fn" href="{{ SITEURL }}/author/{{ article.author }}.html">{{ article.author }}</a></address>{% endif %}
|
||||
{% if article.author %}<address class="vcard author">By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a></address>{% endif %}
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ def get_date(string):
|
|||
string = re.sub(' +', ' ', string)
|
||||
formats = ['%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M',
|
||||
'%Y-%m-%d', '%Y/%m/%d',
|
||||
'%d-%m-%Y', '%Y-%d-%m', # Weird ones
|
||||
'%d-%m-%Y', '%Y-%d-%m', # Weird ones
|
||||
'%d/%m/%Y', '%d.%m.%Y',
|
||||
'%d.%m.%Y %H:%M', '%Y-%m-%d %H:%M:%S']
|
||||
for date_format in formats:
|
||||
|
|
@ -48,6 +48,7 @@ def slugify(value):
|
|||
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
||||
return re.sub('[-\s]+', '-', value)
|
||||
|
||||
|
||||
def copy(path, source, destination, destination_path=None, overwrite=False):
|
||||
"""Copy path from origin to destination.
|
||||
|
||||
|
|
@ -57,8 +58,8 @@ def copy(path, source, destination, destination_path=None, overwrite=False):
|
|||
:param source: the source dir
|
||||
:param destination: the destination dir
|
||||
:param destination_path: the destination path (optional)
|
||||
:param overwrite: wether to overwrite the destination if already exists or not
|
||||
|
||||
:param overwrite: wether to overwrite the destination if already exists or
|
||||
not
|
||||
"""
|
||||
if not destination_path:
|
||||
destination_path = path
|
||||
|
|
@ -109,7 +110,8 @@ def truncate_html_words(s, num, end_text='...'):
|
|||
length = int(num)
|
||||
if length <= 0:
|
||||
return u''
|
||||
html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input')
|
||||
html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area',
|
||||
'hr', 'input')
|
||||
|
||||
# Set up regular expressions
|
||||
re_words = re.compile(r'&.*?;|<.*?>|(\w[\w-]*)', re.U)
|
||||
|
|
@ -147,8 +149,9 @@ def truncate_html_words(s, num, end_text='...'):
|
|||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
# SGML: An end tag closes, back to the matching start tag, all unclosed intervening start tags with omitted end tags
|
||||
open_tags = open_tags[i+1:]
|
||||
# SGML: An end tag closes, back to the matching start tag,
|
||||
# all unclosed intervening start tags with omitted end tags
|
||||
open_tags = open_tags[i + 1:]
|
||||
else:
|
||||
# Add it to the start of the open tags list
|
||||
open_tags.insert(0, tagname)
|
||||
|
|
@ -195,7 +198,7 @@ def process_translations(content_list):
|
|||
default_lang_items = items[:1]
|
||||
|
||||
if not slug:
|
||||
warning('empty slug for %r' %( default_lang_items[0].filename,))
|
||||
warning('empty slug for %r' % (default_lang_items[0].filename,))
|
||||
index.extend(default_lang_items)
|
||||
translations.extend(filter(
|
||||
lambda x: x not in default_lang_items,
|
||||
|
|
@ -233,7 +236,8 @@ def files_changed(path, extensions):
|
|||
|
||||
def set_date_tzinfo(d, tz_name=None):
|
||||
""" Date without tzinfo shoudbe utc.
|
||||
This function set the right tz to date that aren't utc and don't have tzinfo
|
||||
This function set the right tz to date that aren't utc and don't have
|
||||
tzinfo.
|
||||
"""
|
||||
if tz_name is not None:
|
||||
tz = pytz.timezone(tz_name)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import re
|
|||
|
||||
from feedgenerator import Atom1Feed, Rss201rev2Feed
|
||||
from pelican.paginator import Paginator
|
||||
from pelican.log import *
|
||||
from pelican.log import info
|
||||
from pelican.utils import get_relative_path, set_date_tzinfo
|
||||
|
||||
|
||||
|
|
@ -28,7 +28,6 @@ class Writer(object):
|
|||
description=context.get('SITESUBTITLE', ''))
|
||||
return feed
|
||||
|
||||
|
||||
def _add_item_to_the_feed(self, feed, item):
|
||||
|
||||
feed.add_item(
|
||||
|
|
@ -44,8 +43,8 @@ class Writer(object):
|
|||
def write_feed(self, elements, context, filename=None, feed_type='atom'):
|
||||
"""Generate a feed with the list of articles provided
|
||||
|
||||
Return the feed. If no output_path or filename is specified, just return
|
||||
the feed object.
|
||||
Return the feed. If no output_path or filename is specified, just
|
||||
return the feed object.
|
||||
|
||||
:param elements: the articles to put on the feed.
|
||||
:param context: the context to get the feed metadata.
|
||||
|
|
@ -56,7 +55,7 @@ class Writer(object):
|
|||
locale.setlocale(locale.LC_ALL, 'C')
|
||||
try:
|
||||
self.site_url = context.get('SITEURL', get_relative_path(filename))
|
||||
self.feed_url= '%s/%s' % (self.site_url, filename)
|
||||
self.feed_url = '%s/%s' % (self.site_url, filename)
|
||||
|
||||
feed = self._create_new_feed(feed_type, context)
|
||||
|
||||
|
|
@ -132,7 +131,7 @@ class Writer(object):
|
|||
self.settings.get('DEFAULT_PAGINATION'),
|
||||
self.settings.get('DEFAULT_ORPHANS'))
|
||||
else:
|
||||
paginators[key] = Paginator(object_list, len(object_list), 0)
|
||||
paginators[key] = Paginator(object_list, len(object_list))
|
||||
|
||||
# generated pages, and write
|
||||
for page_num in range(paginators.values()[0].num_pages):
|
||||
|
|
@ -140,9 +139,10 @@ class Writer(object):
|
|||
paginated_name = name
|
||||
for key in paginators.iterkeys():
|
||||
paginator = paginators[key]
|
||||
page = paginator.page(page_num+1)
|
||||
paginated_localcontext.update({'%s_paginator' % key: paginator,
|
||||
'%s_page' % key: page})
|
||||
page = paginator.page(page_num + 1)
|
||||
paginated_localcontext.update(
|
||||
{'%s_paginator' % key: paginator,
|
||||
'%s_page' % key: page})
|
||||
if page_num > 0:
|
||||
ext = '.' + paginated_name.rsplit('.')[-1]
|
||||
paginated_name = paginated_name.replace(ext,
|
||||
|
|
@ -160,8 +160,8 @@ class Writer(object):
|
|||
relative paths.
|
||||
|
||||
:param name: name of the file to output.
|
||||
:param context: dict that will be passed to the templates, which need to
|
||||
be updated.
|
||||
:param context: dict that will be passed to the templates, which need
|
||||
to be updated.
|
||||
"""
|
||||
def _update_content(name, input):
|
||||
"""Change all the relatives paths of the input content to relatives
|
||||
|
|
@ -184,9 +184,12 @@ class Writer(object):
|
|||
|
||||
def replacer(m):
|
||||
relative_path = m.group('path')
|
||||
dest_path = os.path.normpath( os.sep.join( (get_relative_path(name),
|
||||
"static", relative_path) ) )
|
||||
return m.group('markup') + m.group('quote') + dest_path + m.group('quote')
|
||||
dest_path = os.path.normpath(
|
||||
os.sep.join((get_relative_path(name), "static",
|
||||
relative_path)))
|
||||
|
||||
return m.group('markup') + m.group('quote') + dest_path \
|
||||
+ m.group('quote')
|
||||
|
||||
return hrefs.sub(replacer, content)
|
||||
|
||||
|
|
|
|||
28
setup.py
28
setup.py
|
|
@ -1,34 +1,36 @@
|
|||
#!/usr/bin/env python
|
||||
from setuptools import setup
|
||||
import sys
|
||||
import platform
|
||||
|
||||
VERSION = "3.0" # find a better way to do so.
|
||||
|
||||
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz']
|
||||
if sys.version_info < (2,7):
|
||||
|
||||
try:
|
||||
import argparse
|
||||
except ImportError:
|
||||
requires.append('argparse')
|
||||
|
||||
scripts = ['bin/pelican', 'tools/pelican-themes', 'tools/pelican-import', 'tools/pelican-quickstart']
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
scripts += [
|
||||
'bin/pelican.bat', 'tools/pelican-themes.bat',
|
||||
'tools/pelican-import.bat', 'tools/pelican-quickstart.bat'
|
||||
]
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
'pelican = pelican:main',
|
||||
'pelican-import = tools.pelican_import:main',
|
||||
'pelican-quickstart = tools.pelican_quickstart:main',
|
||||
'pelican-themes = tools.pelican_themes:main'
|
||||
]
|
||||
}
|
||||
|
||||
setup(
|
||||
name = "pelican",
|
||||
version = VERSION,
|
||||
url = 'http://alexis.notmyidea.org/pelican/',
|
||||
url = 'http://pelican.notmyidea.org/',
|
||||
author = 'Alexis Metaireau',
|
||||
author_email = 'alexis@notmyidea.org',
|
||||
description = "A tool to generate a static blog, with restructured text (or markdown) input files.",
|
||||
description = "A tool to generate a static blog from reStructuredText or Markdown input files.",
|
||||
long_description=open('README.rst').read(),
|
||||
packages = ['pelican'],
|
||||
include_package_data = True,
|
||||
install_requires = requires,
|
||||
scripts = scripts,
|
||||
entry_points = entry_points,
|
||||
classifiers = ['Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Console',
|
||||
'License :: OSI Approved :: GNU Affero General Public License v3',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import with_statement
|
||||
try:
|
||||
from unittest2 import TestCase
|
||||
from unittest2 import TestCase, skip
|
||||
except ImportError, e:
|
||||
from unittest import TestCase
|
||||
from unittest import TestCase, skip
|
||||
|
||||
from pelican.contents import Page
|
||||
from pelican.settings import _DEFAULT_CONFIG
|
||||
|
|
@ -60,12 +60,12 @@ class TestPage(TestCase):
|
|||
"""
|
||||
# if a title is defined, save_as should be set
|
||||
page = Page(**self.page_kwargs)
|
||||
page.save_as = 'foo-bar.html'
|
||||
self.assertEqual(page.save_as, "pages/foo-bar.html")
|
||||
|
||||
# if a language is defined, save_as should include it accordingly
|
||||
self.page_kwargs['metadata'].update({'lang': 'fr', })
|
||||
page = Page(**self.page_kwargs)
|
||||
self.assertEqual(page.save_as, "foo-bar-fr.html")
|
||||
self.assertEqual(page.save_as, "pages/foo-bar-fr.html")
|
||||
|
||||
def test_datetime(self):
|
||||
"""If DATETIME is set to a tuple, it should be used to override LOCALE
|
||||
|
|
@ -82,7 +82,8 @@ class TestPage(TestCase):
|
|||
page_kwargs['metadata']['date'] = dt
|
||||
page = Page( **page_kwargs)
|
||||
|
||||
self.assertEqual(page.locale_date, dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']))
|
||||
self.assertEqual(page.locale_date,
|
||||
unicode(dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']), 'utf-8'))
|
||||
|
||||
|
||||
page_kwargs['settings'] = {x:_DEFAULT_CONFIG[x] for x in _DEFAULT_CONFIG}
|
||||
|
|
@ -93,6 +94,18 @@ class TestPage(TestCase):
|
|||
locale = 'ja_JP.utf8'
|
||||
page_kwargs['settings']['DATE_FORMATS'] = {'jp':(locale,'%Y-%m-%d(%a)')}
|
||||
page_kwargs['metadata']['lang'] = 'jp'
|
||||
page = Page( **page_kwargs)
|
||||
self.assertEqual(page.locale_date, u'2015-09-13(\u65e5)')
|
||||
# above is unicode in Japanese: 2015-09-13(“ú)
|
||||
|
||||
import locale as locale_module
|
||||
try:
|
||||
page = Page( **page_kwargs)
|
||||
self.assertEqual(page.locale_date, u'2015-09-13(\u65e5)')
|
||||
# above is unicode in Japanese: 2015-09-13(“ú)
|
||||
except locale_module.Error:
|
||||
# The constructor of ``Page`` will try to set the locale to
|
||||
# ``ja_JP.utf8``. But this attempt will failed when there is no
|
||||
# such locale in the system. You can see which locales there are
|
||||
# in your system with ``locale -a`` command.
|
||||
#
|
||||
# Until we find some other method to test this functionality, we
|
||||
# will simply skip this test.
|
||||
skip("There is no locale %s in this system." % locale)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ def _filename(*args):
|
|||
class RstReaderTest(unittest2.TestCase):
|
||||
|
||||
def test_article_with_metadata(self):
|
||||
reader = readers.RstReader()
|
||||
reader = readers.RstReader({})
|
||||
content, metadata = reader.read(_filename('article_with_metadata.rst'))
|
||||
expected = {
|
||||
'category': 'yeah',
|
||||
|
|
|
|||
0
tools/__init__.py
Normal file
0
tools/__init__.py
Normal file
|
|
@ -1 +0,0 @@
|
|||
@pypy "%~dpn0" %*
|
||||
|
|
@ -1 +0,0 @@
|
|||
@pypy "%~dpn0" %*
|
||||
|
|
@ -1 +0,0 @@
|
|||
@pypy "%~dpn0" %*
|
||||
|
|
@ -71,7 +71,7 @@ def dc2fields(file):
|
|||
else:
|
||||
posts.append(line)
|
||||
|
||||
print "%i posts read." % len(posts)
|
||||
print("%i posts read." % len(posts))
|
||||
|
||||
for post in posts:
|
||||
fields = post.split('","')
|
||||
|
|
@ -205,7 +205,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False):
|
|||
else:
|
||||
out_filename = os.path.join(output_path, filename+ext)
|
||||
|
||||
print out_filename
|
||||
print(out_filename)
|
||||
|
||||
if in_markup == "html":
|
||||
html_filename = os.path.join(output_path, filename+'.html')
|
||||
|
|
@ -231,18 +231,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False):
|
|||
fs.write(header + content)
|
||||
|
||||
|
||||
def main(input_type, input, out_markup, output_path, dircat=False):
|
||||
if input_type == 'wordpress':
|
||||
fields = wp2fields(input)
|
||||
elif input_type == 'dotclear':
|
||||
fields = dc2fields(input)
|
||||
elif input_type == 'feed':
|
||||
fields = feed2fields(input)
|
||||
|
||||
fields2pelican(fields, out_markup, output_path, dircat=dircat)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Transform feed, Wordpress or Dotclear files to rst files."
|
||||
"Be sure to have pandoc installed")
|
||||
|
|
@ -270,7 +259,7 @@ if __name__ == '__main__':
|
|||
elif args.feed:
|
||||
input_type = 'feed'
|
||||
else:
|
||||
print "you must provide either --wpfile, --dotclear or --feed options"
|
||||
print("you must provide either --wpfile, --dotclear or --feed options")
|
||||
exit()
|
||||
|
||||
if not os.path.exists(args.output):
|
||||
|
|
@ -280,4 +269,14 @@ if __name__ == '__main__':
|
|||
error("Couldn't create the output folder: " + args.output)
|
||||
exit()
|
||||
|
||||
main(input_type, args.input, args.markup, args.output, dircat=args.dircat)
|
||||
# TODO: refactor this long assignment
|
||||
input_type, input, out_markup, output_path, dircat=False = input_type, args.input, args.markup, args.output, args.dircat
|
||||
|
||||
if input_type == 'wordpress':
|
||||
fields = wp2fields(input)
|
||||
elif input_type == 'dotclear':
|
||||
fields = dc2fields(input)
|
||||
elif input_type == 'feed':
|
||||
fields = feed2fields(input)
|
||||
|
||||
fields2pelican(fields, out_markup, output_path, dircat=dircat)
|
||||
|
|
@ -40,7 +40,7 @@ html: clean $$(OUTPUTDIR)/index.html
|
|||
\t@echo 'Done'
|
||||
|
||||
$$(OUTPUTDIR)/%.html:
|
||||
\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE)
|
||||
\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||
|
||||
clean:
|
||||
\trm -fr $$(OUTPUTDIR)
|
||||
|
|
@ -94,7 +94,7 @@ DEFAULT_PAGINATION = $default_pagination
|
|||
|
||||
CONF = {
|
||||
'pelican' : 'pelican',
|
||||
'pelicanopts' : None,
|
||||
'pelicanopts' : '',
|
||||
'basedir': '.',
|
||||
'ftp_host': 'localhost',
|
||||
'ftp_user': 'anonymous',
|
||||
|
|
@ -103,7 +103,7 @@ CONF = {
|
|||
'ssh_user': 'root',
|
||||
'ssh_target_dir': '/var/www',
|
||||
'dropbox_dir' : '~/Dropbox/Public/',
|
||||
'default_pagination' : 7,
|
||||
'default_pagination' : 10,
|
||||
'lang': 'en'
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +270,3 @@ Please answer the following questions so this script can generate the files need
|
|||
print('Error: {0}'.format(e))
|
||||
|
||||
print('Done. Your new project is available at %s' % CONF['basedir'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -212,6 +212,3 @@ def clean(v=False):
|
|||
c+=1
|
||||
|
||||
print("\nRemoved {0} broken links".format(c))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue