mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge branch 'master' into htmlparser
This commit is contained in:
commit
c87cf2d2cf
27 changed files with 485 additions and 243 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
Jinja2
|
Jinja2>=2.4
|
||||||
Pygments
|
Pygments
|
||||||
docutils
|
docutils
|
||||||
feedgenerator
|
feedgenerator
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ Is it mandatory to have a configuration file?
|
||||||
|
|
||||||
No, it's not. Configuration files are just an easy way to configure Pelican.
|
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
|
For basic operations, it's possible to specify options while invoking Pelican
|
||||||
via the command line. See `pelican --help` for more information.
|
via the command line. See ``pelican --help`` for more information.
|
||||||
|
|
||||||
I'm creating my own theme. How do I use Pygments for syntax highlighting?
|
I'm creating my own theme. How do I use Pygments for syntax highlighting?
|
||||||
=========================================================================
|
=========================================================================
|
||||||
|
|
||||||
Pygments adds some classes to the generated content. These classes are used by
|
Pygments adds some classes to the generated content. These classes are used by
|
||||||
themes to style code syntax highlighting via CSS. Specifically, you can
|
themes to style code syntax highlighting via CSS. Specifically, you can
|
||||||
customize the appearance of your syntax highlighting via the `.codehilite pre`
|
customize the appearance of your syntax highlighting via the ``.codehilite pre``
|
||||||
class in your theme's CSS file. To see how various styles can be used to render
|
class in your theme's CSS file. To see how various styles can be used to render
|
||||||
Django code, for example, you can use the demo `on the project website
|
Django code, for example, you can use the demo `on the project website
|
||||||
<http://pygments.org/demo/15101/>`_.
|
<http://pygments.org/demo/15101/>`_.
|
||||||
|
|
@ -30,7 +30,7 @@ How can I help?
|
||||||
|
|
||||||
There are several ways to help out. First, you can use Pelican and report any
|
There are several ways to help out. First, you can use Pelican and report any
|
||||||
suggestions or problems you might have on `the bugtracker
|
suggestions or problems you might have on `the bugtracker
|
||||||
<http://github.com/ametaireau/pelican/issues>`_.
|
<https://github.com/ametaireau/pelican/issues>`_.
|
||||||
|
|
||||||
If you want to contribute, please fork `the git repository
|
If you want to contribute, please fork `the git repository
|
||||||
<https://github.com/ametaireau/pelican/>`_, make your changes, and issue
|
<https://github.com/ametaireau/pelican/>`_, make your changes, and issue
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
Getting started
|
Getting started
|
||||||
###############
|
###############
|
||||||
|
|
||||||
Installing
|
Installing Pelican
|
||||||
==========
|
==================
|
||||||
|
|
||||||
You're ready? Let's go! You can install Pelican via several different methods.
|
You're ready? Let's go! You can install Pelican via several different methods.
|
||||||
The simplest is via `pip <http://www.pip-installer.org/>`_::
|
The simplest is via `pip <http://www.pip-installer.org/>`_::
|
||||||
|
|
||||||
$ pip install pelican
|
$ pip install pelican
|
||||||
|
|
||||||
If you don't have pip installed, an alternative method is easy_install::
|
If you don't have ``pip`` installed, an alternative method is ``easy_install``::
|
||||||
|
|
||||||
$ easy_install pelican
|
$ easy_install pelican
|
||||||
|
|
||||||
|
|
@ -18,12 +18,13 @@ a virtual environment for Pelican via `virtualenv <http://www.virtualenv.org/>`_
|
||||||
and `virtualenvwrapper <http://www.doughellmann.com/projects/virtualenvwrapper/>`_
|
and `virtualenvwrapper <http://www.doughellmann.com/projects/virtualenvwrapper/>`_
|
||||||
before installing Pelican::
|
before installing Pelican::
|
||||||
|
|
||||||
$ pip install virtualenvwrapper
|
$ sudo pip install --upgrade virtualenv virtualenvwrapper
|
||||||
$ mkvirtualenv pelican
|
$ mkvirtualenv pelican
|
||||||
|
$ pip install pelican
|
||||||
|
|
||||||
Once the virtual environment has been created and activated, Pelican can be
|
Once the virtual environment has been created and activated, Pelican can be
|
||||||
be installed via pip or easy_install as noted above. Alternatively, if you
|
be installed via ``pip`` or ``easy_install`` as noted above. Alternatively, if
|
||||||
have the project source, you can install Pelican using the distutils
|
you have the project source, you can install Pelican using the distutils
|
||||||
method::
|
method::
|
||||||
|
|
||||||
$ cd path-to-Pelican-source
|
$ cd path-to-Pelican-source
|
||||||
|
|
@ -34,11 +35,16 @@ version of Pelican rather than a stable release, use the following command::
|
||||||
|
|
||||||
$ pip install -e git://github.com/ametaireau/pelican#egg=pelican
|
$ pip install -e git://github.com/ametaireau/pelican#egg=pelican
|
||||||
|
|
||||||
|
If you plan on using Markdown as a markup format, you'll need to install the
|
||||||
|
Markdown library as well::
|
||||||
|
|
||||||
|
$ pip install Markdown
|
||||||
|
|
||||||
Upgrading
|
Upgrading
|
||||||
---------
|
---------
|
||||||
|
|
||||||
If you installed a stable Pelican release via pip or easy_install and wish to
|
If you installed a stable Pelican release via pip or easy_install and wish to
|
||||||
upgrade to the latest stable release, you can do so by adding `--upgrade` to
|
upgrade to the latest stable release, you can do so by adding ``--upgrade`` to
|
||||||
the relevant command. For pip, that would be::
|
the relevant command. For pip, that would be::
|
||||||
|
|
||||||
$ pip install --upgrade pelican
|
$ pip install --upgrade pelican
|
||||||
|
|
@ -55,13 +61,66 @@ At this time, Pelican is dependent on the following Python packages:
|
||||||
* jinja2, for templating support
|
* jinja2, for templating support
|
||||||
* docutils, for supporting reStructuredText as an input format
|
* docutils, for supporting reStructuredText as an input format
|
||||||
|
|
||||||
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 the ``argparse`` package.
|
||||||
|
|
||||||
Optionally:
|
Optionally:
|
||||||
|
|
||||||
* pygments, for syntax highlighting
|
* pygments, for syntax highlighting
|
||||||
* Markdown, for supporting Markdown as an input format
|
* Markdown, for supporting Markdown as an input format
|
||||||
|
|
||||||
|
Kickstart a blog
|
||||||
|
================
|
||||||
|
|
||||||
|
Following is a brief tutorial for those who want to get started right away.
|
||||||
|
We're going to assume Pelican was installed in a virtual environment via the
|
||||||
|
following steps (if you're not using a virtual environment for Pelican, you can
|
||||||
|
skip to the ``pelican-quickstart`` command)::
|
||||||
|
|
||||||
|
$ sudo pip install --upgrade virtualenv virtualenvwrapper
|
||||||
|
$ mkvirtualenv pelican
|
||||||
|
$ pip install pelican Markdown
|
||||||
|
|
||||||
|
Next we'll create a directory to house our site content and configuration files,
|
||||||
|
which can be located any place you prefer, and associate this new project with
|
||||||
|
the currently-active virtual environment::
|
||||||
|
|
||||||
|
$ mkdir ~/code/yoursitename
|
||||||
|
$ cd ~/code/yoursitename
|
||||||
|
$ setvirtualenvproject
|
||||||
|
|
||||||
|
Now we can run the ``pelican-quickstart`` command, which will ask some questions
|
||||||
|
about your site::
|
||||||
|
|
||||||
|
$ pelican-quickstart
|
||||||
|
|
||||||
|
Once you finish answering all the questions, you can begin adding content to the
|
||||||
|
*content* folder that has been created for you. (See *Writing articles using
|
||||||
|
Pelican* section below for more information about how to format your content.)
|
||||||
|
Once you have some content to generate, you can convert it to HTML via the
|
||||||
|
following command::
|
||||||
|
|
||||||
|
$ make html
|
||||||
|
|
||||||
|
If you'd prefer to have Pelican automatically regenerate your site every time a
|
||||||
|
change is detected (handy when testing locally), use the following command
|
||||||
|
instead::
|
||||||
|
|
||||||
|
$ make regenerate
|
||||||
|
|
||||||
|
To serve the site so it can be previewed in your browser::
|
||||||
|
|
||||||
|
$ make serve
|
||||||
|
|
||||||
|
Visit http://localhost:8000 in your browser to see your site.
|
||||||
|
|
||||||
|
When you're ready to publish your site, you can upload it via the method(s) you
|
||||||
|
chose during the ``pelican-quickstart`` questionnaire. For this example, we'll
|
||||||
|
use rsync over ssh::
|
||||||
|
|
||||||
|
$ make rsync_upload
|
||||||
|
|
||||||
|
That's it! Your site should now be live.
|
||||||
|
|
||||||
Writing articles using Pelican
|
Writing articles using Pelican
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
|
@ -73,7 +132,7 @@ 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.
|
information you need to provide in the form of metadata inside your files.
|
||||||
|
|
||||||
You can provide this metadata in reStructuredText text files via the
|
You can provide this metadata in reStructuredText text files via the
|
||||||
following syntax (give your file the `.rst` extension)::
|
following syntax (give your file the ``.rst`` extension)::
|
||||||
|
|
||||||
My super title
|
My super title
|
||||||
##############
|
##############
|
||||||
|
|
@ -83,10 +142,9 @@ following syntax (give your file the `.rst` extension)::
|
||||||
:category: yeah
|
:category: yeah
|
||||||
:author: Alexis Metaireau
|
:author: Alexis Metaireau
|
||||||
|
|
||||||
|
You can also use Markdown syntax (with a file ending in ``.md``).
|
||||||
You can also use Markdown syntax (with a file ending in `.md`).
|
Markdown generation will not work until you explicitly install the ``Markdown``
|
||||||
Markdown generation will not work until you explicitly install the `Markdown`
|
package, which can be done via ``pip install Markdown``. Metadata syntax for
|
||||||
package, which can be done via `pip install Markdown`. Metadata syntax for
|
|
||||||
Markdown posts should follow this pattern::
|
Markdown posts should follow this pattern::
|
||||||
|
|
||||||
Date: 2010-12-03
|
Date: 2010-12-03
|
||||||
|
|
@ -99,43 +157,48 @@ Markdown posts should follow this pattern::
|
||||||
Note that, aside from the title, none of this metadata is mandatory: if the date
|
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
|
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
|
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
|
example, a file located at ``python/foobar/myfoobar.rst`` will have a category of
|
||||||
`foobar`.
|
``foobar``.
|
||||||
|
|
||||||
Generate your blog
|
Generate your blog
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
To launch Pelican, just use the `pelican` command::
|
The ``make`` shortcut commands mentioned in the ``Kickstart a blog`` section
|
||||||
|
are mostly wrappers around the ``pelican`` command that generates the HTML from
|
||||||
|
the content. The ``pelican`` command can also be run directly::
|
||||||
|
|
||||||
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
|
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
|
||||||
|
|
||||||
And… that's all! Your weblog will be generated and saved in the `content/`
|
The above command will generate your weblog and save it in the ``content/``
|
||||||
folder.
|
folder, using the default theme to produce a simple site. The default theme is
|
||||||
|
simple HTML without styling and is provided so folks may use it as a basis for
|
||||||
|
creating their own themes.
|
||||||
|
|
||||||
The above command will use the default theme to produce a simple site. It's not
|
Pelican has other command-line switches available. Have a look at the help to
|
||||||
very sexy, as it's just simple HTML output (without any style).
|
see all the options you can use::
|
||||||
|
|
||||||
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
|
$ pelican --help
|
||||||
|
|
||||||
Kickstart a blog
|
Auto-reload
|
||||||
----------------
|
-----------
|
||||||
|
|
||||||
You also can use the `pelican-quickstart` script to start a new blog in
|
It's possible to tell Pelican to watch for your modifications, instead of
|
||||||
seconds by just answering a few questions. Just run `pelican-quickstart` and
|
manually re-running it every time you want to see your changes. To enable this,
|
||||||
you're done! (Added in Pelican 3.0)
|
run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
|
||||||
|
|
||||||
Pages
|
Pages
|
||||||
-----
|
-----
|
||||||
|
|
||||||
If you create a folder named `pages`, all the files in it will be used to
|
If you create a folder named ``pages``, all the files in it will be used to
|
||||||
generate static pages.
|
generate static pages.
|
||||||
|
|
||||||
Then, use the `DISPLAY_PAGES_ON_MENU` setting, which will add all the pages to
|
Then, use the ``DISPLAY_PAGES_ON_MENU`` setting, which will add all the pages to
|
||||||
the menu.
|
the menu.
|
||||||
|
|
||||||
|
If you want to exclude any pages from being linked to or listed in the menu
|
||||||
|
then add a ``status: hidden`` attribute to its metadata. This is useful for
|
||||||
|
things like making error pages that fit the generated theme of your site.
|
||||||
|
|
||||||
Importing an existing blog
|
Importing an existing blog
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|
@ -145,8 +208,8 @@ a simple script. See :ref:`import`.
|
||||||
Translations
|
Translations
|
||||||
------------
|
------------
|
||||||
|
|
||||||
It is possible to translate articles. To do so, you need to add a `lang` meta
|
It is possible to translate articles. To do so, you need to add a ``lang`` meta
|
||||||
attribute to your articles/pages and set a `DEFAULT_LANG` setting (which is
|
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
|
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
|
default language will be listed, and each article will be accompanied by a list
|
||||||
of available translations for that article.
|
of available translations for that article.
|
||||||
|
|
@ -205,13 +268,6 @@ For Markdown, format your code blocks thusly::
|
||||||
The specified identifier should be one that appears on the
|
The specified identifier should be one that appears on the
|
||||||
`list of available lexers <http://pygments.org/docs/lexers/>`_.
|
`list of available lexers <http://pygments.org/docs/lexers/>`_.
|
||||||
|
|
||||||
Auto-reload
|
|
||||||
-----------
|
|
||||||
|
|
||||||
It's possible to tell Pelican to watch for your modifications, instead of
|
|
||||||
manually re-running it every time you want to see your changes. To enable this,
|
|
||||||
run the `pelican` command with the `-r` or `--autoreload` option.
|
|
||||||
|
|
||||||
Publishing drafts
|
Publishing drafts
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
@ -234,5 +290,3 @@ Or run a simple web server using Python::
|
||||||
|
|
||||||
cd output && python -m SimpleHTTPServer
|
cd output && python -m SimpleHTTPServer
|
||||||
|
|
||||||
(Tip: If using the latter method in conjunction with the auto-reload feature,
|
|
||||||
ensure that `DELETE_OUTPUT_DIRECTORY` is set to `False` in your settings file.)
|
|
||||||
|
|
|
||||||
|
|
@ -12,34 +12,34 @@ original author wrote with some software design information.
|
||||||
Overall structure
|
Overall structure
|
||||||
=================
|
=================
|
||||||
|
|
||||||
What `pelican` does is take a list of files and process them into some
|
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
|
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
|
files, and the output is a blog, but both input and output can be anything you
|
||||||
want.
|
want.
|
||||||
|
|
||||||
The logic is separated into different classes and concepts:
|
The logic is separated into different classes and concepts:
|
||||||
|
|
||||||
* `writers` are responsible for writing files: .html files, RSS feeds, and so
|
* **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
|
on. Since those operations are commonly used, the object is created once and
|
||||||
then passed to the generators.
|
then passed to the generators.
|
||||||
|
|
||||||
* `readers` are used to read from various formats (Markdown and
|
* **Readers** are used to read from various formats (Markdown and
|
||||||
reStructuredText for now, but the system is extensible). Given a file, they return
|
reStructuredText for now, but the system is extensible). Given a file, they return
|
||||||
metadata (author, tags, category, etc.) and content (HTML-formatted).
|
metadata (author, tags, category, etc.) and content (HTML-formatted).
|
||||||
|
|
||||||
* `generators` generate the different outputs. For instance, Pelican comes with
|
* **Generators** generate the different outputs. For instance, Pelican comes with
|
||||||
`ArticlesGenerator` and `PageGenerator`. Given a configuration, they can do
|
``ArticlesGenerator`` and ``PageGenerator``. Given a configuration, they can do
|
||||||
whatever they want. Most of the time, it's generating files from inputs.
|
whatever they want. Most of the time, it's generating files from inputs.
|
||||||
|
|
||||||
* `pelican` also uses `templates`, so it's easy to write your 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
|
syntax is `Jinja2 <http://jinja.pocoo.org/>`_ and is very easy to learn, so
|
||||||
to jump in and build your own theme.
|
don't hesitate to jump in and build your own theme.
|
||||||
|
|
||||||
How to implement a new reader?
|
How to implement a new reader?
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Is there an awesome markup language you want to add to Pelican?
|
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`
|
Well, the only thing you have to do is to create a class with a ``read``
|
||||||
method that returns HTML content and some metadata.
|
method that returns HTML content and some metadata.
|
||||||
|
|
||||||
Take a look at the Markdown reader::
|
Take a look at the Markdown reader::
|
||||||
|
|
@ -65,8 +65,8 @@ Take a look at the Markdown reader::
|
||||||
Simple, isn't it?
|
Simple, isn't it?
|
||||||
|
|
||||||
If your new reader requires additional Python dependencies, then you should wrap
|
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
|
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.
|
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
|
This makes it possible for users to continue using their favourite markup method
|
||||||
without needing to install modules for formats they don't use.
|
without needing to install modules for formats they don't use.
|
||||||
|
|
||||||
|
|
@ -76,17 +76,17 @@ How to implement a new generator?
|
||||||
Generators have two important methods. You're not forced to create
|
Generators have two important methods. You're not forced to create
|
||||||
both; only the existing ones will be called.
|
both; only the existing ones will be called.
|
||||||
|
|
||||||
* `generate_context`, that is called first, 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
|
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
|
context is shared between all generators, and will be passed to the
|
||||||
templates. For instance, the `PageGenerator` `generate_context` method finds
|
templates. For instance, the ``PageGenerator`` ``generate_context`` method
|
||||||
all the pages, transforms them into objects, and populates the context with
|
finds all the pages, transforms them into objects, and populates the context
|
||||||
them. Be careful *not* to output anything using this context at this stage,
|
with them. Be careful *not* to output anything using this context at this
|
||||||
as it is likely to change by the effect of other generators.
|
stage, as it is likely to change by the effect of other generators.
|
||||||
|
|
||||||
* `generate_output` is then called. And guess what is it made for? Oh,
|
* ``generate_output`` is then called. And guess what is it made for? Oh,
|
||||||
generating the output. :) It's here that you may want to look at the context
|
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
|
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
|
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.
|
the disk (using the writer method ``write_file``) for each page encountered.
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
Plugins
|
Plugins
|
||||||
#######
|
#######
|
||||||
|
|
||||||
Since version 3.0, pelican manages plugins. Plugins are a way to add features
|
Since version 3.0, Pelican manages plugins. Plugins are a way to add features
|
||||||
to pelican without having to directly hack pelican code.
|
to Pelican without having to directly hack Pelican code.
|
||||||
|
|
||||||
Pelican is shipped with a set of core plugins, but you can easily implement
|
Pelican is shipped with a set of core plugins, but you can easily implement
|
||||||
your own (and this page describes how).
|
your own (and this page describes how).
|
||||||
|
|
||||||
How to use plugins?
|
How to use plugins
|
||||||
====================
|
==================
|
||||||
|
|
||||||
To load plugins, you have to specify them in your settings file. You have two
|
To load plugins, you have to specify them in your settings file. You have two
|
||||||
ways to do so.
|
ways to do so.
|
||||||
|
|
@ -23,21 +23,21 @@ Or by importing them and adding them to the list::
|
||||||
from pelican.plugins import gravatar
|
from pelican.plugins import gravatar
|
||||||
PLUGINS = [gravatar, ]
|
PLUGINS = [gravatar, ]
|
||||||
|
|
||||||
If your plugins are not in an importable path, you can specify a `PLUGIN_PATH`
|
If your plugins are not in an importable path, you can specify a ``PLUGIN_PATH``
|
||||||
in the settings::
|
in the settings::
|
||||||
|
|
||||||
PLUGIN_PATH = "plugins"
|
PLUGIN_PATH = "plugins"
|
||||||
PLUGINS = ["list", "of", "plugins"]
|
PLUGINS = ["list", "of", "plugins"]
|
||||||
|
|
||||||
How to create plugins?
|
How to create plugins
|
||||||
======================
|
=====================
|
||||||
|
|
||||||
Plugins are based on the concept of signals. Pelican sends signals and plugins
|
Plugins are based on the concept of signals. Pelican sends signals, and plugins
|
||||||
subscribe to those signals. The list of signals are defined in a following
|
subscribe to those signals. The list of signals are defined in a following
|
||||||
section.
|
section.
|
||||||
|
|
||||||
The only rule to follow for plugins is to define a `register` callable, in
|
The only rule to follow for plugins is to define a ``register`` callable, in
|
||||||
which you map the signals to your plugin logic. Let's take a simple exemple::
|
which you map the signals to your plugin logic. Let's take a simple example::
|
||||||
|
|
||||||
from pelican import signals
|
from pelican import signals
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ List of plugins
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Not all the list are described here, but a few of them have been extracted from
|
Not all the list are described here, but a few of them have been extracted from
|
||||||
pelican core and provided in pelican.plugins. They are described here:
|
the Pelican core and provided in ``pelican.plugins``. They are described here:
|
||||||
|
|
||||||
Tag cloud
|
Tag cloud
|
||||||
---------
|
---------
|
||||||
|
|
@ -82,7 +82,7 @@ Github Activity
|
||||||
This plugin makes use of the ``feedparser`` library that you'll need to
|
This plugin makes use of the ``feedparser`` library that you'll need to
|
||||||
install.
|
install.
|
||||||
|
|
||||||
Set the GITHUB_ACTIVITY_FEED parameter to your github activity feed.
|
Set the ``GITHUB_ACTIVITY_FEED`` parameter to your Github activity feed.
|
||||||
For example, my setting would look like::
|
For example, my setting would look like::
|
||||||
|
|
||||||
GITHUB_ACTIVITY_FEED = 'https://github.com/kpanic.atom'
|
GITHUB_ACTIVITY_FEED = 'https://github.com/kpanic.atom'
|
||||||
|
|
@ -105,4 +105,4 @@ variable, as in the example::
|
||||||
|
|
||||||
|
|
||||||
``github_activity`` is a list of lists. The first element is the title
|
``github_activity`` is a list of lists. The first element is the title
|
||||||
and the second element is the raw html from github.
|
and the second element is the raw HTML from Github.
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,40 @@
|
||||||
Some history about pelican
|
Some history about Pelican
|
||||||
##########################
|
##########################
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
This page comes from a report the original author (Alexis Métaireau) wrote
|
This page comes from a report the original author (Alexis Métaireau) wrote
|
||||||
right after writing pelican, in december 2010. The information may not be
|
right after writing Pelican, in December 2010. The information may not be
|
||||||
up to date.
|
up-to-date.
|
||||||
|
|
||||||
Pelican is a simple static blog generator. It parses markup files
|
Pelican is a simple static blog generator. It parses markup files
|
||||||
(markdown or restructured text for now), and generate a HTML folder
|
(Markdown or reStructuredText for now) and generates an HTML folder
|
||||||
with all the files in it.
|
with all the files in it.
|
||||||
I've chosen to use python to implement pelican because it seemed to
|
I've chosen to use Python to implement Pelican because it seemed to
|
||||||
be simple and to fit to my needs. I did not wanted to define a class for
|
be simple and to fit to my needs. I did not wanted to define a class for
|
||||||
each thing, but still wanted to keep my things loosely coupled.
|
each thing, but still wanted to keep my things loosely coupled.
|
||||||
It turns out that it was exactly what I wanted. From time to time,
|
It turns out that it was exactly what I wanted. From time to time,
|
||||||
thanks to the feedback of some users, it took me a very few time to
|
thanks to the feedback of some users, it took me a very few time to
|
||||||
provide fixes on it. So far, I've re-factored the pelican code by two
|
provide fixes on it. So far, I've re-factored the Pelican code by two
|
||||||
times, each time took less than 30 minutes.
|
times; each time took less than 30 minutes.
|
||||||
|
|
||||||
Use case
|
Use case
|
||||||
========
|
========
|
||||||
|
|
||||||
I was previously using wordpress, a solution you can host on a web
|
I was previously using WordPress, a solution you can host on a web
|
||||||
server to manage your blog. Most of the time, I prefer using markup
|
server to manage your blog. Most of the time, I prefer using markup
|
||||||
languages such as Markdown or RestructuredText to type my articles.
|
languages such as Markdown or reStructuredText to type my articles.
|
||||||
To do so, I use vim. I think it is important to let the people choose the
|
To do so, I use vim. I think it is important to let the people choose the
|
||||||
tool they want to write the articles. In my opinion, a blog manager
|
tool they want to write the articles. In my opinion, a blog manager
|
||||||
should just allow you to take any kind of input and transform it to a
|
should just allow you to take any kind of input and transform it to a
|
||||||
weblog. That's what pelican does.
|
weblog. That's what Pelican does.
|
||||||
You can write your articles using the tool you want, and the markup
|
You can write your articles using the tool you want, and the markup
|
||||||
language you want, and then generate a static HTML weblog
|
language you want, and then generate a static HTML weblog.
|
||||||
|
|
||||||
.. image:: _static/overall.png
|
.. image:: _static/overall.png
|
||||||
|
|
||||||
To be flexible enough, pelican have a template support, so you can
|
To be flexible enough, Pelican has template support, so you can easily write
|
||||||
easily write you own themes if you want to.
|
your own themes if you want to.
|
||||||
|
|
||||||
Design process
|
Design process
|
||||||
==============
|
==============
|
||||||
|
|
@ -42,19 +42,18 @@ Design process
|
||||||
Pelican came from a need I have. I started by creating a single file
|
Pelican came from a need I have. I started by creating a single file
|
||||||
application, and I have make it grow to support what it does by now.
|
application, and I have make it grow to support what it does by now.
|
||||||
To start, I wrote a piece of documentation about what I wanted to do.
|
To start, I wrote a piece of documentation about what I wanted to do.
|
||||||
Then, I have created the content I wanted to parse (the restructured
|
Then, I created the content I wanted to parse (the reStructuredText files)
|
||||||
text files), and started experimenting with the code.
|
and started experimenting with the code. Pelican was 200 lines long and
|
||||||
Pelican was 200 lines long, and contained almost ten functions and one
|
contained almost ten functions and one class when it was first usable.
|
||||||
class when it was first usable.
|
|
||||||
|
|
||||||
I have been facing different problems all over the time, and wanted to
|
I have been facing different problems all over the time and wanted to
|
||||||
add features to pelican while using it. The first change I have done was
|
add features to Pelican while using it. The first change I have done was
|
||||||
to add the support of a settings file. It is possible to pass the options to
|
to add the support of a settings file. It is possible to pass the options to
|
||||||
the command line, but can be tedious if there is a lot of them.
|
the command line, but can be tedious if there is a lot of them.
|
||||||
In the same way, I have added the support of different things over
|
In the same way, I have added the support of different things over
|
||||||
time: atom feeds, multiple themes, multiple markup support, etc.
|
time: Atom feeds, multiple themes, multiple markup support, etc.
|
||||||
At some point, it appears that the “only one file” mantra was not good
|
At some point, it appears that the "only one file" mantra was not good
|
||||||
enough for pelican, so I decided to rework a bit all that, and split this in
|
enough for Pelican, so I decided to rework a bit all that, and split this in
|
||||||
multiple different files.
|
multiple different files.
|
||||||
|
|
||||||
I’ve separated the logic in different classes and concepts:
|
I’ve separated the logic in different classes and concepts:
|
||||||
|
|
@ -64,59 +63,59 @@ I’ve separated the logic in different classes and concepts:
|
||||||
Since those operations are commonly used, the object is created
|
Since those operations are commonly used, the object is created
|
||||||
once, and then passed to the generators.
|
once, and then passed to the generators.
|
||||||
|
|
||||||
* *readers* are used to read from various formats (Markdown, and
|
* *readers* are used to read from various formats (Markdown and
|
||||||
Restructured Text for now, but the system is extensible). Given a
|
reStructuredText for now, but the system is extensible). Given a
|
||||||
file, they return metadata (author, tags, category etc) and
|
file, they return metadata (author, tags, category, etc) and
|
||||||
content (HTML formated).
|
content (HTML formatted).
|
||||||
|
|
||||||
* *generators* generate the different outputs. For instance, pelican
|
* *generators* generate the different outputs. For instance, Pelican
|
||||||
comes with an ArticlesGenerator and PagesGenerator, into
|
comes with an ArticlesGenerator and PagesGenerator, into
|
||||||
others. Given a configuration, they can do whatever you want
|
others. Given a configuration, they can do whatever you want
|
||||||
them to do. Most of the time it’s generating files from inputs
|
them to do. Most of the time it's generating files from inputs
|
||||||
(user inputs and files).
|
(user inputs and files).
|
||||||
|
|
||||||
I also deal with contents objects. They can be `Articles`, `Pages`, `Quotes`,
|
I also deal with contents objects. They can be ``Articles``, ``Pages``,
|
||||||
or whatever you want. They are defined in the contents.py module,
|
``Quotes``, or whatever you want. They are defined in the ``contents.py``
|
||||||
and represent some content to be used by the program.
|
module and represent some content to be used by the program.
|
||||||
|
|
||||||
In more details
|
In more detail
|
||||||
===============
|
==============
|
||||||
|
|
||||||
Here is an overview of the classes involved in pelican.
|
Here is an overview of the classes involved in Pelican.
|
||||||
|
|
||||||
.. image:: _static/uml.jpg
|
.. image:: _static/uml.jpg
|
||||||
|
|
||||||
The interface do not really exists, and I have added it only to clarify the
|
The interface does not really exist, and I have added it only to clarify the
|
||||||
whole picture. I do use duck typing, and not interfaces.
|
whole picture. I do use duck typing and not interfaces.
|
||||||
|
|
||||||
Internally, the following process is followed:
|
Internally, the following process is followed:
|
||||||
|
|
||||||
* First of all, the command line is parsed, and some content from
|
* First of all, the command line is parsed, and some content from
|
||||||
the user are used to initialize the different generator objects.
|
the user is used to initialize the different generator objects.
|
||||||
|
|
||||||
* A `context` is created. It contains the settings from the command
|
* A ``context`` is created. It contains the settings from the command
|
||||||
line and a settings file if provided.
|
line and a settings file if provided.
|
||||||
* The `generate_context` method of each generator is called, updating
|
* The ``generate_context`` method of each generator is called, updating
|
||||||
the context.
|
the context.
|
||||||
|
|
||||||
* The writer is created, and given to the `generate_output` method of
|
* The writer is created and given to the ``generate_output`` method of
|
||||||
each generator.
|
each generator.
|
||||||
|
|
||||||
I make two calls because it is important that when the output is
|
I make two calls because it is important that when the output is
|
||||||
generated by the generators, the context will not change. In other
|
generated by the generators, the context will not change. In other
|
||||||
words, the first method `generate_context` should modify the context,
|
words, the first method ``generate_context`` should modify the context,
|
||||||
whereas the second `generate_output` method should not.
|
whereas the second ``generate_output`` method should not.
|
||||||
|
|
||||||
Then, it is up to the generators to do what the want, in the
|
Then, it is up to the generators to do what the want, in the
|
||||||
`generate_context` and `generate_content` method.
|
``generate_context`` and ``generate_content`` method.
|
||||||
Taking the `ArticlesGenerator` class will help to understand some others
|
Taking the ``ArticlesGenerator`` class will help to understand some others
|
||||||
concepts. Here is what happens when calling the `generate_context`
|
concepts. Here is what happens when calling the ``generate_context``
|
||||||
method:
|
method:
|
||||||
|
|
||||||
* Read the folder “path”, looking for restructured text files, load
|
* Read the folder “path”, looking for restructured text files, load
|
||||||
each of them, and construct a content object (`Article`) with it. To do so,
|
each of them, and construct a content object (``Article``) with it. To do so,
|
||||||
use `Reader` objects.
|
use ``Reader`` objects.
|
||||||
* Update the `context` with all those articles.
|
* Update the ``context`` with all those articles.
|
||||||
|
|
||||||
Then, the `generate_content` method uses the `context` and the `writer` to
|
Then, the ``generate_content`` method uses the ``context`` and the ``writer`` to
|
||||||
generate the wanted output
|
generate the wanted output.
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ Setting name (default value) What doe
|
||||||
timestamp information (mtime) if it can't get
|
timestamp information (mtime) if it can't get
|
||||||
date information from the metadata.
|
date information from the metadata.
|
||||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
||||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory as well as
|
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
|
||||||
the generated files.
|
generating new files.
|
||||||
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
||||||
here or a single string representing one locale.
|
here or a single string representing one locale.
|
||||||
When providing a list, all the locales will be tried
|
When providing a list, all the locales will be tried
|
||||||
|
|
@ -59,18 +59,16 @@ Setting name (default value) What doe
|
||||||
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
||||||
of your documents. You will need to install
|
of your documents. You will need to install
|
||||||
`rst2pdf`.
|
`rst2pdf`.
|
||||||
`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or
|
`RELATIVE_URLS` (``True``) Defines whether Pelican should use document-relative URLs or
|
||||||
not.
|
not. If set to ``False``, Pelican will use the SITEURL
|
||||||
|
setting to construct absolute URLs.
|
||||||
`PLUGINS` (``[]``) The list of plugins to load. See :ref:`plugins`.
|
`PLUGINS` (``[]``) The list of plugins to load. See :ref:`plugins`.
|
||||||
`SITENAME` (``'A Pelican Blog'``) Your site name
|
`SITENAME` (``'A Pelican Blog'``) Your site name
|
||||||
`SITEURL` Base URL of your website. Not defined by default,
|
`SITEURL` Base URL of your website. Not defined by default,
|
||||||
which means the base URL is assumed to be "/" with a
|
so it is best to specify your SITEURL; if you do not, feeds
|
||||||
root-relative URL structure. If `SITEURL` is specified
|
will not be generated with properly-formed URLs. You should
|
||||||
explicitly, there should be no trailing slash at the end,
|
include ``http://`` and your domain, with no trailing
|
||||||
and URLs will be generated with an absolute URL structure
|
slash at the end. Example: ``SITEURL = 'http://mydomain.com'``
|
||||||
(including the domain). If you want to use relative URLs
|
|
||||||
instead of root-relative or absolute URLs, you should
|
|
||||||
instead use the `RELATIVE_URL` setting.
|
|
||||||
`STATIC_PATHS` (``['images']``) The static paths you want to have accessible
|
`STATIC_PATHS` (``['images']``) The static paths you want to have accessible
|
||||||
on the output path "static". By default,
|
on the output path "static". By default,
|
||||||
Pelican will copy the 'images' folder to the
|
Pelican will copy the 'images' folder to the
|
||||||
|
|
@ -107,6 +105,15 @@ Setting name (default value) What doe
|
||||||
URL settings
|
URL settings
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
The first thing to understand is that there are currently two supported methods
|
||||||
|
for URL formation: *relative* and *absolute*. Document-relative URLs are useful
|
||||||
|
when testing locally, and absolute URLs are reliable and most useful when
|
||||||
|
publishing. One method of supporting both is to have one Pelican configuration
|
||||||
|
file for local development and another for publishing. To see an example of this
|
||||||
|
type of setup, use the ``pelican-quickstart`` script as described at the top of
|
||||||
|
the :doc:`Getting Started<getting_started>` page, which will produce two separate
|
||||||
|
configuration files for local development and publishing, respectively.
|
||||||
|
|
||||||
You can customize the URLs and locations where files will be saved. The URLs and
|
You can customize the URLs and locations where files will be saved. The URLs and
|
||||||
SAVE_AS variables use Python's format strings. These variables allow you to place
|
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 them as
|
your articles in a location such as '{slug}/index.html' and link to them as
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@
|
||||||
How to create themes for Pelican
|
How to create themes for Pelican
|
||||||
################################
|
################################
|
||||||
|
|
||||||
Pelican uses the great `jinja2 <http://jinja.pocoo.org>`_ templating engine to
|
Pelican uses the great `Jinja2 <http://jinja.pocoo.org/>`_ templating engine to
|
||||||
generate its HTML output. The jinja2 syntax is really simple. If you want to
|
generate its HTML output. Jinja2 syntax is really simple. If you want to
|
||||||
create your own theme, feel free to take inspiration from the "simple" theme,
|
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>`_.
|
||||||
<https://github.com/ametaireau/pelican/tree/master/pelican/themes/simple/templates>`_
|
|
||||||
|
|
||||||
Structure
|
Structure
|
||||||
=========
|
=========
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,16 @@ file generator, we can take advantage of this.
|
||||||
|
|
||||||
User Pages
|
User Pages
|
||||||
----------
|
----------
|
||||||
Github allows you to create user pages in the form of ``username.github.com``.
|
GitHub allows you to create user pages in the form of ``username.github.com``.
|
||||||
Whatever is created in master branch will be published. For this purposes just
|
Whatever is created in the master branch will be published. For this purpose,
|
||||||
the output generated by pelican needs to pushed at github.
|
just the output generated by Pelican needs to pushed to GitHub.
|
||||||
|
|
||||||
So given a repository containing your articles, just run pelican over the posts
|
So given a repository containing your articles, just run Pelican over the posts
|
||||||
and deploy the master branch at github::
|
and deploy the master branch to GitHub::
|
||||||
|
|
||||||
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
|
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
|
||||||
|
|
||||||
Now add all the files in the output directory generated by pelican::
|
Now add all the files in the output directory generated by Pelican::
|
||||||
|
|
||||||
$ git add /path/to/output/*
|
$ git add /path/to/output/*
|
||||||
$ git commit -am "Your Message"
|
$ git commit -am "Your Message"
|
||||||
|
|
@ -31,12 +31,12 @@ Project Pages
|
||||||
-------------
|
-------------
|
||||||
For creating Project pages, a branch called ``gh-pages`` is used for publishing.
|
For creating Project pages, a branch called ``gh-pages`` is used for publishing.
|
||||||
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_ makes this
|
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_ makes this
|
||||||
really easy. You will have to install it::
|
really easy, which can be installed via::
|
||||||
|
|
||||||
$ pip install ghp-import
|
$ pip install ghp-import
|
||||||
|
|
||||||
Then, given a repository containing your articles, you would simply have
|
Then, given a repository containing your articles, you would simply run
|
||||||
to run Pelican and upload the output to GitHub::
|
Pelican and upload the output to GitHub::
|
||||||
|
|
||||||
$ pelican -s pelican.conf.py .
|
$ pelican -s pelican.conf.py .
|
||||||
$ ghp-import output
|
$ ghp-import output
|
||||||
|
|
@ -45,10 +45,8 @@ to run Pelican and upload the output to GitHub::
|
||||||
And that's it.
|
And that's it.
|
||||||
|
|
||||||
If you want, you can put that directly into a post-commit hook, so each time you
|
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!
|
commit, your blog is up-to-date on GitHub!
|
||||||
|
|
||||||
Put the following into `.git/hooks/post-commit`::
|
Put the following into ``.git/hooks/post-commit``::
|
||||||
|
|
||||||
pelican -s pelican.conf.py . && ghp-import output && git push origin
|
|
||||||
gh-pages
|
|
||||||
|
|
||||||
|
pelican -s pelican.conf.py . && ghp-import output && git push origin gh-pages
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,10 @@ class Pelican(object):
|
||||||
for plugin in self.plugins:
|
for plugin in self.plugins:
|
||||||
# if it's a string, then import it
|
# if it's a string, then import it
|
||||||
if isinstance(plugin, basestring):
|
if isinstance(plugin, basestring):
|
||||||
log.debug("Loading plugin `{0}' ...".format(plugin))
|
logger.debug("Loading plugin `{0}' ...".format(plugin))
|
||||||
plugin = __import__(plugin, globals(), locals(), 'module')
|
plugin = __import__(plugin, globals(), locals(), 'module')
|
||||||
|
|
||||||
log.debug("Registering plugin `{0}' ...".format(plugin.__name__))
|
logger.debug("Registering plugin `{0}' ...".format(plugin.__name__))
|
||||||
plugin.register()
|
plugin.register()
|
||||||
|
|
||||||
def _handle_deprecation(self):
|
def _handle_deprecation(self):
|
||||||
|
|
|
||||||
|
|
@ -357,10 +357,13 @@ class PagesGenerator(Generator):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.pages = []
|
self.pages = []
|
||||||
|
self.hidden_pages = []
|
||||||
|
self.hidden_translations = []
|
||||||
super(PagesGenerator, self).__init__(*args, **kwargs)
|
super(PagesGenerator, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def generate_context(self):
|
def generate_context(self):
|
||||||
all_pages = []
|
all_pages = []
|
||||||
|
hidden_pages = []
|
||||||
for f in self.get_files(
|
for f in self.get_files(
|
||||||
os.path.join(self.path, self.settings['PAGE_DIR']),
|
os.path.join(self.path, self.settings['PAGE_DIR']),
|
||||||
exclude=self.settings['PAGE_EXCLUDES']):
|
exclude=self.settings['PAGE_EXCLUDES']):
|
||||||
|
|
@ -373,15 +376,25 @@ class PagesGenerator(Generator):
|
||||||
filename=f)
|
filename=f)
|
||||||
if not is_valid_content(page, f):
|
if not is_valid_content(page, f):
|
||||||
continue
|
continue
|
||||||
all_pages.append(page)
|
if page.status == "published":
|
||||||
|
all_pages.append(page)
|
||||||
|
elif page.status == "hidden":
|
||||||
|
hidden_pages.append(page)
|
||||||
|
else:
|
||||||
|
logger.warning(u"Unknown status %s for file %s, skipping it." %
|
||||||
|
(repr(unicode.encode(page.status, 'utf-8')),
|
||||||
|
repr(f)))
|
||||||
|
|
||||||
|
|
||||||
self.pages, self.translations = process_translations(all_pages)
|
self.pages, self.translations = process_translations(all_pages)
|
||||||
|
self.hidden_pages, self.hidden_translations = process_translations(hidden_pages)
|
||||||
|
|
||||||
self._update_context(('pages', ))
|
self._update_context(('pages', ))
|
||||||
self.context['PAGES'] = self.pages
|
self.context['PAGES'] = self.pages
|
||||||
|
|
||||||
def generate_output(self, writer):
|
def generate_output(self, writer):
|
||||||
for page in chain(self.translations, self.pages):
|
for page in chain(self.translations, self.pages,
|
||||||
|
self.hidden_translations, self.hidden_pages):
|
||||||
writer.write_file(page.save_as, self.get_template('page'),
|
writer.write_file(page.save_as, self.get_template('page'),
|
||||||
self.context, page=page,
|
self.context, page=page,
|
||||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,13 @@ CONF = {
|
||||||
'ftp_host': 'localhost',
|
'ftp_host': 'localhost',
|
||||||
'ftp_user': 'anonymous',
|
'ftp_user': 'anonymous',
|
||||||
'ftp_target_dir': '/',
|
'ftp_target_dir': '/',
|
||||||
'ssh_host': 'locahost',
|
'ssh_host': 'localhost',
|
||||||
|
'ssh_port': 22,
|
||||||
'ssh_user': 'root',
|
'ssh_user': 'root',
|
||||||
'ssh_target_dir': '/var/www',
|
'ssh_target_dir': '/var/www',
|
||||||
'dropbox_dir' : '~/Dropbox/Public/',
|
'dropbox_dir' : '~/Dropbox/Public/',
|
||||||
'default_pagination' : 10,
|
'default_pagination' : 10,
|
||||||
|
'siteurl': '',
|
||||||
'lang': 'en'
|
'lang': 'en'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +90,7 @@ def ask(question, answer=str, default=None, l=None):
|
||||||
r = default
|
r = default
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
print("You must answer `yes' or `no'")
|
print("You must answer 'yes' or 'no'")
|
||||||
return r
|
return r
|
||||||
elif answer == int:
|
elif answer == int:
|
||||||
r = None
|
r = None
|
||||||
|
|
@ -111,12 +113,12 @@ def ask(question, answer=str, default=None, l=None):
|
||||||
print('You must enter an integer')
|
print('You must enter an integer')
|
||||||
return r
|
return r
|
||||||
else:
|
else:
|
||||||
raise NotImplemented('Arguent `answer` must be str, bool or integer')
|
raise NotImplemented('Argument `answer` must be str, bool, or integer')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="A kickstarter for pelican",
|
description="A kickstarter for Pelican",
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
parser.add_argument('-p', '--path', default=".",
|
parser.add_argument('-p', '--path', default=".",
|
||||||
help="The path to generate the blog into")
|
help="The path to generate the blog into")
|
||||||
|
|
@ -125,7 +127,7 @@ def main():
|
||||||
parser.add_argument('-a', '--author', metavar="author",
|
parser.add_argument('-a', '--author', metavar="author",
|
||||||
help='Set the author name of the website')
|
help='Set the author name of the website')
|
||||||
parser.add_argument('-l', '--lang', metavar="lang",
|
parser.add_argument('-l', '--lang', metavar="lang",
|
||||||
help='Set the default lang of the website')
|
help='Set the default web site language')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
@ -137,36 +139,44 @@ Please answer the following questions so this script can generate the files need
|
||||||
|
|
||||||
'''.format(v=__version__))
|
'''.format(v=__version__))
|
||||||
|
|
||||||
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new Web site ?', answer=str, default=args.path))
|
project = os.path.join(os.environ['VIRTUAL_ENV'], '.project')
|
||||||
CONF['sitename'] = ask('What will be the title of this Web site ?', answer=str, default=args.title)
|
if os.path.isfile(project):
|
||||||
CONF['author'] = ask('Who will be the author of this Web site ?', answer=str, default=args.author)
|
CONF['basedir'] = open(project, 'r').read().rstrip("\n")
|
||||||
CONF['lang'] = ask('What will be the default language of this Web site ?', str, args.lang or CONF['lang'], 2)
|
print('Using project associated with current virtual environment. Will save to:\n%s\n' % CONF['basedir'])
|
||||||
|
else:
|
||||||
|
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new web site?', answer=str, default=args.path))
|
||||||
|
|
||||||
CONF['with_pagination'] = ask('Do you want to enable article pagination ?', bool, bool(CONF['default_pagination']))
|
CONF['sitename'] = ask('What will be the title of this web site?', answer=str, default=args.title)
|
||||||
|
CONF['author'] = ask('Who will be the author of this web site?', answer=str, default=args.author)
|
||||||
|
CONF['lang'] = ask('What will be the default language of this web site?', str, args.lang or CONF['lang'], 2)
|
||||||
|
|
||||||
|
if ask('Do you want to specify a URL prefix? e.g., http://example.com ', answer=bool, default=True):
|
||||||
|
CONF['siteurl'] = ask('What is your URL prefix? (see above example; no trailing slash)', str, CONF['siteurl'])
|
||||||
|
|
||||||
|
CONF['with_pagination'] = ask('Do you want to enable article pagination?', bool, bool(CONF['default_pagination']))
|
||||||
|
|
||||||
if CONF['with_pagination']:
|
if CONF['with_pagination']:
|
||||||
CONF['default_pagination'] = ask('So how many articles per page do you want ?', int, CONF['default_pagination'])
|
CONF['default_pagination'] = ask('How many articles per page do you want?', int, CONF['default_pagination'])
|
||||||
else:
|
else:
|
||||||
CONF['default_pagination'] = False
|
CONF['default_pagination'] = False
|
||||||
|
|
||||||
mkfile = ask('Do you want to generate a Makefile to easily manage your website ?', bool, True)
|
mkfile = ask('Do you want to generate a Makefile to easily manage your website?', bool, True)
|
||||||
|
|
||||||
if mkfile:
|
if mkfile:
|
||||||
if ask('Do you want to upload your website using FTP ?', answer=bool, default=False):
|
if ask('Do you want to upload your website using FTP?', answer=bool, default=False):
|
||||||
CONF['ftp_host'] = ask('What is the hostname of your FTP server ?', str, CONF['ftp_host'])
|
CONF['ftp_host'] = ask('What is the hostname of your FTP server?', str, CONF['ftp_host'])
|
||||||
CONF['ftp_user'] = ask('What is your username on this server ?', str, CONF['ftp_user'])
|
CONF['ftp_user'] = ask('What is your username on that server?', str, CONF['ftp_user'])
|
||||||
CONF['ftp_target_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ftp_target_dir'])
|
CONF['ftp_target_dir'] = ask('Where do you want to put your web site on that server?', str, CONF['ftp_target_dir'])
|
||||||
|
if ask('Do you want to upload your website using SSH?', answer=bool, default=False):
|
||||||
if ask('Do you want to upload your website using SSH ?', answer=bool, default=False):
|
CONF['ssh_host'] = ask('What is the hostname of your SSH server?', str, CONF['ssh_host'])
|
||||||
CONF['ssh_host'] = ask('What is the hostname of your SSH server ?', str, CONF['ssh_host'])
|
CONF['ssh_port'] = ask('What is the port of your SSH server?', int, CONF['ssh_port'])
|
||||||
CONF['ssh_user'] = ask('What is your username on this server ?', str, CONF['ssh_user'])
|
CONF['ssh_user'] = ask('What is your username on that server?', str, CONF['ssh_user'])
|
||||||
CONF['ssh_target_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ssh_target_dir'])
|
CONF['ssh_target_dir'] = ask('Where do you want to put your web site on that server?', str, CONF['ssh_target_dir'])
|
||||||
|
if ask('Do you want to upload your website using Dropbox?', answer=bool, default=False):
|
||||||
if ask('Do you want to upload your website using Dropbox ?', answer=bool, default=False):
|
CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', str, CONF['dropbox_dir'])
|
||||||
CONF['dropbox_dir'] = ask('Where is your Dropbox directory ?', str, CONF['dropbox_dir'])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.join(CONF['basedir'], 'src'))
|
os.makedirs(os.path.join(CONF['basedir'], 'content'))
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
print('Error: {0}'.format(e))
|
print('Error: {0}'.format(e))
|
||||||
|
|
||||||
|
|
@ -176,8 +186,17 @@ Please answer the following questions so this script can generate the files need
|
||||||
print('Error: {0}'.format(e))
|
print('Error: {0}'.format(e))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(CONF['basedir'], 'pelican.conf.py'), 'w') as fd:
|
with open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w') as fd:
|
||||||
for line in get_template('pelican.conf.py'):
|
for line in get_template('pelicanconf.py'):
|
||||||
|
template = string.Template(line)
|
||||||
|
fd.write(template.safe_substitute(CONF))
|
||||||
|
fd.close()
|
||||||
|
except OSError, e:
|
||||||
|
print('Error: {0}'.format(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(os.path.join(CONF['basedir'], 'publishconf.py'), 'w') as fd:
|
||||||
|
for line in get_template('publishconf.py'):
|
||||||
template = string.Template(line)
|
template = string.Template(line)
|
||||||
fd.write(template.safe_substitute(CONF))
|
fd.write(template.safe_substitute(CONF))
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,11 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
parser.add_argument('-i', '--install', dest='to_install', nargs='+', metavar="theme path",
|
parser.add_argument('-i', '--install', dest='to_install', nargs='+', metavar="theme path",
|
||||||
help='The themes to install ')
|
help='The themes to install')
|
||||||
parser.add_argument('-r', '--remove', dest='to_remove', nargs='+', metavar="theme name",
|
parser.add_argument('-r', '--remove', dest='to_remove', nargs='+', metavar="theme name",
|
||||||
help='The themes to remove')
|
help='The themes to remove')
|
||||||
|
parser.add_argument('-U', '--upgrade', dest='to_upgrade', nargs='+',
|
||||||
|
metavar="theme path", help='The themes to upgrade')
|
||||||
parser.add_argument('-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path",
|
parser.add_argument('-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path",
|
||||||
help="Same as `--install', but create a symbolic link instead of copying the theme. Useful for theme development")
|
help="Same as `--install', but create a symbolic link instead of copying the theme. Useful for theme development")
|
||||||
parser.add_argument('-c', '--clean', dest='clean', action="store_true",
|
parser.add_argument('-c', '--clean', dest='clean', action="store_true",
|
||||||
|
|
@ -62,6 +64,9 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
to_install = args.to_install or args.to_upgrade
|
||||||
|
to_sym = args.to_symlink or args.clean
|
||||||
|
|
||||||
|
|
||||||
if args.action:
|
if args.action:
|
||||||
|
|
@ -69,8 +74,7 @@ def main():
|
||||||
list_themes(args.verbose)
|
list_themes(args.verbose)
|
||||||
elif args.action is 'path':
|
elif args.action is 'path':
|
||||||
print(_THEMES_PATH)
|
print(_THEMES_PATH)
|
||||||
elif args.to_install or args.to_remove or args.to_symlink or args.clean:
|
elif to_install or args.to_remove or to_sym:
|
||||||
|
|
||||||
if args.to_remove:
|
if args.to_remove:
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print('Removing themes...')
|
print('Removing themes...')
|
||||||
|
|
@ -85,6 +89,13 @@ def main():
|
||||||
for i in args.to_install:
|
for i in args.to_install:
|
||||||
install(i, v=args.verbose)
|
install(i, v=args.verbose)
|
||||||
|
|
||||||
|
if args.to_upgrade:
|
||||||
|
if args.verbose:
|
||||||
|
print('Upgrading themes...')
|
||||||
|
|
||||||
|
for i in args.to_upgrade:
|
||||||
|
install(i, v=args.verbose, u=True)
|
||||||
|
|
||||||
if args.to_symlink:
|
if args.to_symlink:
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print('Linking themes...')
|
print('Linking themes...')
|
||||||
|
|
@ -149,17 +160,21 @@ def remove(theme_name, v=False):
|
||||||
err(target + ' : no such file or directory')
|
err(target + ' : no such file or directory')
|
||||||
|
|
||||||
|
|
||||||
def install(path, v=False):
|
def install(path, v=False, u=False):
|
||||||
"""Installs a theme"""
|
"""Installs a theme"""
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
err(path + ' : no such file or directory')
|
err(path + ' : no such file or directory')
|
||||||
elif not os.path.isdir(path):
|
elif not os.path.isdir(path):
|
||||||
err(path + ' : no a directory')
|
err(path + ' : not a directory')
|
||||||
else:
|
else:
|
||||||
theme_name = os.path.basename(os.path.normpath(path))
|
theme_name = os.path.basename(os.path.normpath(path))
|
||||||
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
||||||
if os.path.exists(theme_path):
|
exists = os.path.exists(theme_path)
|
||||||
|
if exists and not u:
|
||||||
err(path + ' : already exists')
|
err(path + ' : already exists')
|
||||||
|
elif exists and u:
|
||||||
|
remove(theme_name, v)
|
||||||
|
install(path, v)
|
||||||
else:
|
else:
|
||||||
if v:
|
if v:
|
||||||
print("Copying `{p}' to `{t}' ...".format(p=path, t=theme_path))
|
print("Copying `{p}' to `{t}' ...".format(p=path, t=theme_path))
|
||||||
|
|
@ -174,7 +189,7 @@ def symlink(path, v=False):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
err(path + ' : no such file or directory')
|
err(path + ' : no such file or directory')
|
||||||
elif not os.path.isdir(path):
|
elif not os.path.isdir(path):
|
||||||
err(path + ' : no a directory')
|
err(path + ' : not a directory')
|
||||||
else:
|
else:
|
||||||
theme_name = os.path.basename(os.path.normpath(path))
|
theme_name = os.path.basename(os.path.normpath(path))
|
||||||
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@ PELICAN=$pelican
|
||||||
PELICANOPTS=$pelicanopts
|
PELICANOPTS=$pelicanopts
|
||||||
|
|
||||||
BASEDIR=$$(PWD)
|
BASEDIR=$$(PWD)
|
||||||
INPUTDIR=$$(BASEDIR)/src
|
INPUTDIR=$$(BASEDIR)/content
|
||||||
OUTPUTDIR=$$(BASEDIR)/output
|
OUTPUTDIR=$$(BASEDIR)/output
|
||||||
CONFFILE=$$(BASEDIR)/pelican.conf.py
|
CONFFILE=$$(BASEDIR)/pelicanconf.py
|
||||||
|
PUBLISHCONF=$$(BASEDIR)/publishconf.py
|
||||||
|
|
||||||
FTP_HOST=$ftp_host
|
FTP_HOST=$ftp_host
|
||||||
FTP_USER=$ftp_user
|
FTP_USER=$ftp_user
|
||||||
FTP_TARGET_DIR=$ftp_target_dir
|
FTP_TARGET_DIR=$ftp_target_dir
|
||||||
|
|
||||||
SSH_HOST=$ssh_host
|
SSH_HOST=$ssh_host
|
||||||
|
SSH_PORT=$ssh_port
|
||||||
SSH_USER=$ssh_user
|
SSH_USER=$ssh_user
|
||||||
SSH_TARGET_DIR=$ssh_target_dir
|
SSH_TARGET_DIR=$ssh_target_dir
|
||||||
|
|
||||||
|
|
@ -22,10 +24,11 @@ help:
|
||||||
@echo 'Usage: '
|
@echo 'Usage: '
|
||||||
@echo ' make html (re)generate the web site '
|
@echo ' make html (re)generate the web site '
|
||||||
@echo ' make clean remove the generated files '
|
@echo ' make clean remove the generated files '
|
||||||
@echo ' ftp_upload upload the web site using FTP '
|
@echo ' make publish generate using production settings '
|
||||||
@echo ' ssh_upload upload the web site using SSH '
|
@echo ' ftp_upload upload the web site via FTP '
|
||||||
@echo ' dropbox_upload upload the web site using Dropbox '
|
@echo ' ssh_upload upload the web site via SSH '
|
||||||
@echo ' rsync_upload upload the web site using rsync/ssh'
|
@echo ' dropbox_upload upload the web site via Dropbox '
|
||||||
|
@echo ' rsync_upload upload the web site via rsync/ssh '
|
||||||
@echo ' '
|
@echo ' '
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,23 +39,31 @@ $$(OUTPUTDIR)/%.html:
|
||||||
$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -fr $$(OUTPUTDIR)
|
find $$(OUTPUTDIR) -mindepth 1 -delete
|
||||||
mkdir $$(OUTPUTDIR)
|
|
||||||
|
|
||||||
dropbox_upload: $$(OUTPUTDIR)/index.html
|
regenerate: clean
|
||||||
|
$$(PELICAN) -r $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||||
|
|
||||||
|
serve:
|
||||||
|
cd $$(OUTPUTDIR) && python -m SimpleHTTPServer
|
||||||
|
|
||||||
|
publish:
|
||||||
|
$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(PUBLISHCONF) $$(PELICANOPTS)
|
||||||
|
|
||||||
|
dropbox_upload: publish
|
||||||
cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
||||||
|
|
||||||
ssh_upload: $$(OUTPUTDIR)/index.html
|
ssh_upload: publish
|
||||||
scp -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||||
|
|
||||||
rsync_upload: $$(OUTPUTDIR)/index.html
|
rsync_upload: publish
|
||||||
rsync -e ssh -P -rvz --delete $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
rsync -e "ssh -p $(SSH_PORT)" -P -rvz --delete $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
||||||
|
|
||||||
ftp_upload: $$(OUTPUTDIR)/index.html
|
ftp_upload: publish
|
||||||
lftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit"
|
lftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit"
|
||||||
|
|
||||||
github: $$(OUTPUTDIR)/index.html
|
github: publish
|
||||||
ghp-import $$(OUTPUTDIR)
|
ghp-import $$(OUTPUTDIR)
|
||||||
git push origin gh-pages
|
git push origin gh-pages
|
||||||
|
|
||||||
.PHONY: html help clean ftp_upload ssh_upload rsync_upload dropbox_upload github
|
.PHONY: html help clean regenerate serve publish ftp_upload ssh_upload rsync_upload dropbox_upload github
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*- #
|
|
||||||
|
|
||||||
AUTHOR = u"$author"
|
|
||||||
SITENAME = u"$sitename"
|
|
||||||
SITEURL = '/'
|
|
||||||
|
|
||||||
TIMEZONE = 'Europe/Paris'
|
|
||||||
|
|
||||||
DEFAULT_LANG='$lang'
|
|
||||||
|
|
||||||
# Blogroll
|
|
||||||
LINKS = (
|
|
||||||
('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
|
||||||
('Python.org', 'http://python.org'),
|
|
||||||
('Jinja2', 'http://jinja.pocoo.org'),
|
|
||||||
('You can modify those links in your config file', '#')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Social widget
|
|
||||||
SOCIAL = (
|
|
||||||
('You can add links in your config file', '#'),
|
|
||||||
)
|
|
||||||
|
|
||||||
DEFAULT_PAGINATION = $default_pagination
|
|
||||||
22
pelican/tools/templates/pelicanconf.py.in
Normal file
22
pelican/tools/templates/pelicanconf.py.in
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*- #
|
||||||
|
|
||||||
|
AUTHOR = u"$author"
|
||||||
|
SITENAME = u"$sitename"
|
||||||
|
SITEURL = ''
|
||||||
|
|
||||||
|
TIMEZONE = 'Europe/Paris'
|
||||||
|
|
||||||
|
DEFAULT_LANG = '$lang'
|
||||||
|
|
||||||
|
# Blogroll
|
||||||
|
LINKS = (('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
||||||
|
('Python.org', 'http://python.org'),
|
||||||
|
('Jinja2', 'http://jinja.pocoo.org'),
|
||||||
|
('You can modify those links in your config file', '#'),)
|
||||||
|
|
||||||
|
# Social widget
|
||||||
|
SOCIAL = (('You can add links in your config file', '#'),
|
||||||
|
('Another social link', '#'),)
|
||||||
|
|
||||||
|
DEFAULT_PAGINATION = $default_pagination
|
||||||
16
pelican/tools/templates/publishconf.py.in
Normal file
16
pelican/tools/templates/publishconf.py.in
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*- #
|
||||||
|
|
||||||
|
from pelicanconf import *
|
||||||
|
|
||||||
|
SITEURL = '$siteurl'
|
||||||
|
|
||||||
|
DELETE_OUTPUT_DIRECTORY = True
|
||||||
|
|
||||||
|
# Following items are often useful when publishing
|
||||||
|
|
||||||
|
# Uncomment following line for absolute URLs in production:
|
||||||
|
#RELATIVE_URLS = False
|
||||||
|
|
||||||
|
#DISQUS_SITENAME = ""
|
||||||
|
#GOOGLE_ANALYTICS = ""
|
||||||
|
|
@ -97,10 +97,22 @@ def clean_output_dir(path):
|
||||||
"""Remove all the files from the output directory"""
|
"""Remove all the files from the output directory"""
|
||||||
|
|
||||||
# remove all the existing content from the output folder
|
# remove all the existing content from the output folder
|
||||||
try:
|
for filename in os.listdir(path):
|
||||||
shutil.rmtree(path)
|
file = os.path.join(path, filename)
|
||||||
except Exception:
|
if os.path.isdir(file):
|
||||||
pass
|
try:
|
||||||
|
shutil.rmtree(file)
|
||||||
|
logger.debug("Deleted directory %s" % file)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Unable to delete directory %s; %e" % file, e)
|
||||||
|
elif os.path.isfile(file) or os.path.islink(file):
|
||||||
|
try:
|
||||||
|
os.remove(file)
|
||||||
|
logger.debug("Deleted file/link %s" % file)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Unable to delete file %s; %e" % file, e)
|
||||||
|
else:
|
||||||
|
logger.error("Unable to delete %s, file type unknown" % file)
|
||||||
|
|
||||||
|
|
||||||
def get_relative_path(filename):
|
def get_relative_path(filename):
|
||||||
|
|
|
||||||
9
samples/content/pages/hidden_page.rst
Normal file
9
samples/content/pages/hidden_page.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
This is a test hidden page
|
||||||
|
##########################
|
||||||
|
|
||||||
|
:category: test
|
||||||
|
:status: hidden
|
||||||
|
|
||||||
|
This is great for things like error(404) pages
|
||||||
|
Anyone can see this page but it's not linked to anywhere!
|
||||||
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz', 'blinker']
|
requires = ['feedgenerator', 'jinja2 >= 2.4', 'pygments', 'docutils', 'pytz', 'blinker']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import argparse
|
import argparse
|
||||||
|
|
|
||||||
8
tests/TestPages/bad_page.rst
Normal file
8
tests/TestPages/bad_page.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
This is a test bad page
|
||||||
|
#######################
|
||||||
|
|
||||||
|
:status: invalid
|
||||||
|
|
||||||
|
The quick brown fox jumped over the lazy dog's back.
|
||||||
|
|
||||||
|
The status here is invalid, the page should not render.
|
||||||
8
tests/TestPages/hidden_page.rst
Normal file
8
tests/TestPages/hidden_page.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
This is a test hidden page
|
||||||
|
##########################
|
||||||
|
|
||||||
|
:status: hidden
|
||||||
|
|
||||||
|
The quick brown fox jumped over the lazy dog's back.
|
||||||
|
|
||||||
|
This page is hidden
|
||||||
12
tests/TestPages/hidden_page_markdown.md
Normal file
12
tests/TestPages/hidden_page_markdown.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
title: This is a markdown test hidden page
|
||||||
|
status: hidden
|
||||||
|
|
||||||
|
Test Markdown File Header
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Used for pelican test
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The quick brown fox jumped over the lazy dog's back.
|
||||||
|
|
||||||
|
This page is hidden
|
||||||
4
tests/TestPages/page.rst
Normal file
4
tests/TestPages/page.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
This is a test page
|
||||||
|
###################
|
||||||
|
|
||||||
|
The quick brown fox jumped over the lazy dog's back.
|
||||||
9
tests/TestPages/page_markdown.md
Normal file
9
tests/TestPages/page_markdown.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
title: This is a markdown test page
|
||||||
|
|
||||||
|
Test Markdown File Header
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Used for pelican test
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The quick brown fox jumped over the lazy dog's back.
|
||||||
|
|
@ -4,7 +4,7 @@ from mock import MagicMock
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from pelican.generators import ArticlesGenerator, LessCSSGenerator
|
from pelican.generators import ArticlesGenerator, LessCSSGenerator, PagesGenerator
|
||||||
from pelican.settings import _DEFAULT_CONFIG
|
from pelican.settings import _DEFAULT_CONFIG
|
||||||
from .support import unittest, temporary_folder, skipIfNoExecutable
|
from .support import unittest, temporary_folder, skipIfNoExecutable
|
||||||
|
|
||||||
|
|
@ -94,6 +94,48 @@ class TestArticlesGenerator(unittest.TestCase):
|
||||||
write.assert_called_count == 0
|
write.assert_called_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestPageGenerator(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Every time you want to test for a new field;
|
||||||
|
Make sure the test pages in "TestPages" have all the fields
|
||||||
|
Add it to distilled in distill_pages
|
||||||
|
Then update the assertItemsEqual in test_generate_context to match expected
|
||||||
|
"""
|
||||||
|
|
||||||
|
def distill_pages(self, pages):
|
||||||
|
distilled = []
|
||||||
|
for page in pages:
|
||||||
|
distilled.append([
|
||||||
|
page.title,
|
||||||
|
page.status
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return distilled
|
||||||
|
|
||||||
|
def test_generate_context(self):
|
||||||
|
settings = _DEFAULT_CONFIG.copy()
|
||||||
|
|
||||||
|
settings['PAGE_DIR'] = 'TestPages'
|
||||||
|
generator = PagesGenerator(settings.copy(), settings, CUR_DIR,
|
||||||
|
_DEFAULT_CONFIG['THEME'], None,
|
||||||
|
_DEFAULT_CONFIG['MARKUP'])
|
||||||
|
generator.generate_context()
|
||||||
|
pages = self.distill_pages(generator.pages)
|
||||||
|
hidden_pages = self.distill_pages(generator.hidden_pages)
|
||||||
|
|
||||||
|
pages_expected = [
|
||||||
|
[u'This is a test page', 'published'],
|
||||||
|
[u'This is a markdown test page', 'published']
|
||||||
|
]
|
||||||
|
hidden_pages_expected = [
|
||||||
|
[u'This is a test hidden page', 'hidden'],
|
||||||
|
[u'This is a markdown test hidden page', 'hidden']
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertItemsEqual(pages_expected,pages)
|
||||||
|
self.assertItemsEqual(hidden_pages_expected,hidden_pages)
|
||||||
|
|
||||||
|
|
||||||
class TestLessCSSGenerator(unittest.TestCase):
|
class TestLessCSSGenerator(unittest.TestCase):
|
||||||
|
|
||||||
LESS_CONTENT = """
|
LESS_CONTENT = """
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import shutil
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
@ -86,3 +87,12 @@ class TestUtils(unittest.TestCase):
|
||||||
changed = utils.files_changed(path, 'rst')
|
changed = utils.files_changed(path, 'rst')
|
||||||
self.assertEquals(changed, True)
|
self.assertEquals(changed, True)
|
||||||
self.assertAlmostEqual(utils.LAST_MTIME, t, delta=1)
|
self.assertAlmostEqual(utils.LAST_MTIME, t, delta=1)
|
||||||
|
|
||||||
|
def test_clean_output_dir(self):
|
||||||
|
test_directory = os.path.join(os.path.dirname(__file__), 'clean_output')
|
||||||
|
content = os.path.join(os.path.dirname(__file__), 'content')
|
||||||
|
shutil.copytree(content, test_directory)
|
||||||
|
utils.clean_output_dir(test_directory)
|
||||||
|
self.assertTrue(os.path.isdir(test_directory))
|
||||||
|
self.assertListEqual([], os.listdir(test_directory))
|
||||||
|
shutil.rmtree(test_directory)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue