Merge branch 'master' of git://github.com/ametaireau/pelican

Conflicts:
	docs/settings.rst
	pelican/generators.py
	pelican/settings.py
This commit is contained in:
Alexander Artemenko 2011-03-23 10:25:38 +03:00
commit 138e19fa19
83 changed files with 1664 additions and 1701 deletions

5
.gitignore vendored
View file

@ -2,3 +2,8 @@
.*.swp
.*.swo
*.pyc
docs/_build
docs/fr/_build
build
dist
output

View file

@ -32,8 +32,7 @@ Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
Source code
-----------
You can access the source code via mercurial at http://hg.notmyidea.org/pelican/
or via git on http://github.com/ametaireau/pelican/
You can access the source code via git on http://github.com/ametaireau/pelican/
If you feel hackish, have a look to the `pelican's internals explanations
<http://alexis.notmyidea.org/pelican/internals.html>`_.

4
THANKS
View file

@ -10,3 +10,7 @@ bugs or giving ideas. Thanks to them !
- David Kulak
- Arnaud Bos
- nblock (Florian)
- Bruno Bord
- Laureline Guérin
- Samuel Martin
- Marcus Fredriksson

View file

@ -8,12 +8,15 @@ import sys, os
# -- General configuration -----------------------------------------------------
templates_path = ['_templates']
extensions = ['sphinx.ext.autodoc',]
source_suffix = '.rst'
master_doc = 'index'
project = u'Pelican'
copyright = u'2010, Alexis Metaireau'
copyright = u'2010, Alexis Metaireau and contributors'
exclude_patterns = ['_build']
pygments_style = 'sphinx'
version = "2"
release = version
# -- Options for HTML output ---------------------------------------------------

38
docs/faq.rst Normal file
View file

@ -0,0 +1,38 @@
Frequently Asked Questions (FAQ)
################################
Here is a summary of the frequently asked questions for pelican.
Is it mandatory to have a configuration file ?
==============================================
No, it's not. Configurations files are just an easy way to configure pelican.
For the basic operations, it's possible to specify options while invoking
pelican with the command line (see `pelican --help` for more informations about
that)
I'm creating my own theme, how to use pygments ?
================================================
Pygment add some classes to the generated content, so the theming of your theme
will be done thanks to a css file. You can have a look to the one proposed by
default `on the project website <http://pygments.org/demo/15101/>`_
How do I create my own theme ?
==============================
Please refer yourself to :ref:`theming-pelican`.
How can I help ?
================
You have different options to help. First, you can use pelican, and report any
idea or problem you have on `the bugtracker
<http://github.com/ametaireau/pelican/issues>`_.
If you want to contribute, please have a look to `the git repository
<https://github.com/ametaireau/pelican/>`_, fork it, add your changes and do
a pull request, I'll review them as soon as possible.
You can also contribute by creating themes, and making the documentation
better.

20
docs/fr/astuces.rst Normal file
View file

@ -0,0 +1,20 @@
Trucs et astuces pour Pelican
#############################
Personnaliser l'url d'un article pour Pelican
=============================================
Par défaut, quand vous créez un article ayant pour titre *Mon article pour Pelican*,
l'url par défaut devient *mon-article-pour-pelican.html*. Cependant, il est possible
de modifier cela en utilisant la technique utilisée pour les traductions d'article,
c'est à dire le paramètre *:slug:* ::
Mon article pour Pelican
########################
:date: 2011-01-31 11:05
:slug: super-article-pour-pelican
bla, bla, bla …
En prenant cet exemple ci dessus, votre url deviendra *super-article-pour-pelican.html*

58
docs/fr/bases.rst Normal file
View file

@ -0,0 +1,58 @@
Les bases de Pelican
####################
Créer son premier article
=========================
Pour créer notre premier article, nous allons éditer un fichier, par exemple premier_article.rst ::
Premier article pour Pelican
############################
:author: Guillaume
:date: 2011-01-08 10:20
:category: GNU-Linux
:tags: tutoriel, git
Ceci est un tutoriel pour configurer git.
Bla, bla, bla ....
Maintenant que ce fichier est créé, on va lancer la création du blog ::
pelican .
Vous aller obtenir une sortie comme celle ci — $PATH représente le dossier où vous
avez créé votre article ::
[ok] writing $PATH/output/feeds/all.atom.xml
[ok] writing $PATH/output/feeds/GNU/Linux.atom.xml
[ok] writing $PATH/output/feeds/all-en.atom.xml
[ok] writing $PATH/output/premier-article-pour-pelican.html
[ok] writing $PATH/output/index.html
[ok] writing $PATH/output/tags.html
[ok] writing $PATH/output/categories.html
[ok] writing $PATH/output/archives.html
[ok] writing $PATH/output/tag/tutoriel.html
[ok] writing $PATH/output/tag/git.html
[ok] writing $PATH/output/category/GNU-Linux.html
Première analyse
================
Nous allons décortiquer un peu tout ça ensemble.
* Un dossier output/ a été créé pour y mettre le fichiers xml et html du blog.
* Dans le dossier feeds/, nous retrouvons les différents flux de syndication.
* Le fichier de larticle et la page principale du blog a été généré.
* Le répertoire tag/ propose une page par tag.
* La page correspondant à la catégorie est générée dans le répertoire category/
Si vous ouvrez le fichier index.html — ou un autre — avec votre navigateur, vous
remarquerez que :
* Le thème utilisé par défaut est notmyidea
* Le nom du blog est A Pelican Blog.
Bien évidemment, il y a des paramètres de base que lon peut modifier pour mettre
un peu tout ça à sa sauce. Cest ce que nous allons voir au travers du fichier de configuration.

154
docs/fr/configuration.rst Normal file
View file

@ -0,0 +1,154 @@
Fichier de configuration
************************
On va créer un fichier de configuration que lon va appeler **settings.py**. On peut
utiliser Pelican sans faire ce fichier, mais il faudrait à chaque fois passer les paramètres
en ligne de commande. Et comme il va nous servir à faire dautres choses bien utile,
autant lappréhender de suite. Cependant, nous nallons voir que la base pour linstant.
Paramètres de base
==================
AUTHOR :
Désigne lauteur par défaut ;
DEFAULT_CATEGORY :
La catégorie par défaut des articles. Si ce paramètre nest
pas documenté, il prendra la valeur misc — pour miscellaneous (divers en français) ;
SITENAME :
Le nom de votre site ;
OUTPUT_PATH :
Le répertoire de sortie du blog.
Quand je dis quon va faire simple, on fait simple !
Passons donc à ce quoi doit ressembler le fichier de configuration ::
# -*- coding: utf-8 -*-
AUTHOR = "Guillaume"
DEFAULT_CATEGORY = "GNU-Linux"
SITENAME = "Free Culture"
Si vous avez un serveur comme Apache de configuré pour votre machine, vous
pouvez paramétrer le répertoire de sortie vers **/var/www/blog** par exemple ::
OUTPUT_PATH = "/var/www/blog"
Une remarque importante. Si vous avez besoin de passer un caractère accentué, il
faut le préciser que la chaine est en unicode en faisant par exemple
*AUTHOR = u"Guillaume LAMÉ"*
Pour bien vérifier que les paramètres sont bien pris en compte, nous allons enlever les lignes *:author: Guillaume* et *:category: GNU-Linux* de notre fichier
**premier_article.rst** et regénérer le blog.
Rafraichissez votre page, ce devrait être bon.
Nous allons maintenant passer en revue les différents paramètres de Pelican. Je les
ai regroupé par thème. Cependant, cest surtout un listing avant de rentrer dans les
détails au prochain chapitre.
Flux de syndication
===================
CATEGORY_FEED :
Chemin décriture des flux Atom liés aux catégories ;
CATEGORY_FEED_RSS :
Idem pour les flux rss (Optionnel);
FEED :
Chemin du flux Atom global ;
FEED_RSS :
Chemin du flux Rss global (Optionnel);
TAG_FEED :
Chemin des flux Atom pour les tags (Optionnel);
TAG_FEED_RSS :
Chemin des flux Rss pour les tags (Optionnel).
Traductions
===========
DEFAULT_LANG :
Le langage par défaut à utiliser. «*en*» par défaut ;
TRANSLATION_FEED :
Chemin du flux pour les traductions.
Thèmes
======
CSS_FILE :
Fichier css à utiliser si celui-ci est différent du fichier par défaut (*main.css*) ;
DISPLAY_PAGES_ON_MENU :
Affiche ou non les pages statiques sur le menu du thème ;
DISQUS_SITENAME :
Indiquer le nom du site spécifié sur Disqus ;
GITHUB_URL :
Indiquez votre url Github ;
GOOGLE_ANALYTICS :
'UA-XXXX-YYYY' pour activer Google analytics ;
JINJA_EXTENSIONS :
Liste d'extension Jinja2 que vous souhaitez utiliser ;
LINKS :
Une liste de tuples (Titre, url) pour afficher la liste de lien ;
PDF_PROCESSOR :
Génère ou non les articles et pages au format pdf ;
REVERSE_ARCHIVE_ORDER :
Met les articles plus récent en tête de l'archive ;
SOCIAL :
Une liste de tuples (Titre, url) pour afficher la liste de lien dans la section "Social" ;
STATIC_THEME_PATHS :
Répertoire du thème que vous souhaitez importer dans l'arborescence finale ;
THEME :
Thème à utiliser:
TWITTER_USERNAME :
Permet d'afficher un bouton permettant le tweet des articles.
Paramètres divers
=================
FALLBACK_ON_FS_DATE :
Si *True*, Pelican se basera sur le *mtime* du fichier s'il n'y a pas de date spécifiée dans le fichier de l'article ;
KEEP_OUTPUT DIRECTORY :
Ne génère que les fichiers modifiés et n'efface pas le repertoire de sortie ;
MARKUP :
Langage de balisage à utiliser ;
PATH :
Répertoire à suivre pour les fichiers inclus ;
SITEURL :
URL de base de votre site ;
STATIC_PATHS :
Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ;

18
docs/fr/conventions.rst Normal file
View file

@ -0,0 +1,18 @@
Conventions
###########
Environnement de test
=====================
Les exemples sont basées sur une distribution Debian. Pour les autres distributions,
il y aura des ajustements à faire, notamment pour linstallation de Pelican. Les
noms des paquets peuvent changer.
Conventions typographiques
==========================
Un petit rappel concernant les codes sources.
* $ correspond à une ligne à exécuter en tant quutilisateur courant du systême ;
* # correspond à une ligne à exécuter en tant que root ;
* **settings.py** : Les noms des répertoires et fichiers sont en gras.

35
docs/fr/faq.rst Normal file
View file

@ -0,0 +1,35 @@
*Foire aux questions (FAQ)*
Voici un résumé des questions fréquemment posées pour pelican.
*Est-il obligatoire d'avoir un fichier de configuration ?*
Non. Les fichiers de configuration sont juste un moyen facile de configurer
pelican. Pour les opérations de base, il est possible de spécifier des
options
en invoquant pelican avec la ligne de commande (voir pelican --help pour
plus
d'informations à ce sujet)
*Je crée mon propre thème, comment utiliser pygments?*
Pygment ajoute quelques classes au contenu généré, de sorte qua colorisation
de votre thème se fait grâce à un fichier css. Vous pouvez jeter un oeil à
celui proposé par`sur le site du projet <http://pygments.org/demo/15101/>`_
*Comment puis-je créer mon propre thèm*
Vueillez vous référer à :ref:`theming-pelican-fr`.
*Comment puis-je aider?*
Vous avez plusieurs options pour aider. Tout d'abord, vous pouvez utiliser
le
pélican, et signaler toute idée ou problème que vous avez sur le bugtracker
.
Si vous voulez contribuer, jeter un oeil au dépôt git , ajoutez vos
modifications et faites une demande, je les regarderai dès que possible
Vous pouvez aussi contribuer en créant des thèmes, et/ou compléter la
documentation.

55
docs/fr/index.rst Normal file
View file

@ -0,0 +1,55 @@
Pelican
#######
Pelican est un generateur de blog simple codé en python
* Écrivez vos articles directement dans votre éditeur favori (vim !) et
directement en syntaxe reStructuredText ou Markdown ;
* Un outil simple en ligne de conmmande pour (re)générer le blog ;
* Sortie complètement statique, facile pour l'héberger n'importe où ;
Fonctionnalités
===============
Pelican supporte actuellement :
* des articles de blog ;
* des pages statiques ;
* les commentaires via un service externe (`disqus <http://disqus.com>`_)
Notez qu'étant bien un service externe assez pratique, vous ne gérez pas
vous même les commentaires. Ce qui pourrait occasionner une perte de vos données;
* support de template (les templates sont crées avec `jinja2 <http://jinjna.pocoo.org>`_) ;
* génération optionnelle de vos pages et articles en pdf.
Pourquoi le nom "Pelican" ?
============================
Vous n'avez pas remarqué ? "Pelican" est un anagramme pour "Calepin" ;)
Code source
===========
Vous pouvez accéder au code source via git à l'adresse
http://github.com/ametaireau/pelican/
Feedback !
==========
Si vous voulez de nouvelles fonctionnalitées pour Pelican, n'hésitez pas à nous le dire,
à cloner le dépôt, etc … C'est open source !!!
Contactez Alexis à "alexis at notmyidea dot org" pour quelques requêtes ou retour d'expérience que ce soi !
Documentation
=============
.. toctree::
:maxdepth: 2
conventions
installation
bases
configuration
parametres_article
astuces
faq

67
docs/fr/installation.rst Normal file
View file

@ -0,0 +1,67 @@
Installation et mise à jour de Pelican
######################################
Installation
============
Il y a deux façons dinstaller Pelican sur son système. La première est via lutilitaire
pip, lautre façon est de télécharger Pelican via Github. Ici nous allons voir les deux
façons de procéder.
Via pip
-------
Pour installer Pelican via pip, vous aurez besoin du paquet python-pip. puis installez Pelican ::
# apt-get install python-pip
# pip install pelican
Via Github
----------
Pour installer Pelican en reprenant le code via Github, nous aurons besoin du paquet
git-core pour récupérez les sources de Pelican. Puis nous procédons à linstallation ::
# apt-get install git-core
$ git clone https://github.com/ametaireau/pelican.git
$ cd pelican
# python setup.py install
Mises à jour
============
Via pip
-------
Rien de bien compliqué pour mettre à jour via pip ::
$ cd votreRepertoireSource
$ pip install --upgrade pelican
Via Github
----------
C'est un peu plus long avec Github par contre ::
$ cd votreRepertoireSource
$ git pull origin master
$ cd pelican
# python setup.py install
Vous aurez un message derreur si le module setuptools de python nest pas installé.
La manipulation est la suivante ::
# apt-get install python-setuptools
Alors, quelle méthode choisir ?
===============================
Vous avez le choix entre deux méthodes, mais aussi entre deux concepts. La méthode
de Github est la version de développement, où les modifications arrivent assez
fréquemment sans être testées à fond. La version de pip est une version arrêtée avec un
numéro de version dans laquelle vous aurez moins de bug. Noubliez cependant pas
que le projet est très jeune et manque donc de maturité. Si vous aimez avoir les toutes
dernières versions utilisez Github, sinon penchez vous sur pip.

View file

@ -0,0 +1,106 @@
Les paramètres des articles dans Pelican
########################################
Les catégories
==============
Nous avons vu que pour affecter un article à une catégorie, nous avions le paramètre *:category:*.
Il y a cependant plus simple, affecter un répertoire à une catégorie.
Dans le répertoire ou vous avez vos articles, créez le repertoire **GNU-Linux** et déplacez y le fichier
**premier_article.rst**. Bien évidemment nous ne verront pas la différence, car jusqu'ici *GNU-Linux*
est notre catégorie par défaut.
Nous allons faire un autre exemple d'article avec la catégorie Pelican. Créez le répertoire **Pelican**
et collez cette exemple d'article ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation
Il y a quand même pas mal de boulot pour faire une documentation !
Et lancez la compilation du blog. Vous voyez que la catégorie est affectée automatiquement.
Les tags
========
Pour les tags, il n'y a rien de compliqué. il suffit de mettre le(s) tags séparés si besoin d'une virgule. ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation, pelican
Par contre, par soucis de clarté au niveau des url je vous conseille de mettre les expression de plusieurs
mots séparées par des tirets ::
:tags: mise-a-jour
et non ::
:tags: mise a jour
Les auteurs
===========
Par défaut, vous pouvez indiqué votre nom en tant qu'auteur dans le fichier de configuration.
S'il y a plusieurs auteurs pour le site, vous pouvez le définir manuellement dans
l'article avec la méta-donnée ::
:author: Guillaume
La date
=======
La date se met au format anglophone : **YYYY-MM-DD hh:mm** ::
:date: 2011-01-31 14:12
Les traductions
===============
Pelican permet de générer un blog multilingue assez facilement. Pour cela nous devons :
* Définir la langue de base du blog ;
* Donner une référence à l'article initial ;
* Définir la langue du fichier traduit et y reporter la référence.
Pour définir la langue de base nous allons modifier le fichier **settings.py** et y rajouter la ligne suivante ::
DEFAULT_LANG = "fr"
Puis ajouter la référence dans notre article d'origine qui deviendra ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation
:slug: preparation-de-la-documentation
Il y a quand même pas mal de boulot pour faire une documentation !
Nous n'avons plus qu'à créer l'article en anglais ::
Start of documentation
######################
:slug: preparation-de-la-documention
:lang: en
There are still a lot of work to documentation !
**Il est important de comprendre que la valeur de :slug: deviendra votre url. Ne mettez donc pas un diminutif pour
identifier l'article**
Rien de plus à savoir pour traduire efficacement des articles.
Maintenant que vous avez toutes les clés en main pour créer un article, nous allons passer à la personnalisation
du fichier de configuration.

View file

@ -17,22 +17,26 @@ install. I recommend to do so in a virtualenv::
$ python setup.py install
Dependencies
============
------------
At this time, pelican is dependent of the following python packages:
* feedgenerator, to generate the ATOM feeds.
* jinja2, for templating support.
* pygments, to have syntactic colorization
* docutils and Markdown
If you're not using python 2.7, you will also need `argparse`.
All those dependencies will be processed automatically if you install pelican
using setuptools/distribute or pip.
Optionally:
* docutils, for reST support
* pygments, to have syntactic colorization with resT input
* Markdown, for Markdown as an input format
Writing articles using pelican
==============================
Files metadata
==============
--------------
Pelican tries to be smart enough to get the informations he needs from the
file system (for instance, about the category of your articles), but you need to
@ -63,11 +67,11 @@ directory where the rst file is. For instance, the category of
`python/foobar/myfoobar.rst` is `foobar`.
Generate your blog
==================
------------------
To launch pelican, just use the `pelican` command::
$ pelican /path/to/your/content/
$ pelican /path/to/your/content/ [-s path/to/your/settings.py]
And… that's all! You can see your weblog generated on the `content/` folder.
@ -80,7 +84,7 @@ the options you can use::
$ pelican --help
Pages
=====
-----
If you create a folder named `pages`, all the files in it will be used to
generate static pages.
@ -89,9 +93,60 @@ Then, use the `DISPLAY_PAGES_ON_MENU` setting, which will add all the pages to
the menu.
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
in your articles/pages, and to set a `DEFAULT_LANG` setting (which is en by
default). Then, only articles with this default language will be listed, and
default).
Then, only articles with this default language will be listed, and
each article will have a translation list.
Pelican uses the "slug" of two articles to compare if they are translations of
each others. So it's possible to define (in restructured text) the slug
directly.
Here is an exemple of two articles (one in english and the other one in
french).
The english one::
Foobar is not dead
##################
:slug: foobar-is-not-dead
:lang: en
That's true, foobar is still alive !
And the french one::
Foobar n'est pas mort !
#######################
:slug: foobar-is-not-dead
:lang: fr
Oui oui, foobar est toujours vivant !
Despite the text quality, you can see that only the slug is the same here.
You're not forced to define the slug that way, and it's completely possible to
have two translations with the same title (which defines the slug)
Syntactic recognition
---------------------
Pelican is able to regognise the syntax you are using, and to colorize the
right way your block codes. To do so, you have to use the following syntax::
.. code-block:: identifier
your code goes here
The identifier is one of the lexers available `here
<http://pygments.org/docs/lexers/>`_.
Autoreload
----------
It's possible to tell pelican to watch for your modifications, instead of
manually launching it each time you need. Use the `-r` option, or
`--autoreload`.

View file

@ -29,8 +29,7 @@ Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
Source code
===========
You can access the source code via mercurial at http://hg.notmyidea.org/pelican/
or via git on http://github.com/ametaireau/pelican/
You can access the source code via git on http://github.com/ametaireau/pelican/
Feedback !
==========
@ -43,6 +42,8 @@ Contact me at "alexis at notmyidea dot org" for any request/feedback !
Documentation
=============
A french version of the documentation is available at :doc:`fr/index`.
.. toctree::
:maxdepth: 2
@ -50,3 +51,4 @@ Documentation
settings
themes
internals
faq

View file

@ -40,7 +40,8 @@ method, that is returning an HTML content and some metadata.
Take a look to the Markdown reader::
class MarkdownReader(object):
class MarkdownReader(Reader):
enabled = bool(Markdown)
def read(self, filename):
"""Parse content and metadata of markdown files"""
@ -59,6 +60,12 @@ Take a look to the Markdown reader::
Simple isn't it ?
If your new reader requires additional Python dependencies then you should wrap
their `imports` statements in `try...except`. Then inside the reader's class
set the `enabled` class attribute to mark import success or failure. This makes
it possible for users to continue using their favourite markup method without
needing to install modules for all the additional formats they don't use.
How to implement a new generator ?
==================================

View file

@ -19,67 +19,135 @@ processed.
Here are the available settings. Please note that all the settings you put in
this file will be passed to the templates as well.
======================= =======================================================
Setting name what it does ?
======================= =======================================================
`AUTHOR` Default author (put your name)
`CATEGORY_FEED` Where to put the atom categories feeds. default is
`feeds/%s.atom.xml`, where %s is the name of the
category.
`CATEGORY_FEED_RSS` Where to put the categories rss feeds. default is None
(no rss)
`CSS_FILE` To specify the CSS file you want to load, if it's not
the default one ('main.css')
`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
`DEFAULT_LANG` The default language to use. Default is 'en'.
`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
Templates can follow or not this settings.
`DIRECT_TEMPLATES` Tuple, containing templates to render. There should be
a html file for each of these templates in the theme.
`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
(mtime) if it can't get informations from the
metadata?
`FEED` relative url to output the atom feed. Default is
`feeds/all.atom.xml`
`FEED_RSS` relative url to output the rss feed. Default is
None (no rss)
`KEEP_OUTPUT_DIRECTORY` Keep the output directory and just update all the generated files.
Default is to delete the output directory.
`MARKUP` A list of available markup languages you want to use.
For the moment, only available values are `rst` and `md`.
`OUTPUT_PATH` Where to output the generated files. Default to
"output"
`PATH` path to look at for input files.
`PDF_PROCESSOR` Put True if you want to have PDF versions of your
documents. You will need to install `rst2pdf`.
`REVERSE_ARCHIVE_ORDER` Reverse the archives order. (True makes it in
descending order: the newer first)
`SITEURL` base URL of your website.
`SITENAME` Your site name,
`STATIC_PATHS` The static paths you want to have accessible on the
output path "static". By default, pelican will copy
the 'images' folder to the output folder.
`STATIC_THEME_PATHS` Static theme paths you want to copy. Default values
is `static`, but if your theme have others static paths,
you can put them here.
`TAG_CLOUD_STEPS` Count of different font sizes in the tag cloud.
`TAG_CLOUD_MAX_ITEMS` Maximum tags count in the cloud.
`THEME` theme to use to product the output. can be the
complete static path to a theme folder, or chosen
between the list of default themes (see below)
`TRANSLATION_FEED` Where to put the RSS feed for translations. Default
is feeds/all-%s.atom.xml where %s is the name of the
lang.
======================= =======================================================
======================== =======================================================
Setting name what it does ?
======================== =======================================================
`AUTHOR` Default author (put your name)
`CATEGORY_FEED` Where to put the atom categories feeds. default is
`feeds/%s.atom.xml`, where %s is the name of the
category.
`CATEGORY_FEED_RSS` Where to put the categories rss feeds. default is None
(no rss)
`CSS_FILE` To specify the CSS file you want to load, if it's not
the default one ('main.css')
`DATE_FORMATS` If you do manage multiple languages, you can set
the date formatting here.
`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
`DEFAULT_DATE_FORMAT` The default date format you want to use.
`DEFAULT_LANG` The default language to use. Default is 'en'.
`DEFAULT_ORPHANS` The minimum number of articles allowed on the last
page, defaults to zero. Use this when you don't want
to have a last page with very few articles.
`DEFAULT_PAGINATION` The maximum number of articles to include on a page,
not including orphans. Default is 5.
`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
Templates can follow or not this settings.
`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
(mtime) if it can't get informations from the
metadata?
`FEED` relative url to output the atom feed. Default is
`feeds/all.atom.xml`
`FEED_RSS` relative url to output the rss feed. Default is
None (no rss)
`JINJA_EXTENSIONS` A list of any Jinja2 extensions you want to use.
Default is no extensions (the empty list).
`KEEP_OUTPUT_DIRECTORY` Keep the output directory and just update all the
generated files.
`LOCALE` Change the locale. Default is the system locale.
Default is to delete the output directory.
`MARKUP` A list of available markup languages you want to use.
For the moment, only available values are `rst` and `md`.
`OUTPUT_PATH` Where to output the generated files. Default to
"output"
`PATH` path to look at for input files.
`PDF_PROCESSOR` Put True if you want to have PDF versions of your
documents. You will need to install `rst2pdf`.
`REVERSE_ARCHIVE_ORDER` Reverse the archives order. (True makes it in
descending order: the newer first)
`REVERSE_CATEGORY_ORDER` Reverse the category order. (True makes it in
descending order, default is alphabetically)
`SITEURL` base URL of your website.
`SITENAME` Your site name,
`SKRIBIT_TYPE` The type of skribit widget (TAB or WIDGET).
`SKRIBIT_TAB_COLOR` Tab color (#XXXXXX, default #333333).
`SKRIBIT_TAB_HORIZ` Tab Distance from Left (% or distance, default Null).
`SKRIBIT_TAB_VERT` Tab Distance from Top (% or distance, default 20%).
`SKRIBIT_TAB_PLACEMENT` Tab placement (Top, Bottom, Left or Right, default
LEFT).
`SKRIBIT_TAB_SITENAME` Tab identifier (See Skribit part below).
`SKRIBIT_WIDGET_ID` Widget identifier (See Skribit part below).
`STATIC_PATHS` The static paths you want to have accessible on the
output path "static". By default, pelican will copy
the 'images' folder to the output folder.
`STATIC_THEME_PATHS` Static theme paths you want to copy. Default values
is `static`, but if your theme have others static paths,
you can put them here.
`TAG_CLOUD_STEPS` Count of different font sizes in the tag cloud.
`TAG_CLOUD_MAX_ITEMS` Maximum tags count in the cloud.
`THEME` theme to use to product the output. can be the
complete static path to a theme folder, or chosen
between the list of default themes (see below)
`TRANSLATION_FEED` Where to put the RSS feed for translations. Default
is feeds/all-%s.atom.xml where %s is the name of the
lang.
`WITH_PAGINATION` Activate pagination. Default is False.
======================== =======================================================
Skribit
=======
Skribit has two ways to display suggestions : as a sidebar widget or as a
suggestions tab. You can choose one of the display by setting the SKRIBIT_TYPE
in your config.
Sidebar widget
--------------
The settings for sidebar widget is :
* SKRIBIT_WIDGET_ID : the identifier of your blog.
All the customizations are done in the skribit web interface.
To retrieve your identifier from the code snippet, you can use this python code::
import re
regex = re.compile('.*http://assets.skribit.com/javascripts/SkribitWidget.\
js\?renderTo=writeSkribitHere&amp;blog=(.*)&amp;.*')
snippet = '''SNIPPET CONTENT'''
snippet = snippet.replace('\n', '')
identifier = regex.match(snippet).groups()[0]
Suggestion tab
--------------
The setting for suggestion tab's customizations are :
* SKRIBIT_TAB_COLOR
* SKRIBIT_TAB_DISTANCE_HORIZ
* SKRIBIT_TAB_DISTANCE_VERT
* SKRIBIT_TAB_PLACEMENT
The identifier is :
* SKRIBIT_TAB_SITENAME : the identifier of your blog
To retrieve your sitename from the code snippet, you can use this python code::
import re
regex = re.compile('.*http://skribit.com/lightbox/(.*)\',.*')
snippet = '''SNIPPET CONTENT'''
snippet = snippet.replace('\n', '')
identifier = regex.match(snippet).groups()[0]
Themes
======
3 themes are available. You can specify them using the `-t` option:
By default, two themes are availablee. You can specify them using the `-t` option:
* notmyidea
* simple (a synonym for "full text" :)
* martyalchin
You can define your own theme too, and specify it's emplacement in the same
way (be sure to specify the full absolute path to it).
@ -87,6 +155,8 @@ way (be sure to specify the full absolute path to it).
Here is `a guide on how to create your theme
<http://alexis.notmyidea.org/pelican/themes.html>`_
You can find a list of themes at http://github.com/ametaireau/pelican-themes.
The `notmyidea` theme can make good use of the following settings. I recommend
to use them too in your themes.
@ -102,6 +172,9 @@ Setting name what it does ?
the header.
`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
section.
`TWITTER_USERNAME` Allows to add a button on the articles to tweet about
them. Add you twitter username if you want this
button to appear.
======================= =======================================================
In addition, you can use the "wide" version of the `notmyidea` theme, by

View file

@ -1,8 +1,13 @@
.. _theming-pelican:
How to create themes for pelican
################################
Pelican uses the great `jinja2 <http://jinjna.pocoo.org>`_ templating engine to
generate it's HTML output.
generate it's 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>`_
Structure
=========
@ -61,18 +66,50 @@ categories A dict containing each category (keys), and the
pages The list of pages
============= ===================================================
index.html
----------
Home page of your blog, will finally remain at output/index.html.
If pagination is active, next pages will remain at output/index`n`.html.
=================== ===================================================
Variable Description
=================== ===================================================
articles_paginator A paginator object of article list
articles_page The current page of articles
dates_paginator A paginator object of article list, ordered by date,
ascending
dates_page The current page of articles, ordered by date,
ascending
page_name 'index'. Useful for pagination links.
=================== ===================================================
category.html
-------------
This template will be processed for each of the existing categories, and will
finally remain at output/category/`category_name`.html.
============= ===================================================
Variable Description
============= ===================================================
articles The articles of this category
category The name of the category being processed
============= ===================================================
If pagination is active, next pages will remain at
output/category/`category_name``n`.html.
=================== ===================================================
Variable Description
=================== ===================================================
category The name of the category being processed
articles Articles of this category
dates Articles of this category, but ordered by date,
ascending
articles_paginator A paginator object of article list
articles_page The current page of articles
dates_paginator A paginator object of article list, ordered by date,
ascending
dates_page The current page of articles, ordered by date,
ascending
page_name 'category/`category_name`'. Useful for pagination
links.
=================== ===================================================
article.html
-------------
@ -91,11 +128,38 @@ tag.html
--------
For each tag, this template will be processed. It will create .html files in
/output/tag/`tag_name`.html
/output/tag/`tag_name`.html.
============= ===================================================
Variable Description
============= ===================================================
tag The name of the tag being processed
articles Articles related to this tag
============= ===================================================
If pagination is active, next pages will remain at
output/tag/`tag_name``n`.html.
=================== ===================================================
Variable Description
=================== ===================================================
tag The name of the tag being processed
articles Articles related to this tag
dates Articles related to this tag, but ordered by date,
ascending
articles_paginator A paginator object of article list
articles_page The current page of articles
dates_paginator A paginator object of article list, ordered by date,
ascending
dates_page The current page of articles, ordered by date,
ascending
page_name 'tag/`tag_name`'. Useful for pagination links.
=================== ===================================================
Include skribit script
======================
In order to support skribit scripts in your themes, you must following these
actions :
* Copy `skribit_tab_script.html` and `skribit_widget_script.html` in your
templates directory.
* Add {% include 'skribit_tab_script.html' %} in your <head> part in order to
support suggestions tab.
* Add {% include 'skribit_widget_script.html' %} where you want in order to
support sidebar widget.
You can take a look at notmyidea default theme for working example.

View file

@ -1,87 +1,92 @@
import argparse
import os
from functools import partial
from pelican.settings import read_settings
from pelican.utils import clean_output_dir
from pelican.writers import Writer
from pelican.generators import (ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator)
StaticGenerator, PdfGenerator)
from pelican.settings import read_settings
from pelican.utils import clean_output_dir, files_changed
from pelican.writers import Writer
VERSION = "2.5.3"
VERSION = "2.6.0"
def init_params(settings=None, path=None, theme=None, output_path=None,
markup=None, keep=False):
"""Read the settings, and performs some checks on the environment
before doing anything else.
"""
if settings is None:
settings = {}
settings = read_settings(settings)
path = path or settings['PATH']
if path.endswith('/'):
path = path[:-1]
class Pelican(object):
def __init__(self, settings=None, path=None, theme=None, output_path=None,
markup=None, keep=False):
"""Read the settings, and performs some checks on the environment
before doing anything else.
"""
self.path = path or settings['PATH']
if not self.path:
raise Exception('you need to specify a path to search the docs on !')
if self.path.endswith('/'):
self.path = path[:-1]
# define the default settings
theme = theme or settings['THEME']
output_path = output_path or settings['OUTPUT_PATH']
output_path = os.path.realpath(output_path)
markup = markup or settings['MARKUP']
keep = keep or settings['KEEP_OUTPUT_DIRECTORY']
# define the default settings
self.settings = settings
self.theme = theme or settings['THEME']
output_path = output_path or settings['OUTPUT_PATH']
self.output_path = os.path.realpath(output_path)
self.markup = markup or settings['MARKUP']
self.keep = keep or settings['KEEP_OUTPUT_DIRECTORY']
# find the theme in pelican.theme if the given one does not exists
if not os.path.exists(theme):
theme_path = os.sep.join([os.path.dirname(
os.path.abspath(__file__)), "themes/%s" % theme])
if os.path.exists(theme_path):
theme = theme_path
else:
raise Exception("Impossible to find the theme %s" % theme)
# find the theme in pelican.theme if the given one does not exists
if not os.path.exists(self.theme):
theme_path = os.sep.join([os.path.dirname(
os.path.abspath(__file__)), "themes/%s" % self.theme])
if os.path.exists(theme_path):
self.theme = theme_path
else:
raise Exception("Impossible to find the theme %s" % theme)
# get the list of files to parse
if not path:
raise Exception('you need to specify a path to search the docs on !')
def run(self):
"""Run the generators and return"""
return settings, path, theme, output_path, markup, keep
context = self.settings.copy()
generators = [
cls(
context,
self.settings,
self.path,
self.theme,
self.output_path,
self.markup,
self.keep
) for cls in self.get_generator_classes()
]
for p in generators:
if hasattr(p, 'generate_context'):
p.generate_context()
# erase the directory if it is not the source
if os.path.realpath(self.path).startswith(self.output_path) and not self.keep:
clean_output_dir(self.output_path)
writer = self.get_writer()
for p in generators:
if hasattr(p, 'generate_output'):
p.generate_output(writer)
def run_generators(generators, settings, path, theme, output_path, markup, keep):
"""Run the generators and return"""
def get_generator_classes(self):
generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
if self.settings['PDF_GENERATOR']:
generators.append(PdfGenerator)
return generators
context = settings.copy()
generators = [p(context, settings, path, theme, output_path, markup, keep)
for p in generators]
def get_writer(self):
return Writer(self.output_path, settings=self.settings)
for p in generators:
if hasattr(p, 'generate_context'):
p.generate_context()
# erase the directory if it is not the source
if output_path not in os.path.realpath(path) and not keep:
clean_output_dir(output_path)
writer = Writer(output_path)
for p in generators:
if hasattr(p, 'generate_output'):
p.generate_output(writer)
def run_pelican(settings, path, theme, output_path, markup, delete):
"""Run pelican with the given parameters"""
params = init_params(settings, path, theme, output_path, markup, delete)
generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
if params[0]['PDF_GENERATOR']: # param[0] is settings
generators.append(PdfGenerator)
run_generators(generators, *params)
def main():
parser = argparse.ArgumentParser(description="""A tool to generate a
static blog, with restructured text input files.""")
parser.add_argument(dest='path',
parser.add_argument(dest='path', nargs='?',
help='Path where to find the content files')
parser.add_argument('-t', '--theme-path', dest='theme',
help='Path where to find the theme templates. If not specified, it will'
@ -99,14 +104,37 @@ def main():
help='Keep the output directory and just update all the generated files.'
'Default is to delete the output directory.')
parser.add_argument('--version', action='version', version=VERSION,
help="Print the pelican version and exit")
help='Print the pelican version and exit')
parser.add_argument('-r', '--autoreload', dest='autoreload', action='store_true',
help="Relaunch pelican each time a modification occurs on the content"
"files")
args = parser.parse_args()
# Split the markup languages only if some have been given. Otherwise, populate
# the variable with None.
markup = [a.strip().lower() for a in args.markup.split(',')] if args.markup else None
run_pelican(args.settings, args.path, args.theme, args.output, markup, args.keep)
if args.settings is None:
settings = {}
settings = read_settings(args.settings)
cls = settings.get('PELICAN_CLASS')
if isinstance(cls, basestring):
module, cls_name = cls.rsplit('.', 1)
module = __import__(module)
cls = getattr(module, cls_name)
pelican = cls(settings, args.path, args.theme, args.output, markup, args.keep)
if args.autoreload:
while True:
try:
if files_changed(pelican.path, pelican.markup):
pelican.run()
except KeyboardInterrupt:
break
else:
pelican.run()
if __name__ == '__main__':

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from pelican.utils import slugify, truncate_html_words
@ -11,12 +12,12 @@ class Page(object):
mandatory_properties = ('title',)
def __init__(self, content, metadatas={}, settings={}, filename=None):
self.content = content
self._content = content
self.translations = []
self.status = "published" # default value
for key, value in metadatas.items():
setattr(self, key, value)
setattr(self, key.lower(), value)
if not hasattr(self, 'author'):
if 'AUTHOR' in settings:
@ -47,6 +48,21 @@ class Page(object):
if filename:
self.filename = filename
if not hasattr(self, 'date_format'):
if self.lang in settings['DATE_FORMATS']:
self.date_format = settings['DATE_FORMATS'][self.lang]
else:
self.date_format = settings['DEFAULT_DATE_FORMAT']
if hasattr(self, 'date'):
self.locale_date = self.date.strftime(self.date_format.encode('ascii','xmlcharrefreplace')).decode('utf')
if not hasattr(self, 'summary'):
self.summary = property(lambda self: truncate_html_words(self.content, 50)).__get__(self, Page)
# store the settings ref.
self._settings = settings
def check_properties(self):
"""test that each mandatory property is set."""
for prop in self.mandatory_properties:
@ -54,8 +70,12 @@ class Page(object):
raise NameError(prop)
@property
def summary(self):
return truncate_html_words(self.content, 50)
def content(self):
if hasattr(self, "_get_content"):
content = self._get_content()
else:
content = self._content
return content
class Article(Page):

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from operator import attrgetter, itemgetter
from itertools import chain
from functools import partial
@ -10,13 +11,10 @@ import random
from jinja2 import Environment, FileSystemLoader
from jinja2.exceptions import TemplateNotFound
from pelican.utils import update_dict, copytree, process_translations, open
from pelican.utils import copytree, get_relative_path, process_translations, open
from pelican.contents import Article, Page, is_valid_content
from pelican.readers import read_file
_TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories',
'archives', 'page')
class Generator(object):
"""Baseclass generator"""
@ -32,7 +30,10 @@ class Generator(object):
# templates cache
self._templates = {}
self._templates_path = os.path.expanduser(os.path.join(self.theme, 'templates'))
self._env = Environment(loader = FileSystemLoader(self._templates_path))
self._env = Environment(
loader=FileSystemLoader(self._templates_path),
extensions=self.settings.get('JINJA_EXTENSIONS', []),
)
def get_template(self, name):
"""Return the template by name.
@ -84,8 +85,8 @@ class ArticlesGenerator(Generator):
self.articles = [] # only articles in default language
self.translations = []
self.dates = {}
self.tags = {}
self.categories = {}
self.tags = defaultdict(list)
self.categories = defaultdict(list)
super(ArticlesGenerator, self).__init__(*args, **kwargs)
def generate_feeds(self, writer):
@ -97,7 +98,7 @@ class ArticlesGenerator(Generator):
writer.write_feed(self.articles, self.context,
self.settings['FEED_RSS'], feed_type='rss')
for cat, arts in self.categories.items():
for cat, arts in self.categories:
arts.sort(key=attrgetter('date'), reverse=True)
writer.write_feed(arts, self.context,
self.settings['CATEGORY_FEED'] % cat)
@ -135,26 +136,41 @@ class ArticlesGenerator(Generator):
writer.write_file,
relative_urls = self.settings.get('RELATIVE_URLS')
)
for template in self.settings.get('DIRECT_TEMPLATES'):
write('%s.html' % template, self.get_template(template), self.context,
blog=True)
tag_template = self.get_template('tag')
for tag, articles in self.tags.items():
write('tag/%s.html' % tag, tag_template, self.context, tag=tag,
articles=articles)
category_template = self.get_template('category')
for cat in self.categories:
write('category/%s.html' % cat, category_template, self.context,
category=cat, articles=self.categories[cat])
# to minimize the number of relative path stuff modification
# in writer, articles pass first
article_template = self.get_template('article')
for article in chain(self.translations, self.articles):
write(article.save_as,
article_template, self.context, article=article,
category=article.category)
PAGINATED_TEMPLATES = self.settings.get('PAGINATED_DIRECT_TEMPLATES')
for template in self.settings.get('DIRECT_TEMPLATES'):
paginated = {}
if template in PAGINATED_TEMPLATES:
paginated = {'articles': self.articles, 'dates': self.dates}
write('%s.html' % template, self.get_template(template), self.context,
blog=True, paginated=paginated, page_name=template)
# and subfolders after that
tag_template = self.get_template('tag')
for tag, articles in self.tags.items():
dates = [article for article in self.dates if article in articles]
write('tag/%s.html' % tag, tag_template, self.context, tag=tag,
articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name='tag/%s' % tag)
category_template = self.get_template('category')
for cat, articles in self.categories:
dates = [article for article in self.dates if article in articles]
write('category/%s.html' % cat, category_template, self.context,
category=cat, articles=articles, dates=dates,
paginated={'articles': articles, 'dates': dates},
page_name='category/%s' % cat)
def generate_context(self):
"""change the context"""
@ -166,8 +182,7 @@ class ArticlesGenerator(Generator):
# if no category is set, use the name of the path as a category
if 'category' not in metadatas.keys():
category = os.path.dirname(f).replace(
os.path.expanduser(self.path)+'/', '')
category = os.path.basename(os.path.dirname(f))
if category == self.path:
category = self.settings['DEFAULT_CATEGORY']
@ -186,14 +201,14 @@ class ArticlesGenerator(Generator):
if hasattr(article, 'tags'):
for tag in article.tags:
update_dict(self.tags, tag, article)
self.tags[tag].append(article)
all_articles.append(article)
self.articles, self.translations = process_translations(all_articles)
for article in self.articles:
# only main articles are listed in categories, not translations
update_dict(self.categories, article.category, article)
self.categories[article.category].append(article)
# sort the articles by date
@ -228,8 +243,13 @@ class ArticlesGenerator(Generator):
random.shuffle(self.tag_cloud)
# and generate the output :)
# order the categories per name
self.categories = list(self.categories.items())
self.categories.sort(reverse=self.settings.get('REVERSE_CATEGORY_ORDER'))
self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud'))
def generate_output(self, writer):
self.generate_feeds(writer)
self.generate_pages(writer)
@ -304,7 +324,8 @@ class PdfGenerator(Generator):
pass
def generate_output(self, writer=None):
# we don't use the writer passed as argument here, since we write our own files
# we don't use the writer passed as argument here
# since we write our own files
print u' Generating PDF files...'
pdf_path = os.path.join(self.output_path, 'pdf')
try:

85
pelican/paginator.py Normal file
View file

@ -0,0 +1,85 @@
# From django.core.paginator
from math import ceil
class Paginator(object):
def __init__(self, object_list, per_page, orphans=0):
self.object_list = object_list
self.per_page = per_page
self.orphans = orphans
self._num_pages = self._count = None
def page(self, number):
"Returns a Page object for the given 1-based page number."
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
def _get_count(self):
"Returns the total number of objects, across all pages."
if self._count is None:
self._count = len(self.object_list)
return self._count
count = property(_get_count)
def _get_num_pages(self):
"Returns the total number of pages."
if self._num_pages is None:
hits = max(1, self.count - self.orphans)
self._num_pages = int(ceil(hits / float(self.per_page)))
return self._num_pages
num_pages = property(_get_num_pages)
def _get_page_range(self):
"""
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
return range(1, self.num_pages + 1)
page_range = property(_get_page_range)
class Page(object):
def __init__(self, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = paginator
def __repr__(self):
return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
def has_next(self):
return self.number < self.paginator.num_pages
def has_previous(self):
return self.number > 1
def has_other_pages(self):
return self.has_previous() or self.has_next()
def next_page_number(self):
return self.number + 1
def previous_page_number(self):
return self.number - 1
def start_index(self):
"""
Returns the 1-based index of the first object on this page,
relative to total objects in the paginator.
"""
# Special case, return zero if no items.
if self.paginator.count == 0:
return 0
return (self.paginator.per_page * (self.number - 1)) + 1
def end_index(self):
"""
Returns the 1-based index of the last object on this page,
relative to total objects found (hits).
"""
# Special case for the last page because there can be orphans.
if self.number == self.paginator.num_pages:
return self.paginator.count
return self.number * self.paginator.per_page

View file

@ -1,11 +1,18 @@
from docutils import core
from markdown import Markdown
# -*- coding: utf-8 -*-
try:
from docutils import core
# import the directives to have pygments support
import rstdirectives
except ImportError:
core = False
try:
from markdown import Markdown
except ImportError:
Markdown = False
import re
import string
# import the directives to have pygments support
import rstdirectives
from pelican.utils import get_date, open
@ -16,7 +23,12 @@ _METADATAS_PROCESSORS = {
}
class RstReader(object):
class Reader(object):
enabled = True
class RstReader(Reader):
enabled = bool(core)
extension = "rst"
def _parse_metadata(self, content):
"""Return the dict containing metadatas"""
@ -42,7 +54,9 @@ class RstReader(object):
metadatas['title'] = title
return content, metadatas
class MarkdownReader(object):
class MarkdownReader(Reader):
enabled = bool(Markdown)
extension = "md"
def read(self, filename):
"""Parse content and metadata of markdown files"""
@ -58,8 +72,25 @@ class MarkdownReader(object):
)(value[0])
return content, metadatas
_EXTENSIONS = {'rst': RstReader, 'md': MarkdownReader} # supported formats
class HtmlReader(Reader):
extension = "html"
_re = re.compile('\<\!\-\-\#\s?[A-z0-9_-]*\s?\:s?[A-z0-9\s_-]*\s?\-\-\>')
def read(self, filename):
"""Parse content and metadata of (x)HTML files"""
content = open(filename)
metadatas = {'title':'unnamed'}
for i in self._re.findall(content):
key = i.split(':')[0][5:].strip()
value = i.split(':')[-1][:-3].strip()
metadatas[key.lower()] = value
return content, metadatas
_EXTENSIONS = dict((cls.extension, cls) for cls in Reader.__subclasses__())
def read_file(filename, fmt=None):
"""Return a reader object using the given format."""
@ -68,4 +99,6 @@ def read_file(filename, fmt=None):
if fmt not in _EXTENSIONS.keys():
raise TypeError('Pelican does not know how to parse %s' % filename)
reader = _EXTENSIONS[fmt]()
if not reader.enabled:
raise ValueError("Missing dependencies for %s" % fmt)
return reader.read(filename)

View file

@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import os
import locale
_DEFAULT_THEME = os.sep.join([os.path.dirname(os.path.abspath(__file__)),
"themes/notmyidea"])
@ -18,6 +20,7 @@ _DEFAULT_CONFIG = {'PATH': None,
'FALLBACK_ON_FS_DATE': True,
'CSS_FILE': 'main.css',
'REVERSE_ARCHIVE_ORDER': False,
'REVERSE_CATEGORY_ORDER': False,
'KEEP_OUTPUT_DIRECTORY': False,
'CLEAN_URLS': False, # use /blah/ instead /blah.html in urls
'RELATIVE_URLS': True,
@ -25,6 +28,15 @@ _DEFAULT_CONFIG = {'PATH': None,
'TAG_CLOUD_STEPS': 4,
'TAG_CLOUD_MAX_ITEMS': 100,
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
'PELICAN_CLASS': 'pelican.Pelican',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
'DATE_FORMATS': {},
'JINJA_EXTENSIONS': [],
'LOCALE': '', # default to user locale
'WITH_PAGINATION': False,
'DEFAULT_PAGINATION': 5,
'DEFAULT_ORPHANS': 0,
}
def read_settings(filename):
@ -37,4 +49,7 @@ def read_settings(filename):
for key in tempdict:
if key.isupper():
context[key] = tempdict[key]
# set the locale
locale.setlocale(locale.LC_ALL, context['LOCALE'])
return context

View file

@ -1,205 +0,0 @@
.hll {
background-color:#FFFFCC;
}
.c {
color:#408090;
font-style:italic;
}
.err {
border:1px solid #FF0000;
}
.k {
color:#007020;
font-weight:bold;
}
.o {
color:#666666;
}
.cm {
color:#408090;
font-style:italic;
}
.cp {
color:#007020;
}
.c1 {
color:#408090;
font-style:italic;
}
.cs {
background-color:#FFF0F0;
color:#408090;
}
.gd {
color:#A00000;
}
.ge {
font-style:italic;
}
.gr {
color:#FF0000;
}
.gh {
color:#000080;
font-weight:bold;
}
.gi {
color:#00A000;
}
.go {
color:#303030;
}
.gp {
color:#C65D09;
font-weight:bold;
}
.gs {
font-weight:bold;
}
.gu {
color:#800080;
font-weight:bold;
}
.gt {
color:#0040D0;
}
.kc {
color:#007020;
font-weight:bold;
}
.kd {
color:#007020;
font-weight:bold;
}
.kn {
color:#007020;
font-weight:bold;
}
.kp {
color:#007020;
}
.kr {
color:#007020;
font-weight:bold;
}
.kt {
color:#902000;
}
.m {
color:#208050;
}
.s {
color:#4070A0;
}
.na {
color:#4070A0;
}
.nb {
color:#007020;
}
.nc {
color:#0E84B5;
font-weight:bold;
}
.no {
color:#60ADD5;
}
.nd {
color:#555555;
font-weight:bold;
}
.ni {
color:#D55537;
font-weight:bold;
}
.ne {
color:#007020;
}
.nf {
color:#06287E;
}
.nl {
color:#002070;
font-weight:bold;
}
.nn {
color:#0E84B5;
font-weight:bold;
}
.nt {
color:#062873;
font-weight:bold;
}
.nv {
color:#BB60D5;
}
.ow {
color:#007020;
font-weight:bold;
}
.w {
color:#BBBBBB;
}
.mf {
color:#208050;
}
.mh {
color:#208050;
}
.mi {
color:#208050;
}
.mo {
color:#208050;
}
.sb {
color:#4070A0;
}
.sc {
color:#4070A0;
}
.sd {
color:#4070A0;
font-style:italic;
}
.s2 {
color:#4070A0;
}
.se {
color:#4070A0;
font-weight:bold;
}
.sh {
color:#4070A0;
}
.si {
color:#70A0D0;
font-style:italic;
}
.sx {
color:#C65D09;
}
.sr {
color:#235388;
}
.s1 {
color:#4070A0;
}
.ss {
color:#517918;
}
.bp {
color:#007020;
}
.vc {
color:#BB60D5;
}
.vg {
color:#BB60D5;
}
.vi {
color:#BB60D5;
}
.il {
color:#208050;
}

View file

@ -1,414 +0,0 @@
/*
Design by Free CSS Templates
http://www.freecsstemplates.org
Released for free under a Creative Commons Attribution 2.5 License
*/
@import url("pygment.css");
body {
margin: 30px 0px 0px 0px;
padding: 0;
background: #7E776F url('../images/img01.jpg') repeat left top;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
color: #3E3B36;
}
.summary h2{font-size:1.6em; color:black;}
h1, h2, h3 {
margin: 0;
padding: 0;
font-weight: normal;
color: #F0E9E9;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 2.8em;
}
h3 {
font-size: 1.6em;
}
p, ul, ol {
margin-top: 0;
line-height: 180%;
}
ul, ol {
}
a {
text-decoration: none;
color: #4D8D99;
}
a:hover {
}
#wrapper {
margin: 0 auto;
padding: 0;
}
/* Header */
#header-wrapper {
height: 100px;
background: #3C3230;
border-bottom: 10px solid #4F4440;
}
#header {
width: 950px;
margin: 0 auto;
padding: 0px 0px 0px 30px;
}
/* Logo */
#logo {
width: 280px;
height: 140px;
margin: 0;
padding: 0;
background: url('../images/img07.jpg') no-repeat left top;
color: #34312C;
}
#logo h1, #logo p {
margin: 0;
padding: 0;
letter-spacing: -2px;
text-align: center;
font-family: Georgia, "Times New Roman", Times, serif;
}
#logo h1 {
margin: 0px 0px -20px 0px;
padding: 20px 0px 0px 0px;
font-size: 50px;
color: #4D8D99;
}
#logo h1 a {
color: #F0E9E9;
}
#logo p {
margin: 0px;
padding: 0px;
font-size: 26px;
}
#logo a {
border: none;
background: none;
text-decoration: none;
color: #34312C;
}
/* Search */
#search {
width: 280px;
height: 50px;
padding: 20px 0px 0px 0px;
background: url('../images/img05.jpg') no-repeat left 15px;
}
#search form {
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
}
#search fieldset {
margin: 0;
padding: 0;
border: none;
}
#search-text {
width: 190px;
padding: 0px 5px 2px 10px;
border: none;
background: none;
text-transform: lowercase;
font: normal 11px Arial, Helvetica, sans-serif;
color: #34312C;
}
#search-submit {
width: 70px;
height: 22px;
border: none;
border: none;
background: none;
text-indent: -99999px;
color: #34312C;
}
/* Menu */
#menu {
width: 280px;
margin: 20px auto 20px auto;
padding: 0;
}
#menu ul {
margin: 0;
padding: 50px 0px 0px 0px;
list-style: none;
line-height: normal;
}
#menu li {
border-bottom: 1px dashed #191918;
}
#menu a {
display: block;
width: 260px;
height: 27px;
margin: 4px 0px;
padding: 8px 0px 0px 20px;
text-decoration: none;
text-transform: capitalize;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
font-weight: normal;
color: #FFF;
}
#menu a:hover, #menu .current_page_item a {
background: url('../images/img06.jpg') no-repeat left top;
text-decoration: none;
}
#menu .current_page_item a {
}
/* Page */
#page {
width: 1000px;
margin: 0 auto;
background: url('../images/img04.jpg') repeat-y left top;
}
#page-bgtop {
background: url('../images/img02.jpg') no-repeat left top;
}
#page-bgbtm {
overflow: hidden;
width: 920px;
padding: 20px 40px 20px 40px;
background: url(images/img03.jpg) no-repeat left bottom;
}
/* Content */
#content {
float: right;
width: 520px;
padding: 70px 30px 0px 60px;
}
.post {
margin-bottom: 40px;
}
.post .title {
padding: 0px 0px 0px 0px;
font-family: Georgia, "Times New Roman", Times, serif;
letter-spacing: -.5px;
}
.post .title a {
color: #52483E;
border: none;
}
.post .meta {
margin-bottom: 0px;
padding: 10px 0px 0px 0px;
text-align: left;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
font-weight: bold;
}
.post .meta .date {
float: left;
}
.post .meta .posted {
float: right;
}
.post .meta a {
}
.post .entry {
padding: 0px 0px 20px 0px;
border-bottom: 1px dotted #99938B;
text-align: justify;
}
.post .entry dt {
font-weight:bold ;
}
.post img {
margin:10px;
border:black;
}
.summary img{
display:none
}
.entry h2{
font-size: 1.6em;
color: #36302a;
}
.entry h3{
font-size: 1.2em;
color: #36302a;
}
h2.title {
font-size:2.8em;
}
.highlight{
background:black;
padding:2px;
}
.highlight pre{
color:white;
}
.highlight{
overflow:auto;
}
.links {
padding-top: 20px;
font-size: 12px;
font-weight: bold;
}
/* Sidebar */
#sidebar {
float: left;
width: 280px;
margin: 0px;
padding: 0px 0px 80px 10px;
color: #787878;
}
#sidebar ul {
margin: 0;
padding: 0;
list-style: none;
}
#sidebar li {
margin: 0;
padding: 0;
}
#sidebar li ul {
margin: 0px 0px 0px 20px;
padding-bottom: 30px;
}
#sidebar li li {
line-height: 35px;
border-bottom: 1px dashed #191918;
border-left: none;
}
#sidebar li li span {
display: block;
margin-top: -20px;
padding: 0;
font-size: 11px;
font-style: italic;
}
#sidebar li li a {
color: #787878;
}
#sidebar li li a:hover {
color: #F0E9E9;
}
#sidebar h2 {
height: 38px;
letter-spacing: -.5px;
font-size: 1.8em;
}
#sidebar p {
margin: 0 0px;
padding: 0px 20px 20px 20px;
}
#sidebar a {
border: none;
}
#sidebar a:hover {
}
/* Calendar */
#calendar {
}
#calendar_wrap {
padding: 20px;
}
#calendar table {
width: 100%;
}
#calendar tbody td {
text-align: center;
}
#calendar #next {
text-align: right;
}
/* Footer */
#footer {
width: 920px;
height: 80px;
margin: 0 auto;
padding: 0px 0 15px 310px;
font-family: Arial, Helvetica, sans-serif;
}
#footer p {
margin: 0;
padding-top: 20px;
line-height: normal;
font-size: 9px;
text-transform: uppercase;
text-align: center;
color: #69635E;
}
#footer a {
color: #474440;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,11 +0,0 @@
{% if GOOGLE_ANALYTICS %}
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("{{GOOGLE_ANALYTICS}}");
pageTracker._trackPageview();
} catch(err) {}</script>
{% endif %}

View file

@ -1,19 +0,0 @@
{% extends "base.html" %}
{% block title %}Archives de {{ SITENAME }}{% endblock %}
{% block content %}
<div id="content">
<div class="post">
<dl>
<h2 class="title">Archives de {{ SITENAME }}</h2>
{% for article in dates %}
<dt>{{ article.date.strftime('%a %d %B %Y') }}</dt>
<dd><a href='{{ article.url }}'>{{ article.title }}</a></dd>
<dd>Catégorie : <a href="{{ article.category }}">{{ article.category }}</a></dd>
{% endfor %}
</dl>
</div>
<div style="clear: both;">&nbsp;</div>
</div>
<!-- end #content -->
{% endblock %}

View file

@ -1,35 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ article.title }}{% endblock %}
{% block content %}
<div id="content">
<div class="post">
<h2 class="title"><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p class="meta"><span class="date">Le {{ article.date.strftime('%a %d %B %Y') }} </span><span class="posted">Par <a href="#">{{ article.author }}</a></span><span>&nbsp; | Catégorie : <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a></span></p>
<p class="meta">Tags : {% for tag in article.tags %}
<span><a href="{{ SITEURL }}/tag/{{ tag }}.html">{{ tag }}</a> / </span>
{% endfor %}</p>
<div style="clear: both;">&nbsp;</div>
<div class="entry">
{{ article.content }}
{% include 'twitter.html' %}
</div>
</div>
<div style="clear: both;">&nbsp;</div>
{% if DISQUS_SITENAME %}
<div class="post">
<h2 class="title">Commentaires !</h2>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_identifier = "{{ article.url }}";
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://{{ DISQUS_SITENAME }}.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
</div>
{% endif %}
</div>
<!-- end #content -->
{% endblock %}

View file

@ -1,108 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Design by Free CSS Templates
http://www.freecsstemplates.org
Released for free under a Creative Commons Attribution 2.5 License
Name : Brown Stone
Description: A two-column, fixed-width design with dark color scheme.
Version : 1.0
Released : 20100928
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>{% block title %}{{ SITENAME }}{%endblock%}</title>
<link href="{{ SITEURL }}/theme/css/style.css" rel="stylesheet" type="text/css" media="screen" />
<link href="{{ SITEURL }}/{{ FEED }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} ATOM Feed" />
{% if FEED_RSS %}
<link href="{{ SITEURL }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
{% endif %}
</head>
<body>
<div id="wrapper">
<div id="page">
<div id="page-bgtop">
<div id="page-bgbtm">
{% block content %}
{% endblock %}
<div id="sidebar">
<div id="logo">
<h1><a href="{{ SITEURL }}">{{ SITENAME }}</h1>
{% if SITESUBTITLE %}<p>{{ SITESUBTITLE }}</p>{% endif %}
</div>
<div id="menu">
<ul>
<li class="current_page_item"><a href="{{ SITEURL }}">Home</a></li>
<li><a href="{{ SITEURL }}/archives.html">Archives</a></li>
{% if DISPLAY_PAGES_ON_MENU %}
{% for page in PAGES %}
<li><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
{% endif %}
</ul>
</div>
<ul>
<li>
<h2>Catégories</h2>
<ul>
{% for cat, null in categories %}
<li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/category/{{ cat }}.html">{{ cat }}</a></li>
{% endfor %}
</ul>
</li>
{% if LINKS %}
<li>
<h2>Blogroll</h2>
<ul>
{% for name, link in LINKS %}
<li><a href="{{ link }}">{{ name }}</a></li>
{% endfor %}
</ul>
</li>
{% endif %}
{% if SOCIAL %}
<li>
<h2>Social</h2>
<ul>
<li><a href="{{ SITEURL }}/{{ FEED }}" rel="alternate">Flux Atom</a></li>
{% if FEED_RSS %}
<li><a href="{{ SITEURL }}/{{ FEED_RSS }}" rel="alternate">Flux Rss</a></li>
{% endif %}
{% for name, link in SOCIAL %}
<li><a href="{{ link }}">{{ name }}</a></li>
{% endfor %}
</ul>
</li><!-- /.social -->
{% endif %}
<li>
<h2>Tags</h2>
<ul>
{% for tag, articles in tags %}
<li><a href="{{ SITEURL }}/tag/{{ tag }}.html">{{ tag }}</a></li>
{% endfor %}
</ul>
</li>
</ul>
</div>
<!-- end #sidebar -->
<div style="clear: both;">&nbsp;</div>
</div>
</div>
</div>
<!-- end #page -->
<div id="footer">
<p>Copyright (c) 2008 Sitename.com. All rights reserved. Design by <a href="http://www.freecsstemplates.org/">CSS Templates</a>.</p>
<p>Proudly powered by <a href="http://alexis.notmyidea.org/pelican/">pelican</a>, which takes great advantages of <a href="http://python.org">python</a>.
</p>
</div>
{% include 'analytics.html' %}
<!-- end #footer -->
</body>
</html>

View file

@ -1,18 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div id="content">
<div class="post">
{% if articles %}
{% for article in articles %}
{% if loop.index == 1 %}
<ul>
{% for category, articles in categories %}
<li>{{ category }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -1,2 +0,0 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} - {{ category }}{% endblock %}

View file

@ -1,43 +0,0 @@
{% extends "base.html" %}
{% block content_title %}{% endblock %}
{% block content %}
{% if articles %}
{% for article in articles %}
{% if loop.index == 1 %}
<div id="content">
<div class="post">
<h2 class="title"><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h2>
<p class="meta"><span class="date">Le {{ article.date.strftime('%a %d %B %Y') }} </span><span class="posted">Par <a href="#">{{ article.author }}</a></span><span>&nbsp; | Catégorie : <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a></span></p>
<p class="meta">Tags : {% for tag in article.tags %}
<span><a href="{{ SITEURL }}/tag/{{ tag }}.html">{{ tag }}</a> / </span>
{% endfor %}</p>
<div style="clear: both;">&nbsp;</div>
<div class="entry">
{{ article.content }}
{% include 'twitter.html' %}
</div>
</div>
{% if loop.length > 1 %}
<div class="post">
<h2 class="title">Autres articles</h2>
</div>
{% endif %}
{% else %}
<div class="post summary">
<h2 class="title"><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h2>
<p class="meta"><span class="date">Le {{ article.date.strftime('%a %d %B %Y') }}</span><span class="posted">Par <a href="#">{{ article.author }}</a></span></p>
<div style="clear: both;">&nbsp;</div>
<div class="entry">
{{ article.summary }}
<a class="readmore" href="{{ SITEURL }}/{{ article.url }}">Lire la suite …</a>
</div>
</div>
{% endif %}
{% endfor %}
{% else %}
<div id="content">
</div>
{% endif %}
<div style="clear: both;">&nbsp;</div>
</div>
{% endblock content %}

View file

@ -1,17 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ page.title }}{% endblock %}
{% block content %}
<div id="content">
<div class="post">
<h2 class="title"><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></h1>
{% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ page.slug }}.pdf">get
the pdf</a>{% endif %}
<div style="clear: both;">&nbsp;</div>
<div class="entry">
{{ page.content }}
{% include 'twitter.html' %}
</div>
</div>
</div>
{% endblock %}

View file

@ -1,2 +0,0 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} - {{ tag }}{% endblock %}

View file

@ -1,12 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div id="content">
<div class="post">
<ul>
{% for tag, articles in tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View file

@ -1,3 +0,0 @@
{% if TWITTER_USERNAME %}
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-via="{{TWITTER_USERNAME}}">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
{% endif %}

View file

@ -1,404 +0,0 @@
/* Resets to avoid browser differences */
body, button, div, fieldset, form, h1, h2, h3, input, label, li, p, pre, td, textarea, .typygmentdown {
margin: 0;
padding: 0;
text-align: justify;
font-family: Georgia, serif;
font-size: 100%;
line-height: 1.25;
letter-spacing: 0;
border: none;
background: none;
}
/* Overall page layout */
body {
background: white;
color: black;
color: #303030;
}
@media screen {
body {
width: 700px;
margin: 20px auto;
}
}
@media print {
body {
margin: 0 2em;
}
}
/* Headings */
h1, h2, h3, h4, h5, h6, .info, .info p {
font-family: Times, serif;
font-weight: normal;
page-break-inside: avoid;
}
h1 .caps, h2 .caps, h3 .caps {
letter-spacing: -.05em;
}
h1 {
font-family: Times, serif;
text-align: center;
font-size: 2.25em;
line-height: 1.111;
padding-right: 0.08em;
letter-spacing: -.07em;
}
h2 {
margin-top: 0.714em;
font-size: 1.75em;
line-height: 0.714;
letter-spacing: -.05em;
}
h3 {
margin-top: 0.926em;
font-size: 1.35em;
line-height: 0.926;
letter-spacing: -.03em;
}
h1 .dquo, h2 .dquo, h3 .dquo, h4 .dquo, h5 .dquo, h6 .dquo {
margin-left: -.4em;
}
.info, .info p {
text-align: center;
letter-spacing: -.03em;
}
.info p {
margin: 0;
}
.info img.g {
width: 24px;
height: 24px;
margin: -7px 0;
}
#home h2 a[href*="http://"] {
padding-right: 28px;
background: url("/static/link.png") right center no-repeat;
}
#home h2 a[href*="http://"]:visited {
padding-right: 28px;
background: url("/static/visited.png") right center no-repeat;
}
h2 + p.published {
float: right;
margin-top: -1.25em;
}
.copyright {
margin: 1.25em 0;
}
/* Page text */
p, p[class] + p {
margin-top: 1.25em;
widows: 2;
orphans: 2;
text-indent: 0;
clear: left;
}
p + p {
text-indent: 1.5em;
margin-top: 0;
}
p ~ img {
display: block;
margin: 1.25em auto;
}
.caps {
letter-spacing: 0.1em;
font-size: 75%;
}
abbr {
border-bottom: 1px dotted black;
}
blockquote {
margin: 0 1em;
font-style: italic;
letter-spacing: -0.0625em;
}
blockquote em {
font-style: normal;
letter-spacing: 0;
}
div.image {
text-align: center;
margin: 1.25em 0;
}
img {
border: none;
}
.side {
position: absolute;
width: 150px;
height: auto;
margin-left: 710px;
}
.left.side {
margin-left: -160px;
}
.right.side {
margin-left: 710px;
}
@media screen {
h1 a, h2 a, h3 a, .info a {
text-decoration: none;
}
a:link {
color: #85ac40;
}
a:visited {
color: #61883b;
}
::selection {
background: #dcff9d;
}
::-moz-selection {
background: #e2ffaf;
}
}
@media print {
a {
color: inherit;
text-decoration: none;
}
abbr {
border-bottom: none;
}
}
/* Lists */
ul, ol {
margin: 1.25em 0 1.25em -1.5em;
padding-left: 1.5em;
}
ul ul, ul ol, ol ol, ol ul {
margin: 0;
}
ul li {
list-style: disc;
}
li p {
margin: 0;
}
/* Code */
pre {
margin-top: 1.47em;
font-family: Courier;
font-size: .85em;
line-height: 1.47;
overflow: visible;
}
code {
font-family: Courier;
font-size: .85em;
line-height: 1;
}
.typygmentdown .c {
font-style: italic;
}
.typygmentdown .k, .typygmentdown .ow {
color: #404040;
}
.typygmentdown .c, .typygmentdown .sd {
color: #808080;
}
/* Comments */
#comment-list {
margin: 0;
padding: 0;
}
#comment-list li {
padding-bottom: 1.25em;
}
#comment-list cite {
font-style: normal;
}
#comment-list cite + blockquote {
margin-top: 0;
}
#comment-list blockquote {
margin: 0;
}
#comment-list p {
margin-top: 1.25em;
text-indent: 0;
}
#comment-form th {
width: 25%;
}
#comment-form input, #comment-form select, #comment-form textarea {
width: 100%;
}
@media print {
#comment-list, #comment-form {
display: none;
}
}
/* Friends */
#friends li {
list-style: none;
margin-left: 0;
padding-left: 0;
}
#friends a[rel] {
margin-left: 20px;
}
#friends a[rel~="colleague"] {
background: url("/static/dj.png") left center no-repeat;
margin-left: 0;
padding-left: 20px;
}
/* Forms */
form, form p {
text-indent: 0;
text-align: left;
}
from th, form td {
margin: 0;
padding: 0;
}
input, select, textarea {
background: white;
line-height: 1.5;
}
input[type="submit"], button {
border: 1px outset;
text-align: center;
background: #85ac40;
padding: .2em;
}
input[type="text"], textarea {
vertical-align: top;
border: 1px solid #e0e0e0;
}
input[type="text"], select {
width: 15em;
}
textarea {
width: 45em;
height: 7.5em;
overflow: auto;
}
#honeypot {
display: none;
}
/* Tables */
table {
border-collapse: collapse;
border: none;
margin: 1.25em auto;
}
caption {
border-bottom: 1px solid #303030;
}
thead th {
border-bottom: 1px solid #303030;
}
tfoot th,
tfoot td {
border-top: 1px solid #303030;
}
th[scope="row"],
th[scope="col"] {
text-align: left;
}
tbody + tbody th, tbody + tbody td {
border-top: 1px solid #d0d0d0;
}
tbody + tbody tr + tr th,
tbody + tbody tr + tr td,
tfoot tr + tr th,
tfoot tr + tr td {
border-top: none;
}
th, td {
padding: 0 0.5em;
font-weight: normal;
vertical-align: top;
}
table.numeric td, table.numeric th[scope="col"] {
text-align: right;
}

View file

@ -1,13 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ article.title }}{%endblock%}
{% block content %}
<h1>{{ article.title }}</h1>
<div class="info">
{% if article.author %}
By <a class="url fn" href="#">{{ article.author }}</a>
{% endif %}
on <a>{{ article.date.strftime('%a %d %B %Y') }}</a>
about <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>
</div>
{{ article.content }}
{% endblock %}

View file

@ -1,15 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>{% block title %}{{ SITENAME }}{%endblock%}</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{ SITEURL }}/theme/css/style.css" type="text/css" />
<link href="{{ SITEURL }}/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} ATOM Feed" />
</head>
<body>
{% block content %} {% endblock %}
<div class="copyright info vcard">Design by <a class="fn url"
href="http://martyalchin.com">Marty Alchin</a>, <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">some rights reserved</a>. Powered by <a href="http://alexis.notomyidea.org/pelican/">pelican</a></div>
</body>
</html>

View file

@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ category }}{%endblock%}
<h1>{{ category }}</h1>
<div class="info"></div>
{% for article in articles %}
<h2><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h2>
<p class="published">{{ article.date.strftime('%a %d %B %Y') }}</p>
{{ article.summary }}
{% endfor %}

View file

@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h1><a href="{{ SITEURL }}">{{ SITENAME }}</a></h1>
{% if SITESUBTITLE %} <div class="info">{{ SITESUBTITLE }}</div> {% endif %}
{% for article in articles %}
<h2><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h2>
<p class="published">{{ article.date.strftime('%a %d %B %Y') }}</p>
{{ article.summary }}
{% endfor %}
{% endblock %}

View file

@ -1,7 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ page.title }}{%endblock%}
{% block content %}
<h1>{{ page.title }}</h1>
{{ page.content }}
{% endblock %}

View file

@ -276,14 +276,6 @@ img.left, figure.left {float: right; margin: 0 0 2em 2em;}
padding: .3em .25em;
}
#extras li:last-child,
#extras li:last-child a {border: 0}
#extras .blogroll li:nth-last-child(2),
#extras .blogroll li:nth-last-child(3),
#extras .blogroll li:nth-last-child(2) a,
#extras .blogroll li:nth-last-child(3) a {border: 0;}
#extras a:hover, #extras a:active {color: #fff;}
/* Blogroll */

View file

@ -5,7 +5,7 @@
<dl>
{% for article in dates %}
<dt>{{ article.date.strftime('%a %d %B %Y') }}</dt>
<dt>{{ article.locale_date }}</dt>
<dd><a href='{{ article.url }}'>{{ article.title }}</a></dd>
{% endfor %}
</dl>

View file

@ -7,18 +7,7 @@
rel="bookmark" title="Permalink to {{ article.title }}">{{ article.title
}}</a></h1> {% include 'twitter.html' %} </header>
<div class="entry-content">
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.date.strftime('%a %d %B %Y') }}
</abbr>
{% if article.author %}
<address class="vcard author">
By <a class="url fn" href="#">{{ article.author }}</a>
</address>
{% endif %}
<p>In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>.
{% include 'taglist.html' %}
</footer><!-- /.post-info -->
{% include 'article_infos.html' %}
{{ article.content }}
</div><!-- /.entry-content -->
{% if DISQUS_SITENAME %}

View file

@ -0,0 +1,14 @@
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.locale_date }}
</abbr>
{% if article.author %}
<address class="vcard author">
By <a class="url fn" href="#">{{ article.author }}</a>
</address>
{% endif %}
<p>In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
{% include 'taglist.html' %}
{% include 'translations.html' %}
</footer><!-- /.post-info -->

View file

@ -9,6 +9,7 @@
<link href="{{ SITEURL }}/{{ FEED_RSS }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
{% endif %}
{% include 'skribit_tab_script.html' %}
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
@ -53,6 +54,7 @@
</ul>
</div><!-- /.blogroll -->
{% endif %}
{% include 'skribit_widget_script.html' %}
{% if SOCIAL %}
<div class="social">
<h2>social</h2>

View file

@ -2,62 +2,53 @@
{% block content_title %}{% endblock %}
{% block content %}
{% if articles %}
{% for article in articles %}
{% if loop.index == 1 %}
<aside id="featured" class="body"><article>
<h1 class="entry-title"><a href="{{ SITEURL }}/{{ article.url
}}">{{ article.title }}</a></h1>
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.date.strftime('%a %d %B %Y') }}
</abbr>
{% for article in articles_page.object_list %}
{% if article.author %}
<address class="vcard author">
By <a class="url fn" href="#">{{ article.author }}</a>
</address>
{% endif %}
<p>In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
{% include 'taglist.html' %}
</footer><!-- /.post-info -->
{{ article.content }}
{% include 'comments.html' %}
{# First item #}
{% if loop.first and not articles_page.has_previous() %}
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></h1>
{% include 'article_infos.html' %}{{ article.content }}{% include 'comments.html' %}
</article>
</aside><!-- /#featured -->
{% if loop.length > 1 %}
<section id="content" class="body">
<h1>Other articles</h1>
<hr />
<ol id="posts-list" class="hfeed">
{% endif %}
{% else %}
<li><article class="hentry">
{% if loop.length == 1 %}
{% include 'pagination.html' %}
{% endif %}
</aside><!-- /#featured -->
{% if loop.length > 1 %}
<section id="content" class="body">
<h1>Other articles</h1>
<hr />
<ol id="posts-list" class="hfeed">
{% endif %}
{# other items #}
{% else %}
{% if loop.first and articles_page.has_previous %}
<section id="content" class="body">
<ol id="posts-list" class="hfeed" start="{{ articles_paginator.per_page -1 }}">
{% endif %}
<li><article class="hentry">
<header>
<h1><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h1>
</header>
<div class="entry-content">
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.date.strftime('%a %d %B %Y') }}
</abbr>
<address class="vcard author">
By <a class="url fn" href="#">{{ article.author }}</a>
</address>
<p> In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a></p>
{% include 'taglist.html' %}
<p>{% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">pdf</a>{% endif %}</p>
</footer><!-- /.post-info -->
{% include 'article_infos.html' %}
{{ article.summary }}
<a class="readmore" href="{{ SITEURL }}/{{ article.url }}">read more</a>
{% include 'comments.html' %}
</div><!-- /.entry-content -->
</article></li>
{% endif %}
</article></li>
{% endif %}
{% if loop.last and (articles_page.has_previous()
or not articles_page.has_previous() and loop.length > 1) %}
{% include 'pagination.html' %}
{% endif %}
{% endfor %}
</ol><!-- /#posts-list -->
</section><!-- /#content -->
{% if loop.length > 1 or articles_page.has_previous() %}
</ol><!-- /#posts-list -->
</section><!-- /#content -->
{% endif %}
{% else %}
<section id="content" class="body">
<h2>Pages</h2>

View file

@ -0,0 +1,13 @@
<p class="paginator">
{% if articles_page.has_previous() %}
{% if articles_page.previous_page_number() == 1 %}
<a href="{{ SITEURL }}/{{ page_name }}.html">&laquo;</a>
{% else %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html">&laquo;</a>
{% endif %}
{% endif %}
Page {{ articles_page.number }} / {{ articles_paginator.num_pages }}
{% if articles_page.has_next() %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.next_page_number() }}.html">&raquo;</a>
{% endif %}
</p>

View file

@ -0,0 +1,14 @@
{% if SKRIBIT_TYPE and SKRIBIT_TYPE == 'TAB' and SKRIBIT_TAB_SITENAME %}
<link rel="stylesheet" type="text/css" media="screen" charset="utf-8" href="http://assets.skribit.com/stylesheets/SkribitSuggest.css"></link>
<style type="text/css" media="print" charset="utf-8">a#sk_tab{display:none !important;}</style>
<script src="http://assets.skribit.com/javascripts/SkribitSuggest.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
var skribit_settings = {};
skribit_settings.placement = "{{ SKRIBIT_TAB_PLACEMENT or 'right' }}";
skribit_settings.color = "{{ SKRIBIT_TAB_COLOR or '#333333' }}";
skribit_settings.text_color = "{{ SKRIBIT_TAB_TEXT_COLOR or 'white' }}";
skribit_settings.distance_vert = "{{ SKRIBIT_TAB_VERT or '20%' }}";
skribit_settings.distance_horiz = "{{ SKRIBIT_TAB_HORIZ or '' }}";
SkribitSuggest.suggest('http://skribit.com/lightbox/{{ SKRIBIT_TAB_SITENAME }}', skribit_settings);
</script>
{% endif %}

View file

@ -0,0 +1,8 @@
{% if SKRIBIT_TYPE == 'WIDGET' and SKRIBIT_WIDGET_ID %}
<div id="writeSkribitHere"></div>
<script src="http://assets.skribit.com/javascripts/SkribitWidget.js?renderTo=writeSkribitHere&amp;blog={{ SKRIBIT_WIDGET_ID }}&amp;cnt=5"></script>
<noscript>Sorry, but the
<a href="http://skribit.com" title="Skribit - Cure Writer's Block">Skribit</a> widget only works on browsers with JavaScript support.
<a href="http://skribit.com/blogs/think-different-think-open" title="Skribit Suggestions for Think Different, Think Open">View suggestions for this blog here.</a>
</noscript>
{% endif %}

View file

@ -0,0 +1,6 @@
{% if article.translations %}
Translations:
{% for translation in article.translations %}
<a href="{{ SITEURL }}/{{ translation.url }}">{{ translation.lang }}</a>
{% endfor %}
{% endif %}

View file

@ -4,7 +4,7 @@
<dl>
{% for article in dates %}
<dt>{{ article.date.strftime('%Y-%m-%d %H:%M') }}</dt>
<dt>{{ article.locale_date }}</dt>
<dd><a href='{{ article.url }}'>{{ article.title }}</a></dd>
{% endfor %}
</dl>

View file

@ -4,7 +4,7 @@
<header> <h2 class="entry-title"><a href="{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
{{ article.date.strftime('%Y-%m-%d %H:%M') }}
{{ article.locale_date }}
</abbr>
{% if article.author %}
<address class="vcard author">

View file

@ -11,7 +11,7 @@
</header><!-- /#banner -->
{% if categories %}<ul>
{% for category, articles in categories %}
<li><a href="category/{{category}}.html">{{ category }}</a></li>
<li><a href="{{ SITEURL }}/category/{{category}}.html">{{ category }}</a></li>
{% endfor %}
</ul> {% endif %}
{% block content %}

View file

@ -6,16 +6,29 @@
{% endblock %}
<ol id="post-list">
{% for article in articles %}
{% for article in articles_page.object_list %}
<li><article class="hentry">
<header> <h2 class="entry-title"><a href="{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.date.strftime('%Y-%m-%d %H:%M') }} </abbr>
<abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.locale_date }} </abbr>
{% if article.author %}<address class="vcard author">By <a class="url fn" href="#">{{ article.author }}</a></address>{% endif %}
</footer><!-- /.post-info -->
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
</article></li>
{% endfor %}
</ol><!-- /#posts-list -->
<p class="paginator">
{% if articles_page.has_previous() %}
{% if articles_page.previous_page_number() == 1 %}
<a href="{{ SITEURL }}/{{ page_name }}.html">&laquo;</a>
{% else %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html">&laquo;</a>
{% endif %}
{% endif %}
Page {{ articles_page.number }} / {{ articles_paginator.num_pages }}
{% if articles_page.has_next() %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.next_page_number() }}.html">&raquo;</a>
{% endif %}
</p>
</section><!-- /#content -->
{% endblock content %}

View file

@ -7,17 +7,6 @@ from codecs import open as _open
from itertools import groupby
from operator import attrgetter
def update_dict(mapping, key, value):
"""Update a dict intenal list
:param mapping: the mapping to update
:param key: the key of the mapping to update.
:param value: the value to append to the list.
"""
if key not in mapping:
mapping[key] = []
mapping[key].append(value)
def get_date(string):
"""Return a datetime object from a string.
@ -73,7 +62,7 @@ def clean_output_dir(path):
# remove all the existing content from the output folder
try:
shutil.rmtree(path)
except Exception as e:
except Exception:
pass
@ -159,6 +148,7 @@ def process_translations(content_list):
Also, for each content_list item, it
sets attribute 'translations'
"""
content_list.sort(key=attrgetter('slug'))
grouped_by_slugs = groupby(content_list, attrgetter('slug'))
index = []
translations = []
@ -184,3 +174,27 @@ def process_translations(content_list):
for a in items:
a.translations = filter(lambda x: x != a, items)
return index, translations
LAST_MTIME = 0
def files_changed(path, extensions):
"""Return True if the files have changed since the last check"""
def with_extension(f):
return True if True in [f.endswith(ext) for ext in extensions] else False
def file_times(path):
"""Return the last time files have been modified"""
for top_level in os.listdir(path):
for root, dirs, files in os.walk(top_level):
for file in filter(with_extension, files):
yield os.stat(os.path.join(root, file)).st_mtime
global LAST_MTIME
mtime = max(file_times(path))
if mtime > LAST_MTIME:
LAST_MTIME = mtime
return True
return False

View file

@ -1,15 +1,41 @@
# -*- coding: utf-8 -*-
import os
import re
from codecs import open
from functools import partial
import locale
from feedgenerator import Atom1Feed, Rss201rev2Feed
from pelican.utils import get_relative_path
from pelican.paginator import Paginator
class Writer(object):
def __init__(self, output_path):
def __init__(self, output_path, settings=None):
self.output_path = output_path
self.reminder = dict()
self.settings = settings or {}
def _create_new_feed(self, feed_type, context):
feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
feed = feed_class(
title=context['SITENAME'],
link=self.site_url,
feed_url=self.feed_url,
description=context.get('SITESUBTITLE', ''))
return feed
def _add_item_to_the_feed(self, feed, item):
feed.add_item(
title=item.title,
link='%s/%s' % (self.site_url, item.url),
description=item.content,
categories=item.tags if hasattr(item, 'tags') else None,
author_name=getattr(item, 'author', 'John Doe'),
pubdate=item.date)
def write_feed(self, elements, context, filename=None, feed_type='atom'):
"""Generate a feed with the list of articles provided
@ -23,58 +49,171 @@ class Writer(object):
:param filename: the filename to output.
:param feed_type: the feed type to use (atom or rss)
"""
site_url = context.get('SITEURL', get_relative_path(filename))
old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, 'C')
try:
self.site_url = context.get('SITEURL', get_relative_path(filename))
self.feed_url= '%s/%s' % (self.site_url, filename)
feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
feed = self._create_new_feed(feed_type, context)
feed = feed_class(
title=context['SITENAME'],
link=site_url,
feed_url= "%s/%s" % (site_url, filename),
description=context.get('SITESUBTITLE', ''))
for element in elements:
feed.add_item(
title=element.title,
link= "%s/%s" % (site_url, element.url),
description=element.content,
categories=element.tags if hasattr(element, "tags") else None,
author_name=getattr(element, 'author', 'John Doe'),
pubdate=element.date)
for item in elements:
self._add_item_to_the_feed(feed, item)
if filename:
complete_path = os.path.join(self.output_path, filename)
try:
os.makedirs(os.path.dirname(complete_path))
except Exception:
pass
fp = open(complete_path, 'w')
feed.write(fp, 'utf-8')
print u' [ok] writing %s' % complete_path
if filename:
complete_path = os.path.join(self.output_path, filename)
try:
os.makedirs(os.path.dirname(complete_path))
except Exception:
pass
fp = open(complete_path, 'w')
feed.write(fp, 'utf-8')
print u' [ok] writing %s' % complete_path
fp.close()
return feed
fp.close()
return feed
finally:
locale.setlocale(locale.LC_ALL, old_locale)
def write_file(self, name, template, context, relative_urls=True,
**kwargs):
paginated=None, **kwargs):
"""Render the template and write the file.
:param name: name of the file to output
:param template: template to use to generate the content
:param context: dict to pass to the templates.
:param relative_urls: use relative urls or absolutes ones
:param paginated: dict of article list to paginate - must have the
same length (same list in different orders)
:param **kwargs: additional variables to pass to the templates
"""
def _write_file(template, localcontext, output_path, name):
"""Render the template write the file."""
output = template.render(localcontext)
filename = os.sep.join((output_path, name))
try:
os.makedirs(os.path.dirname(filename))
except Exception:
pass
with open(filename, 'w', encoding='utf-8') as f:
f.write(output)
print u' [ok] writing %s' % filename
localcontext = context.copy()
if relative_urls:
localcontext['SITEURL'] = get_relative_path(name)
localcontext.update(kwargs)
output = template.render(localcontext)
filename = os.sep.join((self.output_path, name))
try:
os.makedirs(os.path.dirname(filename))
except Exception:
pass
with open(filename, 'w', encoding='utf-8') as f:
f.write(output)
print u' [ok] writing %s' % filename
self.update_context_contents(name, localcontext)
# check paginated
paginated = paginated or {}
if paginated:
# pagination needed, init paginators
paginators = {}
for key in paginated.iterkeys():
object_list = paginated[key]
if self.settings.get('WITH_PAGINATION'):
paginators[key] = Paginator(object_list,
self.settings.get('DEFAULT_PAGINATION'),
self.settings.get('DEFAULT_ORPHANS'))
else:
paginators[key] = Paginator(object_list, len(object_list), 0)
# generated pages, and write
for page_num in range(paginators.values()[0].num_pages):
paginated_localcontext = localcontext.copy()
paginated_name = name
for key in paginators.iterkeys():
paginator = paginators[key]
page = paginator.page(page_num+1)
paginated_localcontext.update({'%s_paginator' % key: paginator,
'%s_page' % key: page})
if page_num > 0:
ext = '.' + paginated_name.rsplit('.')[-1]
paginated_name = paginated_name.replace(ext,
'%s%s' % (page_num + 1, ext))
_write_file(template, paginated_localcontext, self.output_path,
paginated_name)
else:
# no pagination
_write_file(template, localcontext, self.output_path, name)
def update_context_contents(self, name, context):
"""Recursively run the context to find elements (articles, pages, etc)
whose content getter needs to
be modified in order to deal with relative paths.
:param name: name of the file to output.
:param context: dict that will be passed to the templates.
"""
if context is None:
return None
if type(context) == tuple:
context = list(context)
if type(context) == dict:
context = list(context.values())
for i in xrange(len(context)):
if type(context[i]) == tuple or type(context[i]) == list:
context[i] = self.update_context_contents(name, context[i])
elif type(context[i]) == dict:
context[i] = self.update_context_contents(name, context[i].values())
elif hasattr(context[i], '_content'):
relative_path = get_relative_path(name)
item = context[i]
if item in self.reminder:
if relative_path not in self.reminder[item]:
l = self.reminder[item]
l.append(relative_path)
self.inject_update_method(name, item)
else:
l = list(relative_path)
self.reminder[item] = l
self.inject_update_method(name, item)
return context
def inject_update_method(self, name, item):
"""Replace the content attribute getter of an element by a function
that will deals with its relatives paths.
"""
def _update_object_content(name, input):
"""Change all the relatives paths of the input content to relatives
paths suitable fot the ouput content
:param name: path of the output.
:param input: input resource that will be passed to the templates.
"""
content = input._content
hrefs = re.compile(r'<\s*[^\>]*href\s*=\s*(["\'])(.*?)\1')
srcs = re.compile(r'<\s*[^\>]*src\s*=\s*(["\'])(.*?)\1')
matches = hrefs.findall(content)
matches.extend(srcs.findall(content))
relative_paths = []
for found in matches:
found = found[1]
if found not in relative_paths:
relative_paths.append(found)
for relative_path in relative_paths:
if not "://" in relative_path: # we don't want to rewrite protocols
dest_path = os.sep.join((get_relative_path(name), "static",
relative_path))
content = content.replace(relative_path, dest_path)
return content
if item:
setattr(item, "_get_content",
partial(_update_object_content, name, item))

View file

@ -0,0 +1,7 @@
Trop bien !
###########
:lang: fr
:slug: oh-yeah
Et voila du contenu en français

View file

@ -1,13 +1,19 @@
Oh yeah !
#########################
#########
:tags: oh, bar, yeah
:date: 2010-10-20 10:14
:category: bar
:author: Alexis Métaireau
:slug: oh-yeah
Why not ?
=========
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !
.. image:: pictures/Sushi.jpg
:height: 450 px
:width: 600 px
:alt: alternate text

View file

@ -0,0 +1,6 @@
Article 1
#########
:date: 2011-02-17
Article 1

View file

@ -0,0 +1,6 @@
Article 2
#########
:date: 2011-02-17
Article 2

View file

@ -0,0 +1,6 @@
Article 3
#########
:date: 2011-02-17
Article 3

View file

@ -0,0 +1,12 @@
This is a test page
###################
:category: test
Just an image.
.. image:: pictures/Fat_Cat.jpg
:height: 450 px
:width: 600 px
:alt: alternate text

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -2,21 +2,33 @@ This is a super article !
#########################
:tags: foo, bar, foobar
:date: 2010-10-02 10:14
:date: 2010-12-02 10:14
:category: yeah
:author: Alexis Métaireau
:summary: This is a simple test
Some content here !
This is a simple title
======================
And here comes the cool stuff.
And here comes the cool stuff_.
.. image:: pictures/Sushi.jpg
:height: 450 px
:width: 600 px
:alt: alternate text
.. image:: pictures/Sushi_Macro.jpg
:height: 450 px
:width: 600 px
:alt: alternate text
.. code-block:: python
>>> from ipdb import set trace
>>> from ipdb import set_trace
>>> set_trace()
→ And now try with some utf8 hell: ééé
.. _stuff: http://books.couchdb.org/relax/design-documents/views

View file

@ -6,6 +6,12 @@ SITEURL = 'http://blog.notmyidea.org'
GITHUB_URL = 'http://github.com/ametaireau/'
DISQUS_SITENAME = "blog-notmyidea"
PDF_GENERATOR = False
REVERSE_CATEGORY_ORDER = True
LOCALE = 'fr_FR.utf8'
DEFAULT_PAGINATION = 2
FEED_RSS = 'feeds/all.rss.xml'
CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
LINKS = (('Biologeek', 'http://biologeek.org'),
('Filyb', "http://filyb.info/"),
@ -17,3 +23,5 @@ LINKS = (('Biologeek', 'http://biologeek.org'),
SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
('lastfm', 'http://lastfm.com/user/akounet'),
('github', 'http://github.com/ametaireau'),)
STATIC_PATHS = ["pictures",]

View file

@ -1,7 +1,7 @@
from setuptools import setup
import sys
VERSION = "2.5.3" # find a better way to do so.
VERSION = "2.6.0" # find a better way to do so.
requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'Markdown']
if sys.version_info < (2,7):

121
tools/importer.py Executable file
View file

@ -0,0 +1,121 @@
#! /usr/bin/env python
from pelican.utils import slugify
from codecs import open
import os
import argparse
import time
def wp2fields(xml):
"""Opens a wordpress XML file, and yield pelican fields"""
from BeautifulSoup import BeautifulStoneSoup
xmlfile = open(xml, encoding='utf-8').read()
soup = BeautifulStoneSoup(xmlfile)
items = soup.rss.channel.findAll('item')
for item in items:
if item.fetch('wp:status')[0].contents[0] == "publish":
title = item.title.contents[0]
content = item.fetch('content:encoded')[0].contents[0]
filename = item.fetch('wp:post_name')[0].contents[0]
raw_date = item.fetch('wp:post_date')[0].contents[0]
date_object = time.strptime(raw_date, "%Y-%m-%d %H:%M:%S")
date = time.strftime("%Y-%m-%d %H:%M", date_object)
author = item.fetch('dc:creator')[0].contents[0].title()
categories = [(cat['nicename'],cat.contents[0]) for cat in item.fetch(domain='category')]
tags = [tag.contents[0].title() for tag in item.fetch(domain='tag', nicename=None)]
yield (title, content, filename, date, author, categories, tags)
def feed2fields(file):
"""Read a feed and yield pelican fields"""
import feedparser
d = feedparser.parse(file)
for entry in d.entries:
date = (time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed)
if hasattr(entry, "updated_parsed") else None)
author = entry.author if hasattr(entry, "author") else None
tags = [e['term'] for e in entry.tags] if hasattr(entry, "tags") else None
slug = slugify(entry.title)
yield (entry.title, entry.description, slug, date, author, [], tags)
def build_header(title, date, author, categories, tags):
"""Build a header from a list of fields"""
header = '%s\n%s\n' % (title, '#' * len(title))
if date:
header += ':date: %s\n' % date
if categories:
header += ':category: %s\n' % ', '.join(categories)
if tags:
header += ':tags: %s\n' % ', '.join(tags)
header += '\n'
return header
def fields2pelican(fields, output_path):
for title, content, filename, date, author, categories, tags in fields:
html_filename = os.path.join(output_path, filename+'.html')
if(len(categories) == 1):
rst_filename = os.path.join(output_path, categories[0][0], filename+'.rst')
if not os.path.isdir(os.path.join(output_path, categories[0][0])):
os.mkdir(os.path.join(output_path, categories[0][0]))
else:
rst_filename = os.path.join(output_path, filename+'.rst')
with open(html_filename, 'w', encoding='utf-8') as fp:
fp.write(content)
os.system('pandoc --from=html --to=rst -o %s %s' % (rst_filename,
html_filename))
os.remove(html_filename)
with open(rst_filename, 'r', encoding='utf-8') as fs:
content = fs.read()
with open(rst_filename, 'w', encoding='utf-8') as fs:
categories = [x[1] for x in categories]
header = build_header(title, date, author, categories, tags)
fs.write(header + content)
def main(input_type, input, output_path):
if input_type == 'wordpress':
fields = wp2fields(input)
elif input_type == 'feed':
fields = feed2fields(input)
fields2pelican(fields, output_path)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Transform even feed or XML files to rst files."
"Be sure to have pandoc installed")
parser.add_argument(dest='input', help='The input file to read')
parser.add_argument('--wpfile', action='store_true', dest='wpfile',
help='Wordpress XML export')
parser.add_argument('--feed', action='store_true', dest='feed',
help='feed to parse')
parser.add_argument('-o', '--output', dest='output', default='output',
help='Output path')
args = parser.parse_args()
input_type = None
if args.wpfile:
input_type = 'wordpress'
elif args.feed:
input_type = 'feed'
else:
print "you must provide either --wpfile or --feed options"
exit()
main(input_type, args.input, args.output)

View file

@ -1,34 +0,0 @@
#! /usr/bin/env python
from BeautifulSoup import BeautifulStoneSoup
from codecs import open
import os
import argparse
def wp2html(xml):
xmlfile = open(xml, encoding='utf-8').read()
soup = BeautifulStoneSoup(xmlfile)
items = soup.rss.channel.findAll('item')
for item in items:
if item.fetch('wp:status')[0].contents[0] == "publish":
title = item.title.contents[0]
content = item.fetch('content:encoded')[0].contents[0]
filename = item.fetch('wp:post_name')[0].contents[0]
yield (title, content, filename)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="""Transform a wordpress xml export into rst files """)
parser.add_argument(dest='xml', help='The xml filepath')
parser.add_argument('-o', '--output', dest='output', default='output', help='Output path')
args = parser.parse_args()
for title, content, filename in wp2html(args.xml):
html_filename = os.path.join(args.output, filename+'.html')
rst_filename = os.path.join(args.output, filename+'.rst')
with open(html_filename, 'w', encoding='utf-8') as fp:
fp.write(content)
os.system('pandoc --from=html --to=rst -o %s %s' % (rst_filename,
html_filename))