mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge branch 'master' of https://github.com/ametaireau/pelican
This commit is contained in:
commit
8a3752de81
34 changed files with 613 additions and 284 deletions
10
CHANGELOG
10
CHANGELOG
|
|
@ -1,10 +1,18 @@
|
|||
X.X
|
||||
3.0 - XX/XX/XXXX
|
||||
|
||||
* Refactored the way URL are handled.
|
||||
* Improved the english documentation
|
||||
* Fixed packaging using setuptools entrypoints
|
||||
* Added typogrify support
|
||||
* Added a way to disable feed generation
|
||||
* Added support for DIRECT_TEMPLATES
|
||||
* Allow multiple extensions for content files
|
||||
* Added less support
|
||||
* Improved the import script
|
||||
* Fixed a bunch of bugs :-)
|
||||
* Added functional tests
|
||||
* Rsync support in the generated Makefile
|
||||
* Improved feed support (easily pluggable with feedburner for instance)
|
||||
|
||||
2.8
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
*Foire aux questions (FAQ)*
|
||||
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 ?*
|
||||
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
|
||||
|
|
@ -11,17 +13,20 @@ 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?*
|
||||
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*
|
||||
Comment puis-je créer mon propre thèm
|
||||
=====================================
|
||||
|
||||
Vueillez vous référer à :ref:`theming-pelican-fr`.
|
||||
|
||||
*Comment puis-je aider?*
|
||||
Comment puis-je aider?
|
||||
======================
|
||||
|
||||
Vous avez plusieurs options pour aider. Tout d'abord, vous pouvez utiliser
|
||||
le
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ For RestructuredText::
|
|||
|
||||
For Markdown, format your code blocks thusly::
|
||||
|
||||
::identifier
|
||||
:::identifier
|
||||
your code goes here
|
||||
|
||||
The specified identifier should be one that appears on the
|
||||
|
|
|
|||
|
|
@ -19,15 +19,31 @@ The conversion from HTML to reStructuredText relies on `pandoc
|
|||
written with Markdown syntax, they will not be converted (as Pelican also
|
||||
supports Markdown).
|
||||
|
||||
Dependencies
|
||||
""""""""""""
|
||||
|
||||
``pelican-import`` has two dependencies not required by the rest of pelican:
|
||||
|
||||
- BeautifulSoup
|
||||
- pandoc
|
||||
|
||||
BeatifulSoup can be installed like any other Python package::
|
||||
|
||||
$ pip install BeautifulSoup
|
||||
|
||||
For pandoc, install a package for your operating system from the
|
||||
`pandoc site <http://johnmacfarlane.net/pandoc/installing.html>`_.
|
||||
|
||||
|
||||
Usage
|
||||
"""""
|
||||
|
||||
| pelican-import [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT]
|
||||
| [--dir-cat]
|
||||
| [-m MARKUP][--dir-cat]
|
||||
| input
|
||||
|
||||
Optional arguments:
|
||||
"""""""""""""""""""
|
||||
Optional arguments
|
||||
""""""""""""""""""
|
||||
|
||||
-h, --help show this help message and exit
|
||||
--wpfile Wordpress XML export
|
||||
|
|
@ -35,6 +51,7 @@ Optional arguments:
|
|||
--feed Feed to parse
|
||||
-o OUTPUT, --output OUTPUT
|
||||
Output path
|
||||
-m MARKUP Output markup
|
||||
--dir-cat Put files in directories with categories name
|
||||
|
||||
Examples
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Pelican currently supports:
|
|||
* Publication of articles in multiple languages
|
||||
* Atom/RSS feeds
|
||||
* Code syntax highlighting
|
||||
* Compilation of less css (optional)
|
||||
* Import from WordPress, Dotclear, or RSS feeds
|
||||
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,71 +21,81 @@ Here is a list of settings for Pelican:
|
|||
Basic settings
|
||||
==============
|
||||
|
||||
================================================ =====================================================
|
||||
Setting name (default value) What does it do?
|
||||
================================================ =====================================================
|
||||
`AUTHOR` Default author (put your name)
|
||||
`DATE_FORMATS` (``{}``) If you do manage multiple languages, you can
|
||||
set the date formatting here. See "Date format and locales"
|
||||
section below for details.
|
||||
`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on.
|
||||
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
|
||||
`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the
|
||||
template. Templates may or not honor this
|
||||
setting.
|
||||
`FALLBACK_ON_FS_DATE` (``True``) If True, Pelican will use the file system
|
||||
timestamp information (mtime) if it can't get
|
||||
date information from the metadata.
|
||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory as well as
|
||||
the generated files.
|
||||
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
||||
here or a single string representing one locale.
|
||||
When providing a list, all the locales will be tried
|
||||
until one works.
|
||||
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
|
||||
to use. For the moment, the only available values
|
||||
are `rst` and `md`.
|
||||
`MD_EXTENSIONS` (``['codehilite','extra']``) A list of the extensions that the Markdown processor
|
||||
will use. Refer to the extensions chapter in the
|
||||
Python-Markdown documentation for a complete list of
|
||||
supported extensions.
|
||||
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
|
||||
`PATH` (``None``) Path to look at for input files.
|
||||
`PAGE_DIR' (``'pages'``) Directory to look at for pages.
|
||||
`PAGE_EXCLUDES' (``()``) A list of directories to exclude when looking for pages.
|
||||
`ARTICLE_DIR' (``''``) Directory to look at for articles.
|
||||
`ARTICLE_EXCLUDES': (``('pages',)``) A list of directories to exclude when looking for articles.
|
||||
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
||||
of your documents. You will need to install
|
||||
`rst2pdf`.
|
||||
`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or
|
||||
not.
|
||||
`SITENAME` (``'A Pelican Blog'``) Your site name
|
||||
`SITEURL` Base URL of your website. Not defined by default,
|
||||
which means the base URL is assumed to be "/" with a
|
||||
root-relative URL structure. If `SITEURL` is specified
|
||||
explicitly, there should be no trailing slash at the end,
|
||||
and URLs will be generated with an absolute URL structure
|
||||
(including the domain). If you want to use relative URLs
|
||||
instead of root-relative or absolute URLs, you should
|
||||
instead use the `RELATIVE_URL` setting.
|
||||
`STATIC_PATHS` (``['images']``) The static paths you want to have accessible
|
||||
on the output path "static". By default,
|
||||
Pelican will copy the 'images' folder to the
|
||||
output folder.
|
||||
`TIMEZONE` The timezone used in the date information, to
|
||||
generate Atom and RSS feeds. See the "timezone"
|
||||
section below for more info.
|
||||
`TYPOGRIFY` (``False``) If set to true, some
|
||||
additional transformations will be done on the
|
||||
generated HTML, using the `Typogrify
|
||||
<http://static.mintchaos.com/projects/typogrify/>`_
|
||||
library
|
||||
================================================ =====================================================
|
||||
===================================================================== =====================================================================
|
||||
Setting name (default value) What does it do?
|
||||
===================================================================== =====================================================================
|
||||
`AUTHOR` Default author (put your name)
|
||||
`DATE_FORMATS` (``{}``) If you do manage multiple languages, you can
|
||||
set the date formatting here. See "Date format and locales"
|
||||
section below for details.
|
||||
`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on.
|
||||
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
|
||||
`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the
|
||||
template. Templates may or not honor this
|
||||
setting.
|
||||
`FALLBACK_ON_FS_DATE` (``True``) If True, Pelican will use the file system
|
||||
timestamp information (mtime) if it can't get
|
||||
date information from the metadata.
|
||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
||||
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory as well as
|
||||
the generated files.
|
||||
`LOCALE` (''[#]_) Change the locale. A list of locales can be provided
|
||||
here or a single string representing one locale.
|
||||
When providing a list, all the locales will be tried
|
||||
until one works.
|
||||
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
|
||||
to use. For the moment, the only available values
|
||||
are `rst` and `md`.
|
||||
`MD_EXTENSIONS` (``['codehilite','extra']``) A list of the extensions that the Markdown processor
|
||||
will use. Refer to the extensions chapter in the
|
||||
Python-Markdown documentation for a complete list of
|
||||
supported extensions.
|
||||
`OUTPUT_PATH` (``'output/'``) Where to output the generated files.
|
||||
`PATH` (``None``) Path to look at for input files.
|
||||
`PAGE_DIR` (``'pages'``) Directory to look at for pages.
|
||||
`PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages.
|
||||
`ARTICLE_DIR` (``''``) Directory to look at for articles.
|
||||
`ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles.
|
||||
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
|
||||
of your documents. You will need to install
|
||||
`rst2pdf`.
|
||||
`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or
|
||||
not.
|
||||
`SITENAME` (``'A Pelican Blog'``) Your site name
|
||||
`SITEURL` Base URL of your website. Not defined by default,
|
||||
which means the base URL is assumed to be "/" with a
|
||||
root-relative URL structure. If `SITEURL` is specified
|
||||
explicitly, there should be no trailing slash at the end,
|
||||
and URLs will be generated with an absolute URL structure
|
||||
(including the domain). If you want to use relative URLs
|
||||
instead of root-relative or absolute URLs, you should
|
||||
instead use the `RELATIVE_URL` setting.
|
||||
`STATIC_PATHS` (``['images']``) The static paths you want to have accessible
|
||||
on the output path "static". By default,
|
||||
Pelican will copy the 'images' folder to the
|
||||
output folder.
|
||||
`TIMEZONE` The timezone used in the date information, to
|
||||
generate Atom and RSS feeds. See the "timezone"
|
||||
section below for more info.
|
||||
`TYPOGRIFY` (``False``) If set to true, some
|
||||
additional transformations will be done on the
|
||||
generated HTML, using the `Typogrify
|
||||
<http://static.mintchaos.com/projects/typogrify/>`_
|
||||
library
|
||||
`LESS_GENERATOR` (``FALSE``) Set to True or complete path to `lessc` (if not
|
||||
found in system PATH) to enable compiling less
|
||||
css files. Requires installation of `less css`_.
|
||||
`DIRECT_TEMPLATES` (``('index', 'tags', 'categories', 'archives')``) List of templates that are used directly to render
|
||||
content. Typically direct templates are used to generate
|
||||
index pages for collections of content e.g. tags and
|
||||
category index pages.
|
||||
`PAGINATED_DIRECT_TEMPLATES` (``('index',)``) Provides the direct templates that should be paginated.
|
||||
===================================================================== =====================================================================
|
||||
|
||||
.. [#] Default is the system locale.
|
||||
|
||||
.. _less css: http://lesscss.org/
|
||||
|
||||
|
||||
URL settings
|
||||
------------
|
||||
|
|
@ -96,14 +106,15 @@ your articles in a location such as '{slug}/index.html' and link to them as
|
|||
'{slug}' for clean URLs. These settings give you the flexibility to place your
|
||||
articles and pages anywhere you want.
|
||||
|
||||
Note: If you specify a datetime directive, it will be substituted using the
|
||||
input files' date metadata attribute. If the date is not specified for a
|
||||
particular file, Pelican will rely on the file's mtime timestamp.
|
||||
.. note::
|
||||
If you specify a datetime directive, it will be substituted using the
|
||||
input files' date metadata attribute. If the date is not specified for a
|
||||
particular file, Pelican will rely on the file's mtime timestamp.
|
||||
|
||||
Check the Python datetime documentation at http://bit.ly/cNcJUC for more
|
||||
information.
|
||||
|
||||
Also, you can use other file metadata attributes as well:
|
||||
Also, you can use other file metadata attributes as well:
|
||||
|
||||
* slug
|
||||
* date
|
||||
|
|
@ -111,7 +122,7 @@ Also, you can use other file metadata attributes as well:
|
|||
* author
|
||||
* category
|
||||
|
||||
Example usage:
|
||||
Example usage:
|
||||
|
||||
* ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'
|
||||
* ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'
|
||||
|
|
@ -140,8 +151,15 @@ Setting name (default value) what does it do?
|
|||
`CATEGORY_SAVE_AS` ('category/{name}.html') The location to save a category.
|
||||
`TAG_URL` ('tag/{name}.html') The URL to use for a tag.
|
||||
`TAG_SAVE_AS` ('tag/{name}.html') The location to save the tag page.
|
||||
`<DIRECT_TEMPLATE_NAME>_SAVE_AS` The location to save content generated from direct
|
||||
templates. Where <DIRECT_TEMPLATE_NAME> is the
|
||||
upper case template name.
|
||||
================================================ =====================================================
|
||||
|
||||
.. note::
|
||||
|
||||
When any of `*_SAVE_AS` is set to False, files will not be created.
|
||||
|
||||
Timezone
|
||||
--------
|
||||
|
||||
|
|
@ -330,7 +348,7 @@ Setting name (default value) What does it do?
|
|||
================================================ =====================================================
|
||||
`REVERSE_ARCHIVE_ORDER` (``False``) Reverse the archives list order. (True: orders by date
|
||||
in descending order, with newer articles first.)
|
||||
`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True: lists by reverse
|
||||
`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True: lists by reverse
|
||||
alphabetical order; default lists alphabetically.)
|
||||
================================================ =====================================================
|
||||
|
||||
|
|
@ -361,8 +379,7 @@ By default, two themes are available. You can specify them using the `-t` option
|
|||
You can define your own theme too, and specify its placement in the same
|
||||
manner. (Be sure to specify the full absolute path to it.)
|
||||
|
||||
Here is `a guide on how to create your theme
|
||||
<http://pelican.notmyidea.org/en/latest/themes.html>`_
|
||||
Here is :doc:`a guide on how to create your theme <themes>`
|
||||
|
||||
You can find a list of themes at http://github.com/ametaireau/pelican-themes.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import logging
|
|||
import argparse
|
||||
|
||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||
StaticGenerator, PdfGenerator)
|
||||
StaticGenerator, PdfGenerator, LessCSSGenerator)
|
||||
from pelican.log import init
|
||||
from pelican.settings import read_settings, _DEFAULT_CONFIG
|
||||
from pelican.utils import clean_output_dir, files_changed
|
||||
|
|
@ -61,8 +61,9 @@ class Pelican(object):
|
|||
def _handle_deprecation(self):
|
||||
|
||||
if self.settings.get('CLEAN_URLS', False):
|
||||
logger.warning('Found deprecated `CLEAN_URLS` in settings. Modifing'
|
||||
' the following settings for the same behaviour.')
|
||||
logger.warning('Found deprecated `CLEAN_URLS` in settings.'
|
||||
' Modifying the following settings for the'
|
||||
' same behaviour.')
|
||||
|
||||
self.settings['ARTICLE_URL'] = '{slug}/'
|
||||
self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
|
||||
|
|
@ -75,7 +76,7 @@ class Pelican(object):
|
|||
|
||||
if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
|
||||
logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
|
||||
' settings. Modifing the following settings for'
|
||||
' settings. Modifying the following settings for'
|
||||
' the same behaviour.')
|
||||
|
||||
structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
|
||||
|
|
@ -133,6 +134,8 @@ class Pelican(object):
|
|||
generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
|
||||
if self.settings['PDF_GENERATOR']:
|
||||
generators.append(PdfGenerator)
|
||||
if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc
|
||||
generators.append(LessCSSGenerator)
|
||||
return generators
|
||||
|
||||
def get_writer(self):
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class Page(object):
|
|||
for key, value in local_metadata.items():
|
||||
setattr(self, key.lower(), value)
|
||||
|
||||
# also keep track of the metadata attributes available
|
||||
self.metadata = local_metadata
|
||||
|
||||
# default author to the one in settings if not defined
|
||||
if not hasattr(self, 'author'):
|
||||
if 'AUTHOR' in settings:
|
||||
|
|
@ -60,7 +63,7 @@ class Page(object):
|
|||
|
||||
self.in_default_lang = (self.lang == default_lang)
|
||||
|
||||
# create the slug if not existing, fro mthe title
|
||||
# create the slug if not existing, from the title
|
||||
if not hasattr(self, 'slug') and hasattr(self, 'title'):
|
||||
self.slug = slugify(self.title)
|
||||
|
||||
|
|
@ -132,7 +135,7 @@ class Page(object):
|
|||
|
||||
def _get_summary(self):
|
||||
"""Returns the summary of an article, based on the summary metadata
|
||||
if it is set, else troncate the content."""
|
||||
if it is set, else truncate the content."""
|
||||
if hasattr(self, '_summary'):
|
||||
return self._summary
|
||||
else:
|
||||
|
|
@ -180,7 +183,12 @@ class URLWrapper(object):
|
|||
|
||||
def _from_settings(self, key):
|
||||
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
|
||||
return unicode(self.settings[setting]).format(**self.as_dict())
|
||||
value = self.settings[setting]
|
||||
if not isinstance(value, basestring):
|
||||
logger.warning(u'%s is set to %s' % (setting, value))
|
||||
return value
|
||||
else:
|
||||
return unicode(value).format(**self.as_dict())
|
||||
|
||||
url = property(functools.partial(_from_settings, key='URL'))
|
||||
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import math
|
|||
import random
|
||||
import logging
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
|
|
@ -162,31 +163,32 @@ class ArticlesGenerator(Generator):
|
|||
writer.write_feed(items, self.context,
|
||||
self.settings['TRANSLATION_FEED'] % lang)
|
||||
|
||||
def generate_pages(self, writer):
|
||||
"""Generate the pages on the disk"""
|
||||
|
||||
write = partial(writer.write_file,
|
||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||
|
||||
# to minimize the number of relative path stuff modification
|
||||
# in writer, articles pass first
|
||||
def generate_articles(self, write):
|
||||
"""Generate the articles."""
|
||||
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)
|
||||
|
||||
def generate_direct_templates(self, write):
|
||||
"""Generate direct templates pages"""
|
||||
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}
|
||||
save_as = self.settings.get("%s_SAVE_AS" % template.upper(),
|
||||
'%s.html' % template)
|
||||
if not save_as:
|
||||
continue
|
||||
|
||||
write('%s.html' % template, self.get_template(template),
|
||||
write(save_as, self.get_template(template),
|
||||
self.context, blog=True, paginated=paginated,
|
||||
page_name=template)
|
||||
|
||||
# and subfolders after that
|
||||
def generate_tags(self, write):
|
||||
"""Generate Tags pages."""
|
||||
tag_template = self.get_template('tag')
|
||||
for tag, articles in self.tags.items():
|
||||
articles.sort(key=attrgetter('date'), reverse=True)
|
||||
|
|
@ -196,6 +198,8 @@ class ArticlesGenerator(Generator):
|
|||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name=u'tag/%s' % tag)
|
||||
|
||||
def generate_categories(self, write):
|
||||
"""Generate category pages."""
|
||||
category_template = self.get_template('category')
|
||||
for cat, articles in self.categories:
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
|
|
@ -204,6 +208,8 @@ class ArticlesGenerator(Generator):
|
|||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name=u'category/%s' % cat)
|
||||
|
||||
def generate_authors(self, write):
|
||||
"""Generate Author pages."""
|
||||
author_template = self.get_template('author')
|
||||
for aut, articles in self.authors:
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
|
|
@ -212,10 +218,30 @@ class ArticlesGenerator(Generator):
|
|||
paginated={'articles': articles, 'dates': dates},
|
||||
page_name=u'author/%s' % aut)
|
||||
|
||||
def generate_drafts(self, write):
|
||||
"""Generate drafts pages."""
|
||||
article_template = self.get_template('article')
|
||||
for article in self.drafts:
|
||||
write('drafts/%s.html' % article.slug, article_template,
|
||||
self.context, article=article, category=article.category)
|
||||
|
||||
def generate_pages(self, writer):
|
||||
"""Generate the pages on the disk"""
|
||||
write = partial(writer.write_file,
|
||||
relative_urls=self.settings.get('RELATIVE_URLS'))
|
||||
|
||||
# to minimize the number of relative path stuff modification
|
||||
# in writer, articles pass first
|
||||
self.generate_articles(write)
|
||||
self.generate_direct_templates(write)
|
||||
|
||||
|
||||
# and subfolders after that
|
||||
self.generate_tags(write)
|
||||
self.generate_categories(write)
|
||||
self.generate_authors(write)
|
||||
self.generate_drafts(write)
|
||||
|
||||
def generate_context(self):
|
||||
"""change the context"""
|
||||
|
||||
|
|
@ -302,10 +328,12 @@ class ArticlesGenerator(Generator):
|
|||
|
||||
# order the categories per name
|
||||
self.categories = list(self.categories.items())
|
||||
self.categories.sort(reverse=self.settings['REVERSE_CATEGORY_ORDER'])
|
||||
self.categories.sort(
|
||||
key=lambda item: item[0].name,
|
||||
reverse=self.settings['REVERSE_CATEGORY_ORDER'])
|
||||
|
||||
self.authors = list(self.authors.items())
|
||||
self.authors.sort()
|
||||
self.authors.sort(key=lambda item: item[0].name)
|
||||
|
||||
self._update_context(('articles', 'dates', 'tags', 'categories',
|
||||
'tag_cloud', 'authors'))
|
||||
|
|
@ -351,7 +379,7 @@ class PagesGenerator(Generator):
|
|||
|
||||
|
||||
class StaticGenerator(Generator):
|
||||
"""copy static paths (what you want to cpy, like images, medias etc.
|
||||
"""copy static paths (what you want to copy, like images, medias etc.
|
||||
to output"""
|
||||
|
||||
def _copy_paths(self, paths, source, destination, output_path,
|
||||
|
|
@ -414,3 +442,50 @@ class PdfGenerator(Generator):
|
|||
|
||||
for page in self.context['pages']:
|
||||
self._create_pdf(page, pdf_path)
|
||||
|
||||
|
||||
class LessCSSGenerator(Generator):
|
||||
"""Compile less css files."""
|
||||
|
||||
def _compile(self, less_file, source_dir, dest_dir):
|
||||
base = os.path.relpath(less_file, source_dir)
|
||||
target = os.path.splitext(
|
||||
os.path.join(dest_dir, base))[0] + '.css'
|
||||
target_dir = os.path.dirname(target)
|
||||
|
||||
if not os.path.exists(target_dir):
|
||||
try:
|
||||
os.makedirs(target_dir)
|
||||
except OSError:
|
||||
logger.error("Couldn't create the less css output folder in " +
|
||||
target_dir)
|
||||
|
||||
subprocess.call([self._lessc, less_file, target])
|
||||
logger.info(u' [ok] compiled %s' % base)
|
||||
|
||||
def generate_output(self, writer=None):
|
||||
logger.info(u' Compiling less css')
|
||||
|
||||
# store out compiler here, so it won't be evaulted on each run of
|
||||
# _compile
|
||||
lg = self.settings['LESS_GENERATOR']
|
||||
self._lessc = lg if isinstance(lg, basestring) else 'lessc'
|
||||
|
||||
# walk static paths
|
||||
for static_path in self.settings['STATIC_PATHS']:
|
||||
for f in self.get_files(
|
||||
os.path.join(self.path, static_path),
|
||||
extensions=['less']):
|
||||
|
||||
self._compile(f, self.path, self.output_path)
|
||||
|
||||
# walk theme static paths
|
||||
theme_output_path = os.path.join(self.output_path, 'theme')
|
||||
|
||||
for static_path in self.settings['THEME_STATIC_PATHS']:
|
||||
theme_static_path = os.path.join(self.theme, static_path)
|
||||
for f in self.get_files(
|
||||
theme_static_path,
|
||||
extensions=['less']):
|
||||
|
||||
self._compile(f, theme_static_path, theme_output_path)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ def render_node_to_html(document, node):
|
|||
|
||||
class RstReader(Reader):
|
||||
enabled = bool(docutils)
|
||||
extension = "rst"
|
||||
file_extensions = ['rst']
|
||||
|
||||
def _parse_metadata(self, document):
|
||||
"""Return the dict containing document metadata"""
|
||||
|
|
@ -111,7 +111,7 @@ class RstReader(Reader):
|
|||
|
||||
class MarkdownReader(Reader):
|
||||
enabled = bool(Markdown)
|
||||
extension = "md"
|
||||
file_extensions = ['md', 'markdown', 'mkd']
|
||||
extensions = ['codehilite', 'extra']
|
||||
|
||||
def read(self, filename):
|
||||
|
|
@ -128,7 +128,7 @@ class MarkdownReader(Reader):
|
|||
|
||||
|
||||
class HtmlReader(Reader):
|
||||
extension = "html"
|
||||
file_extensions = ['html', 'htm']
|
||||
_re = re.compile('\<\!\-\-\#\s?[A-z0-9_-]*\s?\:s?[A-z0-9\s_-]*\s?\-\-\>')
|
||||
|
||||
def read(self, filename):
|
||||
|
|
@ -144,7 +144,11 @@ class HtmlReader(Reader):
|
|||
return content, metadata
|
||||
|
||||
|
||||
_EXTENSIONS = dict((cls.extension, cls) for cls in Reader.__subclasses__())
|
||||
_EXTENSIONS = {}
|
||||
|
||||
for cls in Reader.__subclasses__():
|
||||
for ext in cls.file_extensions:
|
||||
_EXTENSIONS[ext] = cls
|
||||
|
||||
|
||||
def read_file(filename, fmt=None, settings=None):
|
||||
|
|
@ -170,5 +174,6 @@ def read_file(filename, fmt=None, settings=None):
|
|||
if settings and settings['TYPOGRIFY']:
|
||||
from typogrify import Typogrify
|
||||
content = Typogrify.typogrify(content)
|
||||
metadata['title'] = Typogrify.typogrify(metadata['title'])
|
||||
|
||||
return content, metadata
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
|||
'PAGE_SAVE_AS': 'pages/{slug}.html',
|
||||
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
|
||||
'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html',
|
||||
'CATEGORY_URL': 'category/{name}.html',
|
||||
'CATEGORY_SAVE_AS': 'category/{name}.html',
|
||||
'CATEGORY_URL': 'category/{slug}.html',
|
||||
'CATEGORY_SAVE_AS': 'category/{slug}.html',
|
||||
'TAG_URL': 'tag/{slug}.html',
|
||||
'TAG_SAVE_AS': 'tag/{slug}.html',
|
||||
'AUTHOR_URL': u'author/{slug}.html',
|
||||
|
|
@ -67,6 +67,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
|||
'DEFAULT_STATUS': 'published',
|
||||
'ARTICLE_PERMALINK_STRUCTURE': '',
|
||||
'TYPOGRIFY': False,
|
||||
'LESS_GENERATOR': False,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -130,7 +131,7 @@ def configure_settings(settings, default_settings=None, filename=None):
|
|||
if ('SITEURL' in settings):
|
||||
# If SITEURL has a trailing slash, remove it and provide a warning
|
||||
siteurl = settings['SITEURL']
|
||||
if (siteurl[len(siteurl) - 1:] == '/'):
|
||||
if (siteurl.endswith('/')):
|
||||
settings['SITEURL'] = siteurl[:-1]
|
||||
logger.warn("Removed extraneous trailing slash from SITEURL.")
|
||||
# If SITEURL is defined but FEED_DOMAIN isn't, set FEED_DOMAIN = SITEURL
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@ body {
|
|||
font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
|
||||
}
|
||||
|
||||
.body, #banner nav, #banner nav ul, #about, #featured, #content{
|
||||
width: inherit;
|
||||
.post-info{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#banner nav {
|
||||
display: none;
|
||||
-moz-border-radius: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
font-size: 1em;
|
||||
background: #F5F4EF;
|
||||
}
|
||||
|
||||
#banner nav ul{
|
||||
|
|
@ -19,10 +23,11 @@ body {
|
|||
|
||||
#banner nav li{
|
||||
float: right;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#banner nav li:first-child a {
|
||||
-moz-border-radius: 0px;
|
||||
#banner nav li a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#banner h1 {
|
||||
|
|
|
|||
BIN
pelican/themes/notmyidea/static/images/icons/facebook.png
Normal file
BIN
pelican/themes/notmyidea/static/images/icons/facebook.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 300 B |
|
|
@ -6,7 +6,7 @@
|
|||
<dl>
|
||||
{% for article in dates %}
|
||||
<dt>{{ article.locale_date }}</dt>
|
||||
<dd><a href="{{ article.url }}">{{ article.title }}</a></dd>
|
||||
<dd><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,34 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}{{ article.title }}{% endblock %}
|
||||
{% block content %}
|
||||
<section id="content" class="body">
|
||||
<article>
|
||||
<header> <h1 class="entry-title"><a href="{{ pagename }}"
|
||||
rel="bookmark" title="Permalink to {{ article.title }}">{{ article.title
|
||||
}}</a></h1> {% include 'twitter.html' %} </header>
|
||||
<div class="entry-content">
|
||||
{% include 'article_infos.html' %}
|
||||
{{ article.content }}
|
||||
</div><!-- /.entry-content -->
|
||||
{% if DISQUS_SITENAME %}
|
||||
<div class="comments">
|
||||
<h2>Comments !</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 %}
|
||||
{% block title %}{{ article.title|striptags }}{% endblock %}
|
||||
{% block content %}
|
||||
<section id="content" class="body">
|
||||
<article>
|
||||
<header>
|
||||
<h1 class="entry-title">
|
||||
<a href="{{ article.url }}" rel="bookmark"
|
||||
title="Permalink to {{ article.title|striptags }}">{{ article.title}}</a></h1>
|
||||
{% include 'twitter.html' %}
|
||||
</header>
|
||||
|
||||
</article>
|
||||
<div class="entry-content">
|
||||
{% include 'article_infos.html' %}
|
||||
{{ article.content }}
|
||||
</div><!-- /.entry-content -->
|
||||
{% if DISQUS_SITENAME %}
|
||||
<div class="comments">
|
||||
<h2>Comments !</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 %}
|
||||
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for cat, null in categories %}
|
||||
<li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/category/{{ cat }}.html">{{ cat }}</a></li>
|
||||
<li {% if cat == category %}class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
|
||||
{% endfor %}
|
||||
</ul></nav>
|
||||
</header><!-- /#banner -->
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for category, articles in categories %}
|
||||
<li><a href="{{ category.url }}">{{ category }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<dl>
|
||||
{% for article in dates %}
|
||||
<dt>{{ article.locale_date }}</dt>
|
||||
<dd><a href="{{ article.url }}">{{ article.title }}</a></dd>
|
||||
<dd><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<section id="content" class="body">
|
||||
<header> <h2 class="entry-title"><a href="{{ pagename }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<abbr class="published" title="{{ article.date.isoformat() }}">
|
||||
{{ article.locale_date }}
|
||||
</abbr>
|
||||
{% if article.author %}
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||
</address>
|
||||
{% endif %}
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
{{ article.content }}
|
||||
</div><!-- /.entry-content -->
|
||||
{% block content %}
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="{{ article.url }}" rel="bookmark"
|
||||
title="Permalink to {{ article.title|striptags }}">{{ article.title }}</a></h2>
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<abbr class="published" title="{{ article.date.isoformat() }}">
|
||||
{{ article.locale_date }}
|
||||
</abbr>
|
||||
{% if article.author %}
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||
</address>
|
||||
{% endif %}
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
{{ article.content }}
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
{% endfor %}
|
||||
{% else %}
|
||||
{% for cat, null in categories %}
|
||||
<li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/category/{{ cat }}.html">{{ cat }}</a></li>
|
||||
<li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul></nav><!-- /#menu -->
|
||||
|
|
|
|||
|
|
@ -17,18 +17,6 @@
|
|||
</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">«</a>
|
||||
{% else %}
|
||||
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html">«</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">»</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% include 'pagination.html' %}
|
||||
</section><!-- /#content -->
|
||||
{% endblock content %}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,12 @@ from pelican.utils import slugify
|
|||
|
||||
def wp2fields(xml):
|
||||
"""Opens a wordpress XML file, and yield pelican fields"""
|
||||
from BeautifulSoup import BeautifulStoneSoup
|
||||
try:
|
||||
from BeautifulSoup import BeautifulStoneSoup
|
||||
except ImportError:
|
||||
error = ('Missing dependency '
|
||||
'"BeautifulSoup" required to import Wordpress XML files.')
|
||||
sys.exit(error)
|
||||
|
||||
xmlfile = open(xml, encoding='utf-8').read()
|
||||
soup = BeautifulStoneSoup(xmlfile)
|
||||
|
|
@ -34,13 +39,19 @@ def wp2fields(xml):
|
|||
categories = [cat.contents[0] for cat in item.fetch(domain='category')]
|
||||
# caturl = [cat['nicename'] for cat in item.fetch(domain='category')]
|
||||
|
||||
tags = [tag.contents[0].title() for tag in item.fetch(domain='tag', nicename=None)]
|
||||
tags = [tag.contents[0] for tag in item.fetch(domain='post_tag')]
|
||||
|
||||
yield (title, content, filename, date, author, categories, tags, "html")
|
||||
|
||||
def dc2fields(file):
|
||||
"""Opens a Dotclear export file, and yield pelican fields"""
|
||||
from BeautifulSoup import BeautifulStoneSoup
|
||||
try:
|
||||
from BeautifulSoup import BeautifulStoneSoup
|
||||
except ImportError:
|
||||
error = ('Missing dependency '
|
||||
'"BeautifulSoup" required to import Dotclear files.')
|
||||
sys.exit(error)
|
||||
|
||||
|
||||
in_cat = False
|
||||
in_post = False
|
||||
|
|
@ -213,9 +224,12 @@ def fields2pelican(fields, out_markup, output_path, dircat=False):
|
|||
html_filename = os.path.join(output_path, filename+'.html')
|
||||
|
||||
with open(html_filename, 'w', encoding='utf-8') as fp:
|
||||
# Replace simple newlines with <br />+newline so that the HTML file
|
||||
# represents the original post more accurately
|
||||
content = content.replace("\n", "<br />\n")
|
||||
# Replace newlines with paragraphs wrapped with <p> so
|
||||
# HTML is valid before conversion
|
||||
paragraphs = content.split('\n\n')
|
||||
paragraphs = [u'<p>{}</p>'.format(p) for p in paragraphs]
|
||||
new_content = ''.join(paragraphs)
|
||||
|
||||
fp.write(content)
|
||||
|
||||
cmd = 'pandoc --normalize --reference-links --from=html --to={0} -o "{1}" "{2}"'.format(
|
||||
|
|
|
|||
|
|
@ -7,91 +7,9 @@ import argparse
|
|||
|
||||
from pelican import __version__
|
||||
|
||||
TEMPLATES = {
|
||||
'Makefile' : '''
|
||||
PELICAN=$pelican
|
||||
PELICANOPTS=$pelicanopts
|
||||
_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), \
|
||||
"templates")
|
||||
|
||||
BASEDIR=$$(PWD)
|
||||
INPUTDIR=$$(BASEDIR)/src
|
||||
OUTPUTDIR=$$(BASEDIR)/output
|
||||
CONFFILE=$$(BASEDIR)/pelican.conf.py
|
||||
|
||||
FTP_HOST=$ftp_host
|
||||
FTP_USER=$ftp_user
|
||||
FTP_TARGET_DIR=$ftp_target_dir
|
||||
|
||||
SSH_HOST=$ssh_host
|
||||
SSH_USER=$ssh_user
|
||||
SSH_TARGET_DIR=$ssh_target_dir
|
||||
|
||||
DROPBOX_DIR=$dropbox_dir
|
||||
|
||||
help:
|
||||
\t@echo 'Makefile for a pelican Web site '
|
||||
\t@echo ' '
|
||||
\t@echo 'Usage: '
|
||||
\t@echo ' make html (re)generate the web site '
|
||||
\t@echo ' make clean remove the generated files '
|
||||
\t@echo ' ftp_upload upload the web site using FTP '
|
||||
\t@echo ' ssh_upload upload the web site using SSH '
|
||||
\t@echo ' dropbox_upload upload the web site using Dropbox '
|
||||
\t@echo ' '
|
||||
|
||||
|
||||
html: clean $$(OUTPUTDIR)/index.html
|
||||
\t@echo 'Done'
|
||||
|
||||
$$(OUTPUTDIR)/%.html:
|
||||
\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||
|
||||
clean:
|
||||
\trm -fr $$(OUTPUTDIR)
|
||||
\tmkdir $$(OUTPUTDIR)
|
||||
|
||||
dropbox_upload: $$(OUTPUTDIR)/index.html
|
||||
\tcp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
||||
|
||||
ssh_upload: $$(OUTPUTDIR)/index.html
|
||||
\tscp -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||
|
||||
ftp_upload: $$(OUTPUTDIR)/index.html
|
||||
\tlftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit"
|
||||
|
||||
github: $$(OUTPUTDIR)/index.html
|
||||
\tghp-import $$(OUTPUTDIR)
|
||||
\tgit push origin gh-pages
|
||||
|
||||
.PHONY: html help clean ftp_upload ssh_upload dropbox_upload github
|
||||
''',
|
||||
|
||||
'pelican.conf.py': '''#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*- #
|
||||
|
||||
AUTHOR = u"$author"
|
||||
SITENAME = u"$sitename"
|
||||
SITEURL = '/'
|
||||
|
||||
TIMEZONE = 'Europe/Paris'
|
||||
|
||||
DEFAULT_LANG='$lang'
|
||||
|
||||
# Blogroll
|
||||
LINKS = (
|
||||
('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
||||
('Python.org', 'http://python.org'),
|
||||
('Jinja2', 'http://jinja.pocoo.org'),
|
||||
('You can modify those links in your config file', '#')
|
||||
)
|
||||
|
||||
# Social widget
|
||||
SOCIAL = (
|
||||
('You can add links in your config file', '#'),
|
||||
)
|
||||
|
||||
DEFAULT_PAGINATION = $default_pagination
|
||||
'''
|
||||
}
|
||||
|
||||
CONF = {
|
||||
'pelican' : 'pelican',
|
||||
|
|
@ -109,6 +27,20 @@ CONF = {
|
|||
}
|
||||
|
||||
|
||||
def get_template(name):
|
||||
template = os.path.join(_TEMPLATES_DIR, "{0}.in".format(name))
|
||||
|
||||
if not os.path.isfile(template):
|
||||
raise RuntimeError("Cannot open {0}".format(template))
|
||||
|
||||
with open(template, 'r') as fd:
|
||||
line = fd.readline()
|
||||
while line:
|
||||
yield line
|
||||
line = fd.readline()
|
||||
fd.close()
|
||||
|
||||
|
||||
def ask(question, answer=str, default=None, l=None):
|
||||
if answer == str:
|
||||
r = ''
|
||||
|
|
@ -199,16 +131,16 @@ def main():
|
|||
|
||||
print('''Welcome to pelican-quickstart v{v}.
|
||||
|
||||
This script will help you creating a new Pelican based website.
|
||||
This script will help you create a new Pelican-based website.
|
||||
|
||||
Please answer the following questions so this script can generate the files needed by Pelican.
|
||||
|
||||
'''.format(v=__version__))
|
||||
|
||||
CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new Web site ?', answer=str, default=args.path))
|
||||
CONF['sitename'] = ask('How will you call your Web site ?', answer=str, default=args.title)
|
||||
CONF['sitename'] = ask('What will be the title of this Web site ?', answer=str, default=args.title)
|
||||
CONF['author'] = ask('Who will be the author of this Web site ?', answer=str, default=args.author)
|
||||
CONF['lang'] = ask('What will be the default language of this Web site ?', str, args.lang or CONF['lang'], 2)
|
||||
CONF['lang'] = ask('What will be the default language of this Web site ?', str, args.lang or CONF['lang'], 2)
|
||||
|
||||
CONF['with_pagination'] = ask('Do you want to enable article pagination ?', bool, bool(CONF['default_pagination']))
|
||||
|
||||
|
|
@ -223,12 +155,12 @@ Please answer the following questions so this script can generate the files need
|
|||
if ask('Do you want to upload your website using FTP ?', answer=bool, default=False):
|
||||
CONF['ftp_host'] = ask('What is the hostname of your FTP server ?', str, CONF['ftp_host'])
|
||||
CONF['ftp_user'] = ask('What is your username on this server ?', str, CONF['ftp_user'])
|
||||
CONF['ftp_traget_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ftp_target_dir'])
|
||||
CONF['ftp_target_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ftp_target_dir'])
|
||||
|
||||
if ask('Do you want to upload your website using SSH ?', answer=bool, default=False):
|
||||
CONF['ssh_host'] = ask('What is the hostname of your SSH server ?', str, CONF['ssh_host'])
|
||||
CONF['ssh_user'] = ask('What is your username on this server ?', str, CONF['ssh_user'])
|
||||
CONF['ssh_traget_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ssh_target_dir'])
|
||||
CONF['ssh_target_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ssh_target_dir'])
|
||||
|
||||
if ask('Do you want to upload your website using Dropbox ?', answer=bool, default=False):
|
||||
CONF['dropbox_dir'] = ask('Where is your Dropbox directory ?', str, CONF['dropbox_dir'])
|
||||
|
|
@ -243,20 +175,22 @@ Please answer the following questions so this script can generate the files need
|
|||
except OSError, e:
|
||||
print('Error: {0}'.format(e))
|
||||
|
||||
conf = string.Template(TEMPLATES['pelican.conf.py'])
|
||||
try:
|
||||
with open(os.path.join(CONF['basedir'], 'pelican.conf.py'), 'w') as fd:
|
||||
fd.write(conf.safe_substitute(CONF))
|
||||
for line in get_template('pelican.conf.py'):
|
||||
template = string.Template(line)
|
||||
fd.write(template.safe_substitute(CONF))
|
||||
fd.close()
|
||||
except OSError, e:
|
||||
print('Error: {0}'.format(e))
|
||||
|
||||
if mkfile:
|
||||
Makefile = string.Template(TEMPLATES['Makefile'])
|
||||
|
||||
try:
|
||||
with open(os.path.join(CONF['basedir'], 'Makefile'), 'w') as fd:
|
||||
fd.write(Makefile.safe_substitute(CONF))
|
||||
for line in get_template('Makefile'):
|
||||
template = string.Template(line)
|
||||
fd.write(template.safe_substitute(CONF))
|
||||
fd.close()
|
||||
except OSError, e:
|
||||
print('Error: {0}'.format(e))
|
||||
|
|
|
|||
58
pelican/tools/templates/Makefile.in
Normal file
58
pelican/tools/templates/Makefile.in
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
PELICAN=$pelican
|
||||
PELICANOPTS=$pelicanopts
|
||||
|
||||
BASEDIR=$$(PWD)
|
||||
INPUTDIR=$$(BASEDIR)/src
|
||||
OUTPUTDIR=$$(BASEDIR)/output
|
||||
CONFFILE=$$(BASEDIR)/pelican.conf.py
|
||||
|
||||
FTP_HOST=$ftp_host
|
||||
FTP_USER=$ftp_user
|
||||
FTP_TARGET_DIR=$ftp_target_dir
|
||||
|
||||
SSH_HOST=$ssh_host
|
||||
SSH_USER=$ssh_user
|
||||
SSH_TARGET_DIR=$ssh_target_dir
|
||||
|
||||
DROPBOX_DIR=$dropbox_dir
|
||||
|
||||
help:
|
||||
@echo 'Makefile for a pelican Web site '
|
||||
@echo ' '
|
||||
@echo 'Usage: '
|
||||
@echo ' make html (re)generate the web site '
|
||||
@echo ' make clean remove the generated files '
|
||||
@echo ' ftp_upload upload the web site using FTP '
|
||||
@echo ' ssh_upload upload the web site using SSH '
|
||||
@echo ' dropbox_upload upload the web site using Dropbox '
|
||||
@echo ' rsync_upload upload the web site using rsync/ssh'
|
||||
@echo ' '
|
||||
|
||||
|
||||
html: clean $$(OUTPUTDIR)/index.html
|
||||
@echo 'Done'
|
||||
|
||||
$$(OUTPUTDIR)/%.html:
|
||||
$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
|
||||
|
||||
clean:
|
||||
rm -fr $$(OUTPUTDIR)
|
||||
mkdir $$(OUTPUTDIR)
|
||||
|
||||
dropbox_upload: $$(OUTPUTDIR)/index.html
|
||||
cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
|
||||
|
||||
ssh_upload: $$(OUTPUTDIR)/index.html
|
||||
scp -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
|
||||
|
||||
rsync_upload: $$(OUTPUTDIR)/index.html
|
||||
rsync -e ssh -P -rvz --delete $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
||||
|
||||
ftp_upload: $$(OUTPUTDIR)/index.html
|
||||
lftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit"
|
||||
|
||||
github: $$(OUTPUTDIR)/index.html
|
||||
ghp-import $$(OUTPUTDIR)
|
||||
git push origin gh-pages
|
||||
|
||||
.PHONY: html help clean ftp_upload ssh_upload rsync_upload dropbox_upload github
|
||||
25
pelican/tools/templates/pelican.conf.py.in
Normal file
25
pelican/tools/templates/pelican.conf.py.in
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*- #
|
||||
|
||||
AUTHOR = u"$author"
|
||||
SITENAME = u"$sitename"
|
||||
SITEURL = '/'
|
||||
|
||||
TIMEZONE = 'Europe/Paris'
|
||||
|
||||
DEFAULT_LANG='$lang'
|
||||
|
||||
# Blogroll
|
||||
LINKS = (
|
||||
('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
|
||||
('Python.org', 'http://python.org'),
|
||||
('Jinja2', 'http://jinja.pocoo.org'),
|
||||
('You can modify those links in your config file', '#')
|
||||
)
|
||||
|
||||
# Social widget
|
||||
SOCIAL = (
|
||||
('You can add links in your config file', '#'),
|
||||
)
|
||||
|
||||
DEFAULT_PAGINATION = $default_pagination
|
||||
|
|
@ -8,6 +8,7 @@ import logging
|
|||
from codecs import open as _open
|
||||
from datetime import datetime
|
||||
from itertools import groupby
|
||||
from jinja2 import Markup
|
||||
from operator import attrgetter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -44,6 +45,7 @@ def slugify(value):
|
|||
|
||||
Took from django sources.
|
||||
"""
|
||||
value = Markup(value).striptags()
|
||||
if type(value) == unicode:
|
||||
import unicodedata
|
||||
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import logging
|
|||
|
||||
from codecs import open
|
||||
from functools import partial
|
||||
|
||||
from feedgenerator import Atom1Feed, Rss201rev2Feed
|
||||
from jinja2 import Markup
|
||||
from pelican.paginator import Paginator
|
||||
from pelican.utils import get_relative_path, set_date_tzinfo
|
||||
|
||||
|
|
@ -25,8 +25,9 @@ class Writer(object):
|
|||
|
||||
def _create_new_feed(self, feed_type, context):
|
||||
feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
|
||||
sitename = Markup(context['SITENAME']).striptags()
|
||||
feed = feed_class(
|
||||
title=context['SITENAME'],
|
||||
title=sitename,
|
||||
link=(self.site_url + '/'),
|
||||
feed_url=self.feed_url,
|
||||
description=context.get('SITESUBTITLE', ''))
|
||||
|
|
@ -34,9 +35,10 @@ class Writer(object):
|
|||
|
||||
def _add_item_to_the_feed(self, feed, item):
|
||||
|
||||
title = Markup(item.title).striptags()
|
||||
feed.add_item(
|
||||
title=item.title,
|
||||
link='%s%s' % (self.site_url, item.url),
|
||||
title=title,
|
||||
link='%s/%s' % (self.site_url, item.url),
|
||||
unique_id='tag:%s,%s:%s' % (self.site_url.replace('http://', ''),
|
||||
item.date.date(), item.url),
|
||||
description=item.content,
|
||||
|
|
@ -99,6 +101,12 @@ class Writer(object):
|
|||
:param **kwargs: additional variables to pass to the templates
|
||||
"""
|
||||
|
||||
if name is False:
|
||||
return
|
||||
elif not name:
|
||||
# other stuff, just return for now
|
||||
return
|
||||
|
||||
def _write_file(template, localcontext, output_path, name):
|
||||
"""Render the template write the file."""
|
||||
old_locale = locale.setlocale(locale.LC_ALL)
|
||||
|
|
|
|||
10
tests/content/article_with_md_extension.md
Normal file
10
tests/content/article_with_md_extension.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
title: Test md File
|
||||
category: test
|
||||
|
||||
Test Markdown File Header
|
||||
=========================
|
||||
|
||||
Used for pelican test
|
||||
---------------------
|
||||
|
||||
The quick brown fox jumped over the lazy dog's back.
|
||||
10
tests/content/article_with_mkd_extension.mkd
Normal file
10
tests/content/article_with_mkd_extension.mkd
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
title: Test mkd File
|
||||
category: test
|
||||
|
||||
Test Markdown File Header
|
||||
=========================
|
||||
|
||||
Used for pelican test
|
||||
---------------------
|
||||
|
||||
This is another markdown test file. Uses the mkd extension.
|
||||
|
|
@ -4,6 +4,8 @@ __all__ = [
|
|||
'unittest',
|
||||
]
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from contextlib import contextmanager
|
||||
from tempfile import mkdtemp
|
||||
from shutil import rmtree
|
||||
|
|
@ -35,3 +37,21 @@ def get_article(title, slug, content, lang, extra_metadata=None):
|
|||
if extra_metadata is not None:
|
||||
metadata.update(extra_metadata)
|
||||
return Article(content, metadata=metadata)
|
||||
|
||||
|
||||
def skipIfNoExecutable(executable, valid_exit_code=1):
|
||||
"""Tries to run an executable to make sure it's in the path, Skips the tests
|
||||
if not found.
|
||||
"""
|
||||
|
||||
# calling with no params the command should exit with 1
|
||||
with open(os.devnull, 'w') as fnull:
|
||||
try:
|
||||
res = subprocess.call(executable, stdout=fnull, stderr=fnull)
|
||||
except OSError:
|
||||
res = None
|
||||
|
||||
if res != valid_exit_code:
|
||||
return unittest.skip('{0} compiler not found'.format(executable))
|
||||
|
||||
return lambda func: func
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
from mock import MagicMock
|
||||
import os
|
||||
import re
|
||||
|
||||
from pelican.generators import ArticlesGenerator
|
||||
from pelican.generators import ArticlesGenerator, LessCSSGenerator
|
||||
from pelican.settings import _DEFAULT_CONFIG
|
||||
from .support import unittest
|
||||
from .support import unittest, temporary_folder, skipIfNoExecutable
|
||||
|
||||
CUR_DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
class TestArticlesGenerator(unittest.TestCase):
|
||||
|
||||
def test_generate_feeds(self):
|
||||
|
|
@ -46,3 +48,94 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
elif relfilepath == "article_without_category.rst":
|
||||
self.assertEquals(article.category.name, 'Default')
|
||||
|
||||
categories = [cat.name for cat, _ in generator.categories]
|
||||
# assert that the categories are ordered as expected
|
||||
self.assertEquals(
|
||||
categories, ['Default', 'TestCategory', 'Yeah', 'test',
|
||||
'yeah'])
|
||||
|
||||
def test_direct_templates_save_as_default(self):
|
||||
|
||||
settings = _DEFAULT_CONFIG.copy()
|
||||
settings['DIRECT_TEMPLATES'] = ['archives']
|
||||
generator = ArticlesGenerator(settings.copy(), settings, None,
|
||||
_DEFAULT_CONFIG['THEME'], None,
|
||||
_DEFAULT_CONFIG['MARKUP'])
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_with("archives.html",
|
||||
generator.get_template("archives"), settings,
|
||||
blog=True, paginated={}, page_name='archives')
|
||||
|
||||
def test_direct_templates_save_as_modified(self):
|
||||
|
||||
settings = _DEFAULT_CONFIG.copy()
|
||||
settings['DIRECT_TEMPLATES'] = ['archives']
|
||||
settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
|
||||
generator = ArticlesGenerator(settings, settings, None,
|
||||
_DEFAULT_CONFIG['THEME'], None,
|
||||
_DEFAULT_CONFIG['MARKUP'])
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_with("archives/index.html",
|
||||
generator.get_template("archives"), settings,
|
||||
blog=True, paginated={}, page_name='archives')
|
||||
|
||||
def test_direct_templates_save_as_false(self):
|
||||
|
||||
settings = _DEFAULT_CONFIG.copy()
|
||||
settings['DIRECT_TEMPLATES'] = ['archives']
|
||||
settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
|
||||
generator = ArticlesGenerator(settings, settings, None,
|
||||
_DEFAULT_CONFIG['THEME'], None,
|
||||
_DEFAULT_CONFIG['MARKUP'])
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_count == 0
|
||||
|
||||
|
||||
class TestLessCSSGenerator(unittest.TestCase):
|
||||
|
||||
LESS_CONTENT = """
|
||||
@color: #4D926F;
|
||||
|
||||
#header {
|
||||
color: @color;
|
||||
}
|
||||
h2 {
|
||||
color: @color;
|
||||
}
|
||||
"""
|
||||
|
||||
@skipIfNoExecutable('lessc')
|
||||
def test_less_compiler(self):
|
||||
|
||||
settings = _DEFAULT_CONFIG.copy()
|
||||
settings['STATIC_PATHS'] = ['static']
|
||||
settings['LESS_GENERATOR'] = True
|
||||
|
||||
# we'll nest here for py < 2.7 compat
|
||||
with temporary_folder() as temp_content:
|
||||
with temporary_folder() as temp_output:
|
||||
generator = LessCSSGenerator(None, settings, temp_content,
|
||||
_DEFAULT_CONFIG['THEME'], temp_output, None)
|
||||
|
||||
# create a dummy less file
|
||||
less_dir = os.path.join(temp_content, 'static', 'css')
|
||||
less_filename = os.path.join(less_dir, 'test.less')
|
||||
|
||||
less_output = os.path.join(temp_output, 'static', 'css',
|
||||
'test.css')
|
||||
|
||||
os.makedirs(less_dir)
|
||||
with open(less_filename, 'w') as less_file:
|
||||
less_file.write(self.LESS_CONTENT)
|
||||
|
||||
generator.generate_output()
|
||||
|
||||
# we have the file ?
|
||||
self.assertTrue(os.path.exists(less_output))
|
||||
|
||||
# was it compiled ?
|
||||
self.assertIsNotNone(re.search(r'^\s+color:\s*#4D926F;$',
|
||||
open(less_output).read(), re.MULTILINE | re.IGNORECASE))
|
||||
|
|
|
|||
|
|
@ -61,3 +61,25 @@ class RstReaderTest(unittest.TestCase):
|
|||
self.assertEqual(content, expected)
|
||||
except ImportError:
|
||||
return unittest.skip('need the typogrify distribution')
|
||||
|
||||
class MdReaderTest(unittest.TestCase):
|
||||
|
||||
def test_article_with_md_extention(self):
|
||||
# test to ensure the md extension is being processed by the correct reader
|
||||
reader = readers.MarkdownReader({})
|
||||
content, metadata = reader.read(_filename('article_with_md_extension.md'))
|
||||
expected = "<h1>Test Markdown File Header</h1>\n"\
|
||||
"<h2>Used for pelican test</h2>\n"\
|
||||
"<p>The quick brown fox jumped over the lazy dog's back.</p>"
|
||||
|
||||
self.assertEqual(content, expected)
|
||||
|
||||
def test_article_with_mkd_extension(self):
|
||||
# test to ensure the mkd extension is being processed by the correct reader
|
||||
reader = readers.MarkdownReader({})
|
||||
content, metadata = reader.read(_filename('article_with_mkd_extension.mkd'))
|
||||
expected = "<h1>Test Markdown File Header</h1>\n"\
|
||||
"<h2>Used for pelican test</h2>\n"\
|
||||
"<p>This is another markdown test file. Uses the mkd extension.</p>"
|
||||
|
||||
self.assertEqual(content, expected)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue