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
|
||||
docutils
|
||||
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.
|
||||
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?
|
||||
=========================================================================
|
||||
|
||||
Pygments adds some classes to the generated content. These classes are used by
|
||||
themes to style code syntax highlighting via CSS. Specifically, you can
|
||||
customize the appearance of your syntax highlighting via the `.codehilite pre`
|
||||
customize the appearance of your syntax highlighting via the ``.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/>`_.
|
||||
|
|
@ -30,7 +30,7 @@ How can I help?
|
|||
|
||||
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>`_.
|
||||
<https://github.com/ametaireau/pelican/issues>`_.
|
||||
|
||||
If you want to contribute, please fork `the git repository
|
||||
<https://github.com/ametaireau/pelican/>`_, make your changes, and issue
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
Getting started
|
||||
###############
|
||||
|
||||
Installing
|
||||
==========
|
||||
Installing Pelican
|
||||
==================
|
||||
|
||||
You're ready? Let's go! You can install Pelican via several different methods.
|
||||
The simplest is via `pip <http://www.pip-installer.org/>`_::
|
||||
|
||||
$ 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
|
||||
|
||||
|
|
@ -18,12 +18,13 @@ a virtual environment for Pelican via `virtualenv <http://www.virtualenv.org/>`_
|
|||
and `virtualenvwrapper <http://www.doughellmann.com/projects/virtualenvwrapper/>`_
|
||||
before installing Pelican::
|
||||
|
||||
$ pip install virtualenvwrapper
|
||||
$ sudo pip install --upgrade virtualenv virtualenvwrapper
|
||||
$ mkvirtualenv pelican
|
||||
$ pip install pelican
|
||||
|
||||
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
|
||||
have the project source, you can install Pelican using the distutils
|
||||
be installed via ``pip`` or ``easy_install`` as noted above. Alternatively, if
|
||||
you have the project source, you can install Pelican using the distutils
|
||||
method::
|
||||
|
||||
$ 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
|
||||
|
||||
If you plan on using Markdown as a markup format, you'll need to install the
|
||||
Markdown library as well::
|
||||
|
||||
$ pip install Markdown
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
|
||||
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::
|
||||
|
||||
$ pip install --upgrade pelican
|
||||
|
|
@ -55,13 +61,66 @@ At this time, Pelican is dependent on the following Python packages:
|
|||
* jinja2, for templating support
|
||||
* 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:
|
||||
|
||||
* pygments, for syntax highlighting
|
||||
* 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
|
||||
==============================
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
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
|
||||
##############
|
||||
|
|
@ -83,10 +142,9 @@ following syntax (give your file the `.rst` extension)::
|
|||
:category: yeah
|
||||
:author: Alexis Metaireau
|
||||
|
||||
|
||||
You can also use Markdown syntax (with a file ending in `.md`).
|
||||
Markdown generation will not work until you explicitly install the `Markdown`
|
||||
package, which can be done via `pip install Markdown`. Metadata syntax for
|
||||
You can also use Markdown syntax (with a file ending in ``.md``).
|
||||
Markdown generation will not work until you explicitly install the ``Markdown``
|
||||
package, which can be done via ``pip install Markdown``. Metadata syntax for
|
||||
Markdown posts should follow this pattern::
|
||||
|
||||
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
|
||||
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`.
|
||||
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::
|
||||
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]
|
||||
|
||||
And… that's all! Your weblog will be generated and saved in the `content/`
|
||||
folder.
|
||||
The above command will generate your weblog and save it in the ``content/``
|
||||
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
|
||||
very sexy, as it's just simple HTML output (without any style).
|
||||
|
||||
You can create your own style if you want. Have a look at the help to see all
|
||||
the options you can use::
|
||||
Pelican has other command-line switches available. Have a look at the help to
|
||||
see all the options you can use::
|
||||
|
||||
$ pelican --help
|
||||
|
||||
Kickstart a blog
|
||||
----------------
|
||||
Auto-reload
|
||||
-----------
|
||||
|
||||
You also can use the `pelican-quickstart` script to start a new blog in
|
||||
seconds by just answering a few questions. Just run `pelican-quickstart` and
|
||||
you're done! (Added in Pelican 3.0)
|
||||
It's possible to tell Pelican to watch for your modifications, instead of
|
||||
manually re-running it every time you want to see your changes. To enable this,
|
||||
run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
|
||||
|
||||
Pages
|
||||
-----
|
||||
|
||||
If you create a folder named `pages`, 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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
--------------------------
|
||||
|
||||
|
|
@ -145,8 +208,8 @@ a simple script. See :ref:`import`.
|
|||
Translations
|
||||
------------
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
|
@ -205,13 +268,6 @@ For Markdown, format your code blocks thusly::
|
|||
The specified identifier should be one that appears on the
|
||||
`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
|
||||
-----------------
|
||||
|
||||
|
|
@ -234,5 +290,3 @@ Or run a simple web server using Python::
|
|||
|
||||
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
|
||||
=================
|
||||
|
||||
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
|
||||
files, and the output is a blog, but both input and output can be anything you
|
||||
want.
|
||||
|
||||
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
|
||||
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
|
||||
metadata (author, tags, category, etc.) and content (HTML-formatted).
|
||||
|
||||
* `generators` generate the different outputs. For instance, Pelican comes with
|
||||
`ArticlesGenerator` and `PageGenerator`. Given a configuration, they can do
|
||||
* **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 your own theme. The
|
||||
syntax is `jinja2`, and, trust me, really easy to learn, so don't hesitate
|
||||
to jump in and build your own theme.
|
||||
* Pelican also uses templates, so it's easy to write your own theme. The
|
||||
syntax is `Jinja2 <http://jinja.pocoo.org/>`_ and is very easy to learn, so
|
||||
don't hesitate to jump in and build your own theme.
|
||||
|
||||
How to implement a new reader?
|
||||
==============================
|
||||
|
||||
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.
|
||||
|
||||
Take a look at the Markdown reader::
|
||||
|
|
@ -65,8 +65,8 @@ Take a look at the Markdown reader::
|
|||
Simple, isn't it?
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
|
@ -76,17 +76,17 @@ How to implement a new generator?
|
|||
Generators have two important methods. You're not forced to create
|
||||
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
|
||||
context is shared between all generators, and will be passed to the
|
||||
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.
|
||||
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,
|
||||
* ``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
|
||||
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
|
||||
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
|
||||
#######
|
||||
|
||||
Since version 3.0, pelican manages plugins. Plugins are a way to add features
|
||||
to pelican without having to directly hack pelican code.
|
||||
Since version 3.0, Pelican manages plugins. Plugins are a way to add features
|
||||
to Pelican without having to directly hack Pelican code.
|
||||
|
||||
Pelican is shipped with a set of core plugins, but you can easily implement
|
||||
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
|
||||
ways to do so.
|
||||
|
|
@ -23,21 +23,21 @@ Or by importing them and adding them to the list::
|
|||
from pelican.plugins import 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::
|
||||
|
||||
PLUGIN_PATH = "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
|
||||
section.
|
||||
|
||||
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::
|
||||
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 example::
|
||||
|
||||
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
|
||||
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
|
||||
---------
|
||||
|
|
@ -82,7 +82,7 @@ Github Activity
|
|||
This plugin makes use of the ``feedparser`` library that you'll need to
|
||||
install.
|
||||
|
||||
Set the GITHUB_ACTIVITY_FEED parameter to your github activity feed.
|
||||
Set the ``GITHUB_ACTIVITY_FEED`` parameter to your Github activity feed.
|
||||
For example, my setting would look like::
|
||||
|
||||
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
|
||||
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::
|
||||
|
||||
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
|
||||
up to date.
|
||||
right after writing Pelican, in December 2010. The information may not be
|
||||
up-to-date.
|
||||
|
||||
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.
|
||||
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
|
||||
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,
|
||||
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
|
||||
times, each time took less than 30 minutes.
|
||||
provide fixes on it. So far, I've re-factored the Pelican code by two
|
||||
times; each time took less than 30 minutes.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
language you want, and then generate a static HTML weblog
|
||||
language you want, and then generate a static HTML weblog.
|
||||
|
||||
.. image:: _static/overall.png
|
||||
|
||||
To be flexible enough, pelican have a template support, so you can
|
||||
easily write you own themes if you want to.
|
||||
To be flexible enough, Pelican has template support, so you can easily write
|
||||
your own themes if you want to.
|
||||
|
||||
Design process
|
||||
==============
|
||||
|
|
@ -42,19 +42,18 @@ Design process
|
|||
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.
|
||||
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
|
||||
text files), and started experimenting with the code.
|
||||
Pelican was 200 lines long, and contained almost ten functions and one
|
||||
class when it was first usable.
|
||||
Then, I created the content I wanted to parse (the reStructuredText files)
|
||||
and started experimenting with the code. Pelican was 200 lines long and
|
||||
contained almost ten functions and one class when it was first usable.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
In the same way, I have added the support of different things over
|
||||
time: atom feeds, multiple themes, multiple markup support, etc.
|
||||
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
|
||||
time: Atom feeds, multiple themes, multiple markup support, etc.
|
||||
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
|
||||
multiple different files.
|
||||
|
||||
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
|
||||
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
|
||||
* *generators* generate the different outputs. For instance, Pelican
|
||||
comes with an ArticlesGenerator and PagesGenerator, into
|
||||
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).
|
||||
|
||||
I also deal with contents objects. They can be `Articles`, `Pages`, `Quotes`,
|
||||
or whatever you want. They are defined in the contents.py module,
|
||||
and represent some content to be used by the program.
|
||||
I also deal with contents objects. They can be ``Articles``, ``Pages``,
|
||||
``Quotes``, or whatever you want. They are defined in the ``contents.py``
|
||||
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
|
||||
|
||||
The interface do not really exists, and I have added it only to clarify the
|
||||
whole picture. I do use duck typing, and not interfaces.
|
||||
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.
|
||||
|
||||
Internally, the following process is followed:
|
||||
|
||||
* 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.
|
||||
* The `generate_context` method of each generator is called, updating
|
||||
* The ``generate_context`` method of each generator is called, updating
|
||||
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.
|
||||
|
||||
I make two calls because it is important that when the output is
|
||||
generated by the generators, the context will not change. In other
|
||||
words, the first method `generate_context` should modify the context,
|
||||
whereas the second `generate_output` method should not.
|
||||
words, the first method ``generate_context`` should modify the context,
|
||||
whereas the second ``generate_output`` method should not.
|
||||
|
||||
Then, it is up to the generators to do what the want, in the
|
||||
`generate_context` and `generate_content` method.
|
||||
Taking the `ArticlesGenerator` class will help to understand some others
|
||||
concepts. Here is what happens when calling the `generate_context`
|
||||
``generate_context`` and ``generate_content`` method.
|
||||
Taking the ``ArticlesGenerator`` class will help to understand some others
|
||||
concepts. Here is what happens when calling the ``generate_context``
|
||||
method:
|
||||
|
||||
* Read the folder “path”, looking for restructured text files, load
|
||||
each of them, and construct a content object (`Article`) with it. To do so,
|
||||
use `Reader` objects.
|
||||
* Update the `context` with all those articles.
|
||||
each of them, and construct a content object (``Article``) with it. To do so,
|
||||
use ``Reader`` objects.
|
||||
* Update the ``context`` with all those articles.
|
||||
|
||||
Then, the `generate_content` method uses the `context` and the `writer` to
|
||||
generate the wanted output
|
||||
Then, the ``generate_content`` method uses the ``context`` and the ``writer`` to
|
||||
generate the wanted output.
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ Setting name (default value) What doe
|
|||
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 as well as
|
||||
the generated files.
|
||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
|
||||
generating new 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
|
||||
|
|
@ -59,18 +59,16 @@ Setting name (default value) What doe
|
|||
`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 whether Pelican should use relative URLs or
|
||||
not.
|
||||
`RELATIVE_URLS` (``True``) Defines whether Pelican should use document-relative URLs or
|
||||
not. If set to ``False``, Pelican will use the SITEURL
|
||||
setting to construct absolute URLs.
|
||||
`PLUGINS` (``[]``) The list of plugins to load. See :ref:`plugins`.
|
||||
`SITENAME` (``'A Pelican Blog'``) Your site name
|
||||
`SITEURL` Base URL of your website. Not defined by default,
|
||||
which means the base URL is assumed to be "/" with a
|
||||
root-relative URL structure. If `SITEURL` is specified
|
||||
explicitly, there should be no trailing slash at the end,
|
||||
and URLs will be generated with an absolute URL structure
|
||||
(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.
|
||||
so it is best to specify your SITEURL; if you do not, feeds
|
||||
will not be generated with properly-formed URLs. You should
|
||||
include ``http://`` and your domain, with no trailing
|
||||
slash at the end. Example: ``SITEURL = 'http://mydomain.com'``
|
||||
`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
|
||||
|
|
@ -107,6 +105,15 @@ Setting name (default value) What doe
|
|||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
How to create themes for Pelican
|
||||
################################
|
||||
|
||||
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
|
||||
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>`_
|
||||
Pelican uses the great `Jinja2 <http://jinja.pocoo.org/>`_ templating engine 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
|
||||
<https://github.com/ametaireau/pelican/tree/master/pelican/themes/simple/templates>`_.
|
||||
|
||||
Structure
|
||||
=========
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ file generator, we can take advantage of this.
|
|||
|
||||
User Pages
|
||||
----------
|
||||
Github allows you to create user pages in the form of ``username.github.com``.
|
||||
Whatever is created in master branch will be published. For this purposes just
|
||||
the output generated by pelican needs to pushed at github.
|
||||
GitHub allows you to create user pages in the form of ``username.github.com``.
|
||||
Whatever is created in the master branch will be published. For this purpose,
|
||||
just the output generated by Pelican needs to pushed to GitHub.
|
||||
|
||||
So given a repository containing your articles, just run pelican over the posts
|
||||
and deploy the master branch at github::
|
||||
So given a repository containing your articles, just run Pelican over the posts
|
||||
and deploy the master branch to GitHub::
|
||||
|
||||
$ pelican -s pelican.conf.py ./path/to/posts -o /path/to/output
|
||||
|
||||
Now add all the files in the output directory generated by pelican::
|
||||
Now add all the files in the output directory generated by Pelican::
|
||||
|
||||
$ git add /path/to/output/*
|
||||
$ git commit -am "Your Message"
|
||||
|
|
@ -31,12 +31,12 @@ Project Pages
|
|||
-------------
|
||||
For creating Project pages, a branch called ``gh-pages`` is used for publishing.
|
||||
The excellent `ghp-import <https://github.com/davisp/ghp-import>`_ makes this
|
||||
really easy. You will have to install it::
|
||||
really easy, which can be installed via::
|
||||
|
||||
$ pip install ghp-import
|
||||
|
||||
Then, given 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 run
|
||||
Pelican and upload the output to GitHub::
|
||||
|
||||
$ pelican -s pelican.conf.py .
|
||||
$ ghp-import output
|
||||
|
|
@ -45,10 +45,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!
|
||||
commit, your blog is up-to-date on GitHub!
|
||||
|
||||
Put the following into `.git/hooks/post-commit`::
|
||||
|
||||
pelican -s pelican.conf.py . && ghp-import output && git push origin
|
||||
gh-pages
|
||||
Put the following into ``.git/hooks/post-commit``::
|
||||
|
||||
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:
|
||||
# if it's a string, then import it
|
||||
if isinstance(plugin, basestring):
|
||||
log.debug("Loading plugin `{0}' ...".format(plugin))
|
||||
logger.debug("Loading plugin `{0}' ...".format(plugin))
|
||||
plugin = __import__(plugin, globals(), locals(), 'module')
|
||||
|
||||
log.debug("Registering plugin `{0}' ...".format(plugin.__name__))
|
||||
logger.debug("Registering plugin `{0}' ...".format(plugin.__name__))
|
||||
plugin.register()
|
||||
|
||||
def _handle_deprecation(self):
|
||||
|
|
|
|||
|
|
@ -357,10 +357,13 @@ class PagesGenerator(Generator):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.pages = []
|
||||
self.hidden_pages = []
|
||||
self.hidden_translations = []
|
||||
super(PagesGenerator, self).__init__(*args, **kwargs)
|
||||
|
||||
def generate_context(self):
|
||||
all_pages = []
|
||||
hidden_pages = []
|
||||
for f in self.get_files(
|
||||
os.path.join(self.path, self.settings['PAGE_DIR']),
|
||||
exclude=self.settings['PAGE_EXCLUDES']):
|
||||
|
|
@ -373,15 +376,25 @@ class PagesGenerator(Generator):
|
|||
filename=f)
|
||||
if not is_valid_content(page, f):
|
||||
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.hidden_pages, self.hidden_translations = process_translations(hidden_pages)
|
||||
|
||||
self._update_context(('pages', ))
|
||||
self.context['PAGES'] = self.pages
|
||||
|
||||
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'),
|
||||
self.context, page=page,
|
||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ CONF = {
|
|||
'ftp_host': 'localhost',
|
||||
'ftp_user': 'anonymous',
|
||||
'ftp_target_dir': '/',
|
||||
'ssh_host': 'locahost',
|
||||
'ssh_host': 'localhost',
|
||||
'ssh_port': 22,
|
||||
'ssh_user': 'root',
|
||||
'ssh_target_dir': '/var/www',
|
||||
'dropbox_dir' : '~/Dropbox/Public/',
|
||||
'default_pagination' : 10,
|
||||
'siteurl': '',
|
||||
'lang': 'en'
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ def ask(question, answer=str, default=None, l=None):
|
|||
r = default
|
||||
break
|
||||
else:
|
||||
print("You must answer `yes' or `no'")
|
||||
print("You must answer 'yes' or 'no'")
|
||||
return r
|
||||
elif answer == int:
|
||||
r = None
|
||||
|
|
@ -111,12 +113,12 @@ def ask(question, answer=str, default=None, l=None):
|
|||
print('You must enter an integer')
|
||||
return r
|
||||
else:
|
||||
raise NotImplemented('Arguent `answer` must be str, bool or integer')
|
||||
raise NotImplemented('Argument `answer` must be str, bool, or integer')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="A kickstarter for pelican",
|
||||
description="A kickstarter for Pelican",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument('-p', '--path', default=".",
|
||||
help="The path to generate the blog into")
|
||||
|
|
@ -125,7 +127,7 @@ def main():
|
|||
parser.add_argument('-a', '--author', metavar="author",
|
||||
help='Set the author name of the website')
|
||||
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()
|
||||
|
||||
|
|
@ -137,36 +139,44 @@ Please answer the following questions so this script can generate the files need
|
|||
|
||||
'''.format(v=__version__))
|
||||
|
||||
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new Web site ?', answer=str, default=args.path))
|
||||
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)
|
||||
project = os.path.join(os.environ['VIRTUAL_ENV'], '.project')
|
||||
if os.path.isfile(project):
|
||||
CONF['basedir'] = open(project, 'r').read().rstrip("\n")
|
||||
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']:
|
||||
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:
|
||||
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 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_user'] = ask('What is your username on this 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'])
|
||||
|
||||
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_user'] = ask('What is your username on this 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'])
|
||||
|
||||
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'])
|
||||
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_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 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):
|
||||
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 that server?', str, CONF['ssh_user'])
|
||||
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):
|
||||
CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', str, CONF['dropbox_dir'])
|
||||
|
||||
try:
|
||||
os.makedirs(os.path.join(CONF['basedir'], 'src'))
|
||||
os.makedirs(os.path.join(CONF['basedir'], 'content'))
|
||||
except OSError, 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))
|
||||
|
||||
try:
|
||||
with open(os.path.join(CONF['basedir'], 'pelican.conf.py'), 'w') as fd:
|
||||
for line in get_template('pelican.conf.py'):
|
||||
with open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w') as fd:
|
||||
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)
|
||||
fd.write(template.safe_substitute(CONF))
|
||||
fd.close()
|
||||
|
|
|
|||
|
|
@ -48,9 +48,11 @@ def main():
|
|||
|
||||
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
|
|
@ -62,6 +64,9 @@ def main():
|
|||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
to_install = args.to_install or args.to_upgrade
|
||||
to_sym = args.to_symlink or args.clean
|
||||
|
||||
|
||||
if args.action:
|
||||
|
|
@ -69,8 +74,7 @@ def main():
|
|||
list_themes(args.verbose)
|
||||
elif args.action is '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.verbose:
|
||||
print('Removing themes...')
|
||||
|
|
@ -85,6 +89,13 @@ def main():
|
|||
for i in args.to_install:
|
||||
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.verbose:
|
||||
print('Linking themes...')
|
||||
|
|
@ -149,17 +160,21 @@ def remove(theme_name, v=False):
|
|||
err(target + ' : no such file or directory')
|
||||
|
||||
|
||||
def install(path, v=False):
|
||||
def install(path, v=False, u=False):
|
||||
"""Installs a theme"""
|
||||
if not os.path.exists(path):
|
||||
err(path + ' : no such file or directory')
|
||||
elif not os.path.isdir(path):
|
||||
err(path + ' : no a directory')
|
||||
err(path + ' : not a directory')
|
||||
else:
|
||||
theme_name = os.path.basename(os.path.normpath(path))
|
||||
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')
|
||||
elif exists and u:
|
||||
remove(theme_name, v)
|
||||
install(path, v)
|
||||
else:
|
||||
if v:
|
||||
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):
|
||||
err(path + ' : no such file or directory')
|
||||
elif not os.path.isdir(path):
|
||||
err(path + ' : no a directory')
|
||||
err(path + ' : not a directory')
|
||||
else:
|
||||
theme_name = os.path.basename(os.path.normpath(path))
|
||||
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@ PELICAN=$pelican
|
|||
PELICANOPTS=$pelicanopts
|
||||
|
||||
BASEDIR=$$(PWD)
|
||||
INPUTDIR=$$(BASEDIR)/src
|
||||
INPUTDIR=$$(BASEDIR)/content
|
||||
OUTPUTDIR=$$(BASEDIR)/output
|
||||
CONFFILE=$$(BASEDIR)/pelican.conf.py
|
||||
CONFFILE=$$(BASEDIR)/pelicanconf.py
|
||||
PUBLISHCONF=$$(BASEDIR)/publishconf.py
|
||||
|
||||
FTP_HOST=$ftp_host
|
||||
FTP_USER=$ftp_user
|
||||
FTP_TARGET_DIR=$ftp_target_dir
|
||||
|
||||
SSH_HOST=$ssh_host
|
||||
SSH_PORT=$ssh_port
|
||||
SSH_USER=$ssh_user
|
||||
SSH_TARGET_DIR=$ssh_target_dir
|
||||
|
||||
|
|
@ -22,10 +24,11 @@ help:
|
|||
@echo 'Usage: '
|
||||
@echo ' make html (re)generate the web site '
|
||||
@echo ' make clean remove the generated files '
|
||||
@echo ' ftp_upload upload the web site using FTP '
|
||||
@echo ' ssh_upload upload the web site using SSH '
|
||||
@echo ' dropbox_upload upload the web site using Dropbox '
|
||||
@echo ' rsync_upload upload the web site using rsync/ssh'
|
||||
@echo ' make publish generate using production settings '
|
||||
@echo ' ftp_upload upload the web site via FTP '
|
||||
@echo ' ssh_upload upload the web site via SSH '
|
||||
@echo ' dropbox_upload upload the web site via Dropbox '
|
||||
@echo ' rsync_upload upload the web site via rsync/ssh '
|
||||
@echo ' '
|
||||
|
||||
|
||||
|
|
@ -36,23 +39,31 @@ $$(OUTPUTDIR)/%.html:
|
|||
$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||
|
||||
clean:
|
||||
rm -fr $$(OUTPUTDIR)
|
||||
mkdir $$(OUTPUTDIR)
|
||||
find $$(OUTPUTDIR) -mindepth 1 -delete
|
||||
|
||||
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)
|
||||
|
||||
ssh_upload: $$(OUTPUTDIR)/index.html
|
||||
scp -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||
ssh_upload: publish
|
||||
scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||
|
||||
rsync_upload: $$(OUTPUTDIR)/index.html
|
||||
rsync -e ssh -P -rvz --delete $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
||||
rsync_upload: publish
|
||||
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"
|
||||
|
||||
github: $$(OUTPUTDIR)/index.html
|
||||
github: publish
|
||||
ghp-import $$(OUTPUTDIR)
|
||||
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 existing content from the output folder
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except Exception:
|
||||
pass
|
||||
for filename in os.listdir(path):
|
||||
file = os.path.join(path, filename)
|
||||
if os.path.isdir(file):
|
||||
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):
|
||||
|
|
|
|||
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
|
||||
from setuptools import setup
|
||||
|
||||
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz', 'blinker']
|
||||
requires = ['feedgenerator', 'jinja2 >= 2.4', 'pygments', 'docutils', 'pytz', 'blinker']
|
||||
|
||||
try:
|
||||
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 re
|
||||
|
||||
from pelican.generators import ArticlesGenerator, LessCSSGenerator
|
||||
from pelican.generators import ArticlesGenerator, LessCSSGenerator, PagesGenerator
|
||||
from pelican.settings import _DEFAULT_CONFIG
|
||||
from .support import unittest, temporary_folder, skipIfNoExecutable
|
||||
|
||||
|
|
@ -94,6 +94,48 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
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):
|
||||
|
||||
LESS_CONTENT = """
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import shutil
|
||||
import os
|
||||
import datetime
|
||||
import time
|
||||
|
|
@ -86,3 +87,12 @@ class TestUtils(unittest.TestCase):
|
|||
changed = utils.files_changed(path, 'rst')
|
||||
self.assertEquals(changed, True)
|
||||
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