From a54fbafd7cb3cb288261da716d46b0b8e3ffac21 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sat, 20 Nov 2010 16:01:40 +0000
Subject: [PATCH 0001/2735] Added tag 2.5.0 for changeset 87745dfdd51b
---
.hgtags | 1 +
1 file changed, 1 insertion(+)
diff --git a/.hgtags b/.hgtags
index 174c6504..59157608 100644
--- a/.hgtags
+++ b/.hgtags
@@ -19,3 +19,4 @@ e65199a0b2706d2fb48f7a3c015e869716e0bec1 2.4.1
979b4473af56a191a278c83058bc9c8fa1fde30e 2.4.3
26a444fbb78becae358afa0a5b47587db8739b21 2.4.4
3542b65fd1963ae7065b6a3bc912fbb6c150e98c 2.4.5
+87745dfdd51b96bf18eaaf6c402effa902c1b856 2.5.0
From 4702dd896b33e605b8970aeadab41b486e24a5fc Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sat, 20 Nov 2010 18:47:40 +0000
Subject: [PATCH 0002/2735] Add section open and close in the notmyidea theme.
---
pelican/themes/notmyidea/templates/archives.html | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pelican/themes/notmyidea/templates/archives.html b/pelican/themes/notmyidea/templates/archives.html
index 31b02a0d..b3e534b7 100644
--- a/pelican/themes/notmyidea/templates/archives.html
+++ b/pelican/themes/notmyidea/templates/archives.html
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% block content %}
+
Archives for {{ SITENAME }}
@@ -10,4 +11,5 @@
{% endfor %}
{% endfor %}
+
{% endblock %}
From 8fb52e1c63e0a8c85e2c585e8337eb0c617b8908 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sat, 20 Nov 2010 19:37:27 +0000
Subject: [PATCH 0003/2735] Update the README with accurate informations.
---
README.rst | 110 ++++++++++++++++++++++++++++-------------------------
1 file changed, 59 insertions(+), 51 deletions(-)
diff --git a/README.rst b/README.rst
index 82a126da..40fd03ff 100644
--- a/README.rst
+++ b/README.rst
@@ -35,9 +35,10 @@ You can also use a mardown syntax (with a file ending in `.md`)::
Put you content here.
-Note that only the `date` metadata is mandatory, so you just have to add that in i
-your files. The category can also be determined by the directory where the rst file
-is. For instance, the category of `python/foobar/myfoobar.rst` is `foobar`.
+Note that none of those are mendatory: if the date is not specified, pelican will
+rely on the mtime of your file, and the category can also be determined by the
+directory where the rst file is. For instance, the category of
+`python/foobar/myfoobar.rst` is `foobar`.
Features
--------
@@ -54,8 +55,8 @@ Pelican currently supports:
Getting started — Generate your blog
-------------------------------------
-Yeah? You're ready? Let's go ! You can install pelican in a lot of different
-ways, the simpler one is via pip::
+You're ready? Let's go ! You can install pelican in a lot of different ways,
+the simpler one is via `pip `_.
$ pip install pelican
@@ -63,7 +64,7 @@ Then, you have just to launch pelican, like this::
$ pelican /path/to/your/content/
-And… that's all! You can see your weblog generated on the content/ folder.
+And… that's all! You can see your weblog generated on the `content/` folder.
This one will just generate a simple output, with the default theme. It's not
really sexy, as it's a simple HTML output (without any style).
@@ -76,33 +77,43 @@ the options you can use::
Settings
--------
-Pelican try to be configurable via a configuration file, that you can pass to
-the command line. Here are the settings you can use, with some basic
-description.
+Pelican is configurable thanks a configuration file, that you can pass to
+the command line::
-Please note that all the settings you put in this file will be passed to the
-templates as well.
+ $ pelican -s path/to/your/settingsfile.py path
-* `SITEURL` is the base URL of your website.
-* `PATH` is the path to look at for input files.
-* `THEME`: the theme to use to product the output. can be the complete static
- path to a theme folder, or choosed between the default pelican themes (see
- below)
-* `OUTPUT_PATH`: Where to output the generated files. Default to "output"
-* `SITENAME`: Your site name,
-* `DISPLAY_PAGES_ON_MENU`: Display or not the pages on the menu of the
- template. Templates can follow or not this settings.
-* `PDF_PROCESSOR`: Put True if you want to have PDF outputs as well as HTML
- pages,
-* `DEFAULT_CATEGORY`: The default category. `misc` by default.
-* `FALLBACK_ON_FS_DATE`: Choose to fallback on filesystem dates informations if
- any other way to retreive the date currrently exists.,
-* `MARKUP`: A list of available markup languages you want to use. At the
- moment, only available values are `rst` and `md`.
-* `STATIC_PATHS`: The static paths you want to copy under "static"
-* `FEED`: url to output the feed.,
-* `CATEGORY_FEED`: Where to put the categories feeds. default is `feeds/%s.atom.xml`
-* `CSS_FILE`: To specify the CSS file you want to load.
+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 ?
+======================= =======================================================
+`SITEURL` base URL of your website.
+`PATH` path to look at for input files.
+`THEME` theme to use to product the output. can be the
+ complete static path to a theme folder, or choosed
+ between the list of default themes (see below)
+`OUTPUT_PATH` Where to output the generated files. Default to
+ "output"
+`SITENAME` Your site name,
+`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
+ Templates can follow or not this settings.
+`PDF_PROCESSOR` Put True if you want to have PDF versions of your
+ documents. You will need to install `rst2pdf`.
+`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
+`FALLBACK_ON_FS_DATE` If True, pelican will use the filesystem dates infos
+ (mtime) if it can't get informations from the
+ metadatas?
+`MARKUP` A list of available markup languages you want to use.
+ moment, only available values are `rst` and `md`.
+`STATIC_PATHS` The static paths you want to copy under "static"
+`FEED` relative url to output the feed. Default is
+ `feeds/all.atom.xml`
+`CATEGORY_FEED` Where to put the categories feeds. default is
+ `feeds/%s.atom.xml`
+`CSS_FILE` To specify the CSS file you want to load, if it's not
+ the default one ('main.css')
+======================= =======================================================
Themes
------
@@ -110,20 +121,28 @@ Themes
3 themes are available. You can specify them using the `-t` option:
* notmyidea
-* default
+* simple (a synonym for "full text" :)
* martyalchin
You can define your own theme too, and specify it's emplacement in the same
-way.
+way (be sure to specify the full absolute path to it).
-The `notmyidea` theme can make good use of the following settings:
+The `notmyidea` theme can make good use of the following settings. I recommand
+to use them too in your themes.
-* `GITHUB_URL` = your github URL (if you have one)
-* `DISQUS_SITENAME` can handle disqus comments
-* `LINKS` is a list of tuples Title, Url, for links
-* `SOCIAL` (('twitter', 'yourtwitter complete url'),) and any other name/link
- you want to put under "social"
-* `GOOGLE_ANALYTICS` = 'UA-XXXX-YYYY' to activate google analytics.
+======================= =======================================================
+Setting name what it does ?
+======================= =======================================================
+`GITHUB_URL` Your github URL (if you have one), it will then
+ use it to create a github ribbon.
+`DISQUS_SITENAME` Pelican can handle disqus comments, specify the
+ sitename you've filled in on disqus
+`LINKS` A list of tuples (Title, Url) for links to appear on
+ the header.
+`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
+ section.
+`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
+======================= =======================================================
In addition, you can use the "wide" version of the `notmyidea` theme, by
adding that in your configuration::
@@ -163,14 +182,3 @@ If you want to see new features in Pelican, dont hesitate to tell me, to clone
the repository, etc. That's open source, dude!
Contact me at "alexis at notmyidea dot org" for any request/feedback !
-
-FAQ
----
-
-How can I specify the url of my website ?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Configure the `SITEURL` to your site base url, let's say
-`http://myswebsite.tld`, in your settings file::
-
- SITEURL = "http://mywebsite.tld"
From 6bec424f4eb1dab4e5206be120121065d0c3e7be Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sat, 20 Nov 2010 19:57:22 +0000
Subject: [PATCH 0004/2735] Fix doc misspellings
---
README.rst | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/README.rst b/README.rst
index 40fd03ff..60ac7cae 100644
--- a/README.rst
+++ b/README.rst
@@ -9,11 +9,11 @@ Pelican is a simple weblog generator, writen in python.
* Easy to interface with DVCSes and web hooks
* Completely static output, so easy to host anywhere !
-Files metadatas
----------------
+Files metadata
+--------------
Pelican tries to be smart enough to get the informations he needs from the
-filesystem (for instance, about the category of your articles), but you need to
+file system (for instance, about the category of your articles), but you need to
provide by hand some of those informations in your files.
You could provide the metadata in the restructured text files, using the
@@ -28,14 +28,14 @@ following syntax (give your file the `.rst` extension)::
:author: Alexis Metaireau
-You can also use a mardown syntax (with a file ending in `.md`)::
+You can also use a markdown syntax (with a file ending in `.md`)::
Date: 2010-12-03
Title: My super title
Put you content here.
-Note that none of those are mendatory: if the date is not specified, pelican will
+Note that none of those are mandatory: if the date is not specified, pelican will
rely on the mtime of your file, and the category can also be determined by the
directory where the rst file is. For instance, the category of
`python/foobar/myfoobar.rst` is `foobar`.
@@ -56,7 +56,7 @@ Getting started — Generate your blog
-------------------------------------
You're ready? Let's go ! You can install pelican in a lot of different ways,
-the simpler one is via `pip `_.
+the simpler one is via `pip `_::
$ pip install pelican
@@ -91,7 +91,7 @@ Setting name what it does ?
`SITEURL` base URL of your website.
`PATH` path to look at for input files.
`THEME` theme to use to product the output. can be the
- complete static path to a theme folder, or choosed
+ complete static path to a theme folder, or chosen
between the list of default themes (see below)
`OUTPUT_PATH` Where to output the generated files. Default to
"output"
@@ -101,9 +101,9 @@ Setting name what it does ?
`PDF_PROCESSOR` Put True if you want to have PDF versions of your
documents. You will need to install `rst2pdf`.
`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
-`FALLBACK_ON_FS_DATE` If True, pelican will use the filesystem dates infos
+`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
(mtime) if it can't get informations from the
- metadatas?
+ metadata?
`MARKUP` A list of available markup languages you want to use.
moment, only available values are `rst` and `md`.
`STATIC_PATHS` The static paths you want to copy under "static"
@@ -127,7 +127,7 @@ Themes
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).
-The `notmyidea` theme can make good use of the following settings. I recommand
+The `notmyidea` theme can make good use of the following settings. I recommend
to use them too in your themes.
======================= =======================================================
@@ -152,7 +152,7 @@ adding that in your configuration::
Why the name "Pelican" ?
------------------------
-Heh, you didnt noticed? "Pelican" is an anagram for "Calepin" ;)
+Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
Dependencies
------------
@@ -166,7 +166,7 @@ At this time, pelican is dependent of the following python packages:
If you're not using python 2.7, you will also need `argparse`.
-All those dependencies will be processed automaticaly if you install pelican
+All those dependencies will be processed automatically if you install pelican
using setuptools/distribute or pip.
Source code
From 090a02a9e35b0a49117779611ecdb4483b2764bf Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sun, 21 Nov 2010 01:49:37 +0000
Subject: [PATCH 0005/2735] Change the output directory structure and a bit the
templates structure as well.
--HG--
rename : pelican/themes/martyalchin/css/style.css => pelican/themes/martyalchin/static/css/style.css
rename : pelican/themes/notmyidea/css/main.css => pelican/themes/notmyidea/static/css/main.css
rename : pelican/themes/notmyidea/css/pygment.css => pelican/themes/notmyidea/static/css/pygment.css
rename : pelican/themes/notmyidea/css/reset.css => pelican/themes/notmyidea/static/css/reset.css
rename : pelican/themes/notmyidea/css/wide.css => pelican/themes/notmyidea/static/css/wide.css
rename : pelican/themes/notmyidea/images/icons/delicious.png => pelican/themes/notmyidea/static/images/icons/delicious.png
rename : pelican/themes/notmyidea/images/icons/lastfm.png => pelican/themes/notmyidea/static/images/icons/lastfm.png
rename : pelican/themes/notmyidea/images/icons/linkedin.png => pelican/themes/notmyidea/static/images/icons/linkedin.png
rename : pelican/themes/notmyidea/images/icons/rss.png => pelican/themes/notmyidea/static/images/icons/rss.png
rename : pelican/themes/notmyidea/images/icons/twitter.png => pelican/themes/notmyidea/static/images/icons/twitter.png
---
TODO | 2 --
pelican/generators.py | 16 ++++-----
pelican/processors.py | 31 +++++++++++-------
pelican/settings.py | 3 +-
.../martyalchin/{ => static}/css/style.css | 0
.../themes/martyalchin/templates/base.html | 2 +-
.../notmyidea/{ => static}/css/main.css | 0
.../notmyidea/{ => static}/css/pygment.css | 0
.../notmyidea/{ => static}/css/reset.css | 0
.../notmyidea/{ => static}/css/wide.css | 0
.../{ => static}/images/icons/delicious.png | Bin
.../{ => static}/images/icons/lastfm.png | Bin
.../{ => static}/images/icons/linkedin.png | Bin
.../{ => static}/images/icons/rss.png | Bin
.../{ => static}/images/icons/twitter.png | Bin
pelican/themes/notmyidea/templates/base.html | 2 +-
pelican/utils.py | 10 +++---
17 files changed, 37 insertions(+), 29 deletions(-)
rename pelican/themes/martyalchin/{ => static}/css/style.css (100%)
rename pelican/themes/notmyidea/{ => static}/css/main.css (100%)
rename pelican/themes/notmyidea/{ => static}/css/pygment.css (100%)
rename pelican/themes/notmyidea/{ => static}/css/reset.css (100%)
rename pelican/themes/notmyidea/{ => static}/css/wide.css (100%)
rename pelican/themes/notmyidea/{ => static}/images/icons/delicious.png (100%)
rename pelican/themes/notmyidea/{ => static}/images/icons/lastfm.png (100%)
rename pelican/themes/notmyidea/{ => static}/images/icons/linkedin.png (100%)
rename pelican/themes/notmyidea/{ => static}/images/icons/rss.png (100%)
rename pelican/themes/notmyidea/{ => static}/images/icons/twitter.png (100%)
diff --git a/TODO b/TODO
index 858b441c..9928ced2 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,6 @@
* Add a way to support pictures (see how sphinx makes that)
* Find a way to extend the existing templates instead of rewriting all from scratch.
-* find a better way to specify the templates. an idea is simply to install the templates in a default path, and to add a way to specify them by default under a "_themes" dir.
* Make the program support UTF8-encoded files as input (and later: any encoding?)
-* Change the directory structure ?
* Add status support (draft, published, hidden)
* Add a serve + automatic generation behaviour.
* Recompile only the changed files, not all.
diff --git a/pelican/generators.py b/pelican/generators.py
index fb6e48ce..f5374722 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -16,10 +16,10 @@ _TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories',
class Generator(object):
"""Handle all generation process: files writes, feed creation, and this
kind of basic stuff"""
-
- def __init__(self, settings=None, path=None, theme=None, output_path=None,
+
+ def __init__(self, settings=None, path=None, theme=None, output_path=None,
markup=None):
- """Read the settings, and performs some checks on the environement
+ """Read the settings, and performs some checks on the environment
before doing anything else.
"""
if settings is None:
@@ -64,7 +64,7 @@ class Generator(object):
for p in processors:
p.process(context, self)
- def generate_feed(self, elements, context, filename=None,
+ def generate_feed(self, elements, context, filename=None,
relative_urls=True):
"""Generate a feed with the list of articles provided
@@ -72,7 +72,7 @@ class Generator(object):
the feed object.
:param articles: the articles to put on the feed.
- :param context: the context to get the feed metadatas.
+ :param context: the context to get the feed metadata.
:param output_path: where to output the file.
:param filename: the filename to output.
:param relative_urls: use relative urls or absolutes ones
@@ -104,11 +104,11 @@ class Generator(object):
fp = open(complete_path, 'w')
feed.write(fp, 'utf-8')
print u' [ok] writing %s' % complete_path
-
+
fp.close()
return feed
- def generate_file(self, name, template, context, relative_urls=True,
+ def generate_file(self, name, template, context, relative_urls=True,
**kwargs):
"""Write the file with the given informations
@@ -166,7 +166,7 @@ class Generator(object):
files.extend([os.sep.join((root, f)) for f in temp_files
if True in [f.endswith(ext) for ext in extensions]])
return files
-
+
def _get_relative_siteurl(self, filename):
"""Return the siteurl relative to the given filename"""
return '../' * filename.count('/') + '.'
diff --git a/pelican/processors.py b/pelican/processors.py
index e4248645..e9720c55 100644
--- a/pelican/processors.py
+++ b/pelican/processors.py
@@ -11,7 +11,7 @@ _DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives')
class Processor(object):
def _update_context(self, context, items):
- """Update the context with the given items from the currrent
+ """Update the context with the given items from the currrent
processor.
"""
for item in items:
@@ -24,20 +24,20 @@ class Processor(object):
class ArticlesProcessor(Processor):
def __init__(self, settings=None):
- self.articles = []
+ self.articles = []
self.dates = {}
self.years = {}
self.tags = {}
- self.categories = {}
-
+ self.categories = {}
+
def generate_feeds(self, context, generator):
- """Generate the feeds from the current context, and output files."""
+ """Generate the feeds from the current context, and output files."""
generator.generate_feed(self.articles, context, context['FEED'])
for cat, arts in self.categories.items():
arts.sort(key=attrgetter('date'), reverse=True)
- generator.generate_feed(arts, context,
+ generator.generate_feed(arts, context,
context['CATEGORY_FEED'] % cat)
def generate_pages(self, context, generator):
@@ -92,7 +92,7 @@ class ArticlesProcessor(Processor):
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
# and generate the output :)
- self._update_context(context, ('articles', 'dates', 'years',
+ self._update_context(context, ('articles', 'dates', 'years',
'tags', 'categories'))
def process(self, context, generator):
@@ -116,11 +116,11 @@ class PagesProcessor(Processor):
self.pages.append(page)
context['PAGES'] = self.pages
-
+
def process(self, context, generator):
templates = generator.get_templates()
for page in self.pages:
- generator.generate_file('pages/%s' % page.url,
+ generator.generate_file('pages/%s' % page.url,
templates['page'], context, page=page)
self._update_context(context, ('pages',))
@@ -128,10 +128,17 @@ class PagesProcessor(Processor):
class StaticProcessor(Processor):
"""copy static paths to output"""
+ def _copy_paths(self, paths, source, destination, output_path,
+ final_path=None):
+ for path in paths:
+ copytree(path, source, os.path.join(output_path, destination),
+ final_path)
+
def process(self, context, generator):
- for path in generator.settings['STATIC_PATHS']:
- copytree(path, generator.theme, generator.output_path)
- copytree('pics', generator.path, generator.output_path)
+ self._copy_paths(generator.settings['STATIC_PATHS'], generator.path,
+ 'static', generator.output_path)
+ self._copy_paths(generator.settings['THEME_PATHS'], generator.theme,
+ 'theme', generator.output_path, '.')
class PdfProcessor(Processor):
diff --git a/pelican/settings.py b/pelican/settings.py
index dfd5c48d..79de75cd 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -6,7 +6,8 @@ _DEFAULT_CONFIG = {'PATH': None,
'THEME': _DEFAULT_THEME,
'OUTPUT_PATH': 'output/',
'MARKUP': ('rst', 'md'),
- 'STATIC_PATHS': ['css', 'images'],
+ 'STATIC_PATHS': ['images',],
+ 'THEME_PATHS': ['static',],
'FEED': 'feeds/all.atom.xml',
'CATEGORY_FEED': 'feeds/%s.atom.xml',
'SITENAME': 'A Pelican Blog',
diff --git a/pelican/themes/martyalchin/css/style.css b/pelican/themes/martyalchin/static/css/style.css
similarity index 100%
rename from pelican/themes/martyalchin/css/style.css
rename to pelican/themes/martyalchin/static/css/style.css
diff --git a/pelican/themes/martyalchin/templates/base.html b/pelican/themes/martyalchin/templates/base.html
index 0e7ac866..eea18f9b 100644
--- a/pelican/themes/martyalchin/templates/base.html
+++ b/pelican/themes/martyalchin/templates/base.html
@@ -3,7 +3,7 @@
{% block title %}{{ SITENAME }}{%endblock%}
-
+
diff --git a/pelican/themes/notmyidea/css/main.css b/pelican/themes/notmyidea/static/css/main.css
similarity index 100%
rename from pelican/themes/notmyidea/css/main.css
rename to pelican/themes/notmyidea/static/css/main.css
diff --git a/pelican/themes/notmyidea/css/pygment.css b/pelican/themes/notmyidea/static/css/pygment.css
similarity index 100%
rename from pelican/themes/notmyidea/css/pygment.css
rename to pelican/themes/notmyidea/static/css/pygment.css
diff --git a/pelican/themes/notmyidea/css/reset.css b/pelican/themes/notmyidea/static/css/reset.css
similarity index 100%
rename from pelican/themes/notmyidea/css/reset.css
rename to pelican/themes/notmyidea/static/css/reset.css
diff --git a/pelican/themes/notmyidea/css/wide.css b/pelican/themes/notmyidea/static/css/wide.css
similarity index 100%
rename from pelican/themes/notmyidea/css/wide.css
rename to pelican/themes/notmyidea/static/css/wide.css
diff --git a/pelican/themes/notmyidea/images/icons/delicious.png b/pelican/themes/notmyidea/static/images/icons/delicious.png
similarity index 100%
rename from pelican/themes/notmyidea/images/icons/delicious.png
rename to pelican/themes/notmyidea/static/images/icons/delicious.png
diff --git a/pelican/themes/notmyidea/images/icons/lastfm.png b/pelican/themes/notmyidea/static/images/icons/lastfm.png
similarity index 100%
rename from pelican/themes/notmyidea/images/icons/lastfm.png
rename to pelican/themes/notmyidea/static/images/icons/lastfm.png
diff --git a/pelican/themes/notmyidea/images/icons/linkedin.png b/pelican/themes/notmyidea/static/images/icons/linkedin.png
similarity index 100%
rename from pelican/themes/notmyidea/images/icons/linkedin.png
rename to pelican/themes/notmyidea/static/images/icons/linkedin.png
diff --git a/pelican/themes/notmyidea/images/icons/rss.png b/pelican/themes/notmyidea/static/images/icons/rss.png
similarity index 100%
rename from pelican/themes/notmyidea/images/icons/rss.png
rename to pelican/themes/notmyidea/static/images/icons/rss.png
diff --git a/pelican/themes/notmyidea/images/icons/twitter.png b/pelican/themes/notmyidea/static/images/icons/twitter.png
similarity index 100%
rename from pelican/themes/notmyidea/images/icons/twitter.png
rename to pelican/themes/notmyidea/static/images/icons/twitter.png
diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html
index bdeb7b9f..99d6d380 100644
--- a/pelican/themes/notmyidea/templates/base.html
+++ b/pelican/themes/notmyidea/templates/base.html
@@ -3,7 +3,7 @@
{% block title %}{{ SITENAME }}{%endblock%}
-
+
diff --git a/pelican/utils.py b/pelican/utils.py
index 1bcc8604..79bd4e38 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -50,14 +50,16 @@ def slugify(value):
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
return re.sub('[-\s]+', '-', value)
-def copytree(path, origin, destination):
+def copytree(path, origin, destination, topath=None):
"""Copy path from origin to destination, silent any errors"""
-
+
+ if not topath:
+ topath = path
try:
fromp = os.path.expanduser(os.path.join(origin, path))
- to = os.path.expanduser(os.path.join(destination, path))
+ to = os.path.expanduser(os.path.join(destination, topath))
shutil.copytree(fromp, to)
- print u' [ok] copying %s' % fromp
+ print u' [ok] copying %s to %s' % (fromp, to)
except OSError:
pass
From d489000c65150aa9a25f65a3bc435cac81c79510 Mon Sep 17 00:00:00 2001
From: Philippe Pepiot
Date: Wed, 24 Nov 2010 15:49:10 +0100
Subject: [PATCH 0006/2735] Add syntax highlight support for markdown posts
---
pelican/readers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index c8d43c08..e9520fe6 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -45,7 +45,7 @@ class MarkdownReader(object):
def read(self, filename):
"""Parse content and metadata of markdown files"""
text = open(filename)
- md = Markdown(extensions = ['meta'])
+ md = Markdown(extensions = ['meta', 'codehilite'])
content = md.convert(text)
metadatas = {}
From a3f0ded1464ea9fc2bef9fe3bed31ab774cc944b Mon Sep 17 00:00:00 2001
From: Philippe Pepiot
Date: Wed, 24 Nov 2010 15:58:58 +0100
Subject: [PATCH 0007/2735] Missing import in processors
---
pelican/processors.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/pelican/processors.py b/pelican/processors.py
index e9720c55..63843030 100644
--- a/pelican/processors.py
+++ b/pelican/processors.py
@@ -1,4 +1,5 @@
from operator import attrgetter
+from datetime import datetime
import os
from pelican.utils import update_dict, copytree
From 5fda51de423012d38977b6bee48b03f11f708815 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Fri, 26 Nov 2010 23:37:03 +0000
Subject: [PATCH 0008/2735] Fix #12
---
pelican/processors.py | 8 +++-----
pelican/themes/notmyidea/templates/archives.html | 8 +++-----
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/pelican/processors.py b/pelican/processors.py
index 63843030..d35abdbf 100644
--- a/pelican/processors.py
+++ b/pelican/processors.py
@@ -27,7 +27,6 @@ class ArticlesProcessor(Processor):
def __init__(self, settings=None):
self.articles = []
self.dates = {}
- self.years = {}
self.tags = {}
self.categories = {}
@@ -82,8 +81,6 @@ class ArticlesProcessor(Processor):
if not is_valid_content(article, f):
continue
- update_dict(self.dates, article.date.strftime('%Y-%m-%d'), article)
- update_dict(self.years, article.date.year, article)
update_dict(self.categories, article.category, article)
if hasattr(article, 'tags'):
for tag in article.tags:
@@ -92,9 +89,10 @@ class ArticlesProcessor(Processor):
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
+ self.dates = self.articles
+ self.dates.sort(key=attrgetter('date'))
# and generate the output :)
- self._update_context(context, ('articles', 'dates', 'years',
- 'tags', 'categories'))
+ self._update_context(context, ('articles', 'dates', 'tags', 'categories'))
def process(self, context, generator):
self.generate_feeds(context, generator)
diff --git a/pelican/themes/notmyidea/templates/archives.html b/pelican/themes/notmyidea/templates/archives.html
index b3e534b7..1644affb 100644
--- a/pelican/themes/notmyidea/templates/archives.html
+++ b/pelican/themes/notmyidea/templates/archives.html
@@ -4,11 +4,9 @@
Archives for {{ SITENAME }}
-{% for date, articles in dates %}
- {% for article in articles %}
-
From dd202744d03bdee89bd014218a9b0fd5aa6a81a9 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sat, 27 Nov 2010 15:20:38 +0000
Subject: [PATCH 0009/2735] Oops. Make a copy of the list before modifying it !
---
pelican/processors.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/processors.py b/pelican/processors.py
index d35abdbf..c209cafd 100644
--- a/pelican/processors.py
+++ b/pelican/processors.py
@@ -89,7 +89,7 @@ class ArticlesProcessor(Processor):
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
- self.dates = self.articles
+ self.dates = list(self.articles)
self.dates.sort(key=attrgetter('date'))
# and generate the output :)
self._update_context(context, ('articles', 'dates', 'tags', 'categories'))
From 7d11af5630c9cc0ea0367a30f3c30d9bb3c16215 Mon Sep 17 00:00:00 2001
From: Philippe Pepiot
Date: Wed, 1 Dec 2010 02:30:38 +0100
Subject: [PATCH 0010/2735] Dont use relative urls if SITEURL is set. Feeds
needs absolutes urls
---
pelican/generators.py | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/pelican/generators.py b/pelican/generators.py
index f5374722..7e4fe327 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -64,8 +64,7 @@ class Generator(object):
for p in processors:
p.process(context, self)
- def generate_feed(self, elements, context, filename=None,
- relative_urls=True):
+ def generate_feed(self, elements, context, filename=None):
"""Generate a feed with the list of articles provided
Return the feed. If no output_path or filename is specified, just return
@@ -75,22 +74,18 @@ class Generator(object):
:param context: the context to get the feed metadata.
:param output_path: where to output the file.
:param filename: the filename to output.
- :param relative_urls: use relative urls or absolutes ones
"""
- if relative_urls:
- site_url = self._get_relative_siteurl(filename)
- else:
- site_url = context['SITEURL']
+ site_url = context.get('SITEURL', self._get_relative_siteurl(filename))
feed = Atom1Feed(
title=context['SITENAME'],
link=site_url,
- feed_url= filename,
+ feed_url= "%s/%s" % (site_url, filename),
description=context.get('SITESUBTITLE', ''))
for element in elements:
feed.add_item(
title=element.title,
- link= element.url,
+ link= "%s/%s" % (site_url, element.url),
description=element.content,
author_name=getattr(element, 'author', 'John Doe'),
pubdate=element.date)
From a8ac5c626868850565ad66bbfed1c894f52a7ef6 Mon Sep 17 00:00:00 2001
From: Philippe Pepiot
Date: Wed, 1 Dec 2010 02:48:11 +0100
Subject: [PATCH 0011/2735] Support RSS2 feeds
---
README.rst | 2 ++
pelican/generators.py | 10 +++++++---
pelican/processors.py | 10 ++++++++++
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/README.rst b/README.rst
index 60ac7cae..8846f34f 100644
--- a/README.rst
+++ b/README.rst
@@ -109,8 +109,10 @@ Setting name what it does ?
`STATIC_PATHS` The static paths you want to copy under "static"
`FEED` relative url to output the feed. Default is
`feeds/all.atom.xml`
+`FEED_RSS` relative url to output the rss feed. Default is None (no rss)
`CATEGORY_FEED` Where to put the categories feeds. default is
`feeds/%s.atom.xml`
+`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')
======================= =======================================================
diff --git a/pelican/generators.py b/pelican/generators.py
index 7e4fe327..2c97d76a 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -4,7 +4,7 @@ from codecs import open
from jinja2 import Environment, FileSystemLoader
from jinja2.exceptions import TemplateNotFound
-from feedgenerator import Atom1Feed
+from feedgenerator import Atom1Feed, Rss201rev2Feed
from pelican.settings import read_settings
from pelican.utils import clean_output_dir
@@ -64,7 +64,8 @@ class Generator(object):
for p in processors:
p.process(context, self)
- def generate_feed(self, elements, context, filename=None):
+ def generate_feed(self, elements, context, filename=None,
+ feed_type='atom'):
"""Generate a feed with the list of articles provided
Return the feed. If no output_path or filename is specified, just return
@@ -74,10 +75,13 @@ class Generator(object):
:param context: the context to get the feed metadata.
:param output_path: where to output the file.
:param filename: the filename to output.
+ :param feed_type: the feed type to use (atom or rss)
"""
site_url = context.get('SITEURL', self._get_relative_siteurl(filename))
- feed = Atom1Feed(
+ feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
+
+ feed = feed_class(
title=context['SITENAME'],
link=site_url,
feed_url= "%s/%s" % (site_url, filename),
diff --git a/pelican/processors.py b/pelican/processors.py
index c209cafd..fe21e9f3 100644
--- a/pelican/processors.py
+++ b/pelican/processors.py
@@ -35,11 +35,21 @@ class ArticlesProcessor(Processor):
generator.generate_feed(self.articles, context, context['FEED'])
+ if 'FEED_RSS' in context:
+ generator.generate_feed(self.articles, context,
+ context['FEED_RSS'], feed_type='rss')
+
for cat, arts in self.categories.items():
arts.sort(key=attrgetter('date'), reverse=True)
generator.generate_feed(arts, context,
context['CATEGORY_FEED'] % cat)
+ if 'CATEGORY_FEED_RSS' in context:
+ generator.generate_feed(arts, context,
+ context['CATEGORY_FEED_RSS'] % cat,
+ feed_type='rss')
+
+
def generate_pages(self, context, generator):
"""Generate the pages on the disk"""
From 8fd1c50086b1d520bcf59fa561e8f1ed4676c42f Mon Sep 17 00:00:00 2001
From: Philippe Pepiot
Date: Wed, 1 Dec 2010 02:52:35 +0100
Subject: [PATCH 0012/2735] Support tags on feeds
---
pelican/generators.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/pelican/generators.py b/pelican/generators.py
index 2c97d76a..678c4bd9 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -91,6 +91,7 @@ class Generator(object):
title=element.title,
link= "%s/%s" % (site_url, element.url),
description=element.content,
+ categories=element.tags,
author_name=getattr(element, 'author', 'John Doe'),
pubdate=element.date)
From 3b7c546136d6987414e8d93804de18b5e86810e5 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 1 Dec 2010 18:45:44 +0000
Subject: [PATCH 0013/2735] Fix #15: check for tags before using them in the
feeds.
---
pelican/generators.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/generators.py b/pelican/generators.py
index 678c4bd9..cbe905a8 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -91,7 +91,7 @@ class Generator(object):
title=element.title,
link= "%s/%s" % (site_url, element.url),
description=element.content,
- categories=element.tags,
+ categories=element.tags if hasattr(element, "tags") else None,
author_name=getattr(element, 'author', 'John Doe'),
pubdate=element.date)
From 836d4ea117483ae8f030d1857d8291606c71750f Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Thu, 2 Dec 2010 03:22:24 +0000
Subject: [PATCH 0014/2735] Refactoring, Again :)
Added some more notes about how this is working on the documentation. I do think
that the overall structure is clearer now, and easiest to understand. After all,
that's how it should always be !
--HG--
rename : pelican/processors.py => pelican/generators.py
rename : pelican/generators.py => pelican/writers.py
---
README.rst | 4 +
THANKS | 1 +
bin/pelican | 39 +--
docs/internals.rst | 81 +++++
pelican/__init__.py | 104 ++++++
pelican/contents.py | 2 +-
pelican/generators.py | 316 +++++++++++-------
pelican/processors.py | 179 ----------
pelican/settings.py | 2 +-
pelican/themes/notmyidea/templates/index.html | 2 +-
pelican/utils.py | 7 +
pelican/writers.py | 80 +++++
12 files changed, 473 insertions(+), 344 deletions(-)
create mode 100644 docs/internals.rst
delete mode 100644 pelican/processors.py
create mode 100644 pelican/writers.py
diff --git a/README.rst b/README.rst
index 8846f34f..84cd594d 100644
--- a/README.rst
+++ b/README.rst
@@ -177,6 +177,10 @@ 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/
+If you feel hackish, have a look to the `pelican's internals explanations
+`_.
+
+
Feedback !
----------
diff --git a/THANKS b/THANKS
index 7c316d5f..168c0398 100644
--- a/THANKS
+++ b/THANKS
@@ -8,3 +8,4 @@ bugs or giving ideas. Thanks to them !
- Jérome Renard
- Nicolas Martin
- David Kulak
+- Arnaud Bos
diff --git a/bin/pelican b/bin/pelican
index 098bd9e0..3fe2ee57 100755
--- a/bin/pelican
+++ b/bin/pelican
@@ -1,38 +1,3 @@
#!/usr/bin/env python
-import argparse
-
-from pelican.generators import Generator
-from pelican.processors import (ArticlesProcessor, PagesProcessor,
- StaticProcessor, PdfProcessor)
-
-parser = argparse.ArgumentParser(description="""A tool to generate a
-static blog, with restructured text input files.""")
-
-parser.add_argument(dest='path',
- 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'
- 'use the default one included with pelican.')
-parser.add_argument('-o', '--output', dest='output',
- help='Where to output the generated files. If not specified, a directory'
- ' will be created, named "output" in the current path.')
-parser.add_argument('-m', '--markup', default='rst, md', dest='markup',
- help='the markup language to use. Currently only ReSTreucturedtext is'
- ' available.')
-parser.add_argument('-s', '--settings', dest='settings',
- help='the settings of the application. Default to None.')
-
-
-if __name__ == '__main__':
- args = parser.parse_args()
- markup = [a.split()[0] for a in args.markup.split(',')]
-
- generator = Generator(args.settings, args.path, args.theme,
- args.output, markup)
-
- processors = [ArticlesProcessor, PagesProcessor, StaticProcessor]
- if generator.settings['PDF_PROCESSOR']:
- processors.append(PdfProcessor)
-
- generator.run(processors)
- print "Enjoy !"
+from pelican import main
+main()
diff --git a/docs/internals.rst b/docs/internals.rst
new file mode 100644
index 00000000..80fc8661
--- /dev/null
+++ b/docs/internals.rst
@@ -0,0 +1,81 @@
+Pelican internals
+#################
+
+This section describe how pelican is working internally. As you'll see, it's
+quite simple, but a bit of documentation doesn't hurt :)
+
+Overall structure
+=================
+
+What `pelican` does, is taking a list of files, and processing them, to some
+sort of output. Usually, the files are restructured text and markdown files,
+and the output is a blog, but it can be anything you want.
+
+I've separated the logic in different classes and concepts:
+
+* `writers` are responsible of all the writing process of the
+ files. It's writing .html files, RSS feeds and so on. Since those operations
+ are commonly used, the object is created once, and then passed to the
+ generators.
+
+* `readers` are used to read from various formats (Markdown, and Restructured
+ Text for now, but the system is extensible). Given a file, they return
+ metadata (author, tags, category etc) and content (HTML formated)
+
+* `generators` generate the different outputs. For instance, pelican comes with
+ `ArticlesGenerator` and `PageGenerator`, into others. Given
+ a configurations, they can do whatever they want. Most of the time it's
+ generating files from inputs.
+
+* `pelican` also uses `templates`, so it's easy to write you own theme. The
+ syntax is `jinja2`, and, trust me, really easy to learn, so don't hesitate
+ a second.
+
+How to implement a new reader ?
+===============================
+
+There is an awesome markup language you want to add to pelican ?
+Well, the only thing you have to do is to create a class that have a `read`
+method, that is returning an HTML content and some metadata.
+
+Take a look to the Markdown reader::
+
+ class MarkdownReader(object):
+
+ def read(self, filename):
+ """Parse content and metadata of markdown files"""
+ text = open(filename)
+ md = Markdown(extensions = ['meta', 'codehilite'])
+ content = md.convert(text)
+
+ metadatas = {}
+ for name, value in md.Meta.items():
+ if name in _METADATAS_FIELDS:
+ meta = _METADATAS_FIELDS[name](value[0])
+ else:
+ meta = value[0]
+ metadatas[name.lower()] = meta
+ return content, metadatas
+
+Simple isn't it ?
+
+How to implement a new generator ?
+==================================
+
+Generators have basically two important methods. You're not forced to create
+both, only the existing ones will be called.
+
+* `generate_context`, that is called in a first place, for all the generators.
+ Do whatever you have to do, and update the global context if needed. This
+ context is shared between all generators, and will be passed to the
+ templates. For instance, the `PageGenerator` `generate_context` method find
+ all the pages, transform them into objects, and populate the context with
+ them. Be careful to *not* output anything using this context at this stage,
+ as it is likely to change by the effect of others generators.
+
+* `generate_output` is then called. And guess what is it made for ? Oh,
+ generating the output :) That's here that you may want to look at the context
+ and call the methods of the `writer` object, that is passed at the first
+ argument of this function. In the `PageGenerator` example, this method will
+ look at all the pages recorded in the global context, and output a file on
+ the disk (using the writer method `write_file`) for each page encountered.
diff --git a/pelican/__init__.py b/pelican/__init__.py
index e69de29b..3e7c90ec 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -0,0 +1,104 @@
+import argparse
+import os
+
+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)
+
+
+def init_params(settings=None, path=None, theme=None, output_path=None,
+ markup=None):
+ """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]
+
+ # 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']
+
+ # 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)
+
+ if 'SITEURL' not in settings:
+ settings['SITEURL'] = output_path
+
+ # get the list of files to parse
+ if not path:
+ raise Exception('you need to specify a path to search the docs on !')
+
+ return settings, path, theme, output_path, markup
+
+
+def run_generators(generators, settings, path, theme, output_path, markup):
+ """Run the generators and return"""
+
+ context = settings.copy()
+ generators = [p(context, settings, path, theme, output_path, markup)
+ for p in generators]
+
+ writer = Writer(output_path)
+
+ 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):
+ clean_output_dir(output_path)
+
+ for p in generators:
+ if hasattr(p, 'generate_output'):
+ p.generate_output(writer)
+
+
+def run_pelican(settings, path, theme, output_path, markup):
+ """Run pelican with the given parameters"""
+
+ params = init_params(settings, path, theme, output_path, markup)
+ generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
+ if params[0]['PDF_GENERATOR']: # param[0] is settings
+ processors.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',
+ 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'
+ 'use the default one included with pelican.')
+ parser.add_argument('-o', '--output', dest='output',
+ help='Where to output the generated files. If not specified, a directory'
+ ' will be created, named "output" in the current path.')
+ parser.add_argument('-m', '--markup', default='rst, md', dest='markup',
+ help='the markup language to use. Currently only ReSTreucturedtext is'
+ ' available.')
+ parser.add_argument('-s', '--settings', dest='settings',
+ help='the settings of the application. Default to None.')
+ args = parser.parse_args()
+ markup = [a.split()[0] for a in args.markup.split(',')]
+
+ run_pelican(args.settings, args.path, args.theme, args.output, markup)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/pelican/contents.py b/pelican/contents.py
index 815a0cde..0caceed0 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -2,7 +2,7 @@ from pelican.utils import slugify, truncate_html_words
class Page(object):
- """Represents a page..
+ """Represents a page
Given a content, and metadatas, create an adequate object.
:param string: the string to parse, containing the original content.
diff --git a/pelican/generators.py b/pelican/generators.py
index cbe905a8..34a1b181 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -1,137 +1,29 @@
-# -*- coding: utf-8 -*-
+from operator import attrgetter
+from datetime import datetime
import os
-from codecs import open
from jinja2 import Environment, FileSystemLoader
from jinja2.exceptions import TemplateNotFound
-from feedgenerator import Atom1Feed, Rss201rev2Feed
-from pelican.settings import read_settings
-from pelican.utils import clean_output_dir
+from pelican.utils import update_dict, copytree
+from pelican.contents import Article, Page, is_valid_content
+from pelican.readers import read_file
_TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories',
'archives', 'page')
+_DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives')
class Generator(object):
- """Handle all generation process: files writes, feed creation, and this
- kind of basic stuff"""
-
- def __init__(self, settings=None, path=None, theme=None, output_path=None,
- markup=None):
- """Read the settings, and performs some checks on the environment
- before doing anything else.
- """
- if settings is None:
- settings = {}
- self.settings = read_settings(settings)
- self.path = path or self.settings['PATH']
- if self.path.endswith('/'):
- self.path = self.path[:-1]
+ """Baseclass generator"""
- self.theme = theme or self.settings['THEME']
- output_path = output_path or self.settings['OUTPUT_PATH']
- self.output_path = os.path.realpath(output_path)
- self.markup = markup or self.settings['MARKUP']
+ def __init__(self, *args, **kwargs):
+ for idx, item in enumerate(('context', 'settings', 'path', 'theme',
+ 'output_path', 'markup')):
+ setattr(self, item, args[idx])
- 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" % self.theme)
-
- if 'SITEURL' not in self.settings:
- self.settings['SITEURL'] = self.output_path
-
- # 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, processors):
- """Get the context from each processor, and then process them"""
- context = self.settings.copy()
- processors = [p() for p in processors]
-
- for p in processors:
- if hasattr(p, 'preprocess'):
- p.preprocess(context, self)
-
- if self.output_path not in os.path.realpath(self.path):
- clean_output_dir(self.output_path)
-
- for p in processors:
- p.process(context, self)
-
- def generate_feed(self, elements, context, filename=None,
- feed_type='atom'):
- """Generate a feed with the list of articles provided
-
- Return the feed. If no output_path or filename is specified, just return
- the feed object.
-
- :param articles: the articles to put on the feed.
- :param context: the context to get the feed metadata.
- :param output_path: where to output the file.
- :param filename: the filename to output.
- :param feed_type: the feed type to use (atom or rss)
- """
- site_url = context.get('SITEURL', self._get_relative_siteurl(filename))
-
- feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
-
- 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)
-
- 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
-
- def generate_file(self, name, template, context, relative_urls=True,
- **kwargs):
- """Write the file with the given informations
-
- :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 **kwargs: additional variables to pass to the templates
- """
- context = context.copy()
- if relative_urls:
- context['SITEURL'] = self._get_relative_siteurl(name)
-
- context.update(kwargs)
- output = template.render(context)
- 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
+ for arg, value in kwargs.items():
+ setattr(self, arg, value)
def get_templates(self):
"""Return the templates to use.
@@ -166,7 +58,181 @@ class Generator(object):
files.extend([os.sep.join((root, f)) for f in temp_files
if True in [f.endswith(ext) for ext in extensions]])
return files
-
- def _get_relative_siteurl(self, filename):
- """Return the siteurl relative to the given filename"""
- return '../' * filename.count('/') + '.'
+
+ def _update_context(self, items):
+ """Update the context with the given items from the currrent
+ processor.
+ """
+ for item in items:
+ value = getattr(self, item)
+ if hasattr(value, 'items'):
+ value = value.items()
+ self.context[item] = value
+
+
+class ArticlesGenerator(Generator):
+ """Generate blog articles"""
+
+ def __init__(self, *args, **kwargs):
+ """initialize properties"""
+ self.articles = []
+ self.dates = {}
+ self.tags = {}
+ self.categories = {}
+ super(ArticlesGenerator, self).__init__(*args, **kwargs)
+
+ def generate_feeds(self, writer):
+ """Generate the feeds from the current context, and output files."""
+
+ writer.write_feed(self.articles, self.context, self.settings['FEED'])
+
+ if 'FEED_RSS' in self.settings:
+ writer.write_feed(self.articles, self.context,
+ self.settings['FEED_RSS'], feed_type='rss')
+
+ for cat, arts in self.categories.items():
+ arts.sort(key=attrgetter('date'), reverse=True)
+ writer.write_feed(arts, self.context,
+ self.settings['CATEGORY_FEED'] % cat)
+
+ if 'CATEGORY_FEED_RSS' in self.settings:
+ writer.write_feed(arts, self.context,
+ self.settings['CATEGORY_FEED_RSS'] % cat,
+ feed_type='rss')
+
+
+ def generate_pages(self, writer):
+ """Generate the pages on the disk
+ TODO: change the name"""
+
+ templates = self.get_templates()
+ write = writer.write_file
+ for template in _DIRECT_TEMPLATES:
+ write('%s.html' % template, templates[template], self.context,
+ blog=True)
+ for tag in self.tags:
+ write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag)
+ for cat in self.categories:
+ write('category/%s.html' % cat, templates['category'], self.context,
+ category=cat, articles=self.categories[cat])
+ for article in self.articles:
+ write('%s' % article.url,
+ templates['article'], self.context, article=article,
+ category=article.category)
+
+ def generate_context(self):
+ """change the context"""
+
+ # return the list of files to use
+ files = self.get_files(self.path, exclude=['pages',])
+ for f in files:
+ content, metadatas = read_file(f)
+
+ # 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)+'/', '')
+
+ if category == self.path:
+ category = self.settings['DEFAULT_CATEGORY']
+
+ if category != '':
+ metadatas['category'] = unicode(category)
+
+ if 'date' not in metadatas.keys()\
+ and self.settings['FALLBACK_ON_FS_DATE']:
+ metadatas['date'] = datetime.fromtimestamp(os.stat(f).st_ctime)
+
+ article = Article(content, metadatas, settings=self.settings,
+ filename=f)
+ if not is_valid_content(article, f):
+ continue
+
+ update_dict(self.categories, article.category, article)
+ if hasattr(article, 'tags'):
+ for tag in article.tags:
+ update_dict(self.tags, tag, article)
+ self.articles.append(article)
+
+ # sort the articles by date
+ self.articles.sort(key=attrgetter('date'), reverse=True)
+ self.dates = list(self.articles)
+ self.dates.sort(key=attrgetter('date'))
+ # and generate the output :)
+ self._update_context(('articles', 'dates', 'tags', 'categories'))
+
+ def generate_output(self, writer):
+ self.generate_feeds(writer)
+ self.generate_pages(writer)
+
+
+class PagesGenerator(Generator):
+ """Generate pages"""
+
+ def __init__(self, *args, **kwargs):
+ self.pages = []
+ super(PagesGenerator, self).__init__(*args, **kwargs)
+
+ def generate_context(self):
+ for f in self.get_files(os.sep.join((self.path, 'pages'))):
+ content, metadatas = read_file(f)
+ page = Page(content, metadatas, settings=self.settings,
+ filename=f)
+ if not is_valid_content(page, f):
+ continue
+ self.pages.append(page)
+
+ self._update_context(('pages', ))
+
+ def generate_output(self, writer):
+ templates = self.get_templates()
+ for page in self.pages:
+ writer.write_file('pages/%s' % page.url, templates['page'],
+ self.context, page=page)
+ self._update_context(('pages',))
+
+
+class StaticGenerator(Generator):
+ """copy static paths to output"""
+
+ def _copy_paths(self, paths, source, destination, output_path,
+ final_path=None):
+ for path in paths:
+ copytree(path, source, os.path.join(output_path, destination),
+ final_path)
+
+ def generate_output(self, writer):
+ self._copy_paths(self.settings['STATIC_PATHS'], self.path,
+ 'static', self.output_path)
+ self._copy_paths(self.settings['THEME_PATHS'], self.theme,
+ 'theme', self.output_path, '.')
+
+
+class PdfGenerator(Generator):
+ """Generate PDFs on the output dir, for all articles and pages coming from
+ rst"""
+ def __init__(self, *args, **kwargs):
+ try:
+ from rst2pdf.createpdf import RstToPdf
+ self.pdfcreator = RstToPdf(breakside=0, stylesheets=['twelvepoint'])
+ except ImportError:
+ raise Exception("unable to find rst2pdf")
+ super(PdfGenerator, self).__init(*args, **kwargs)
+
+ def _create_pdf(self, obj, output_path):
+ if obj.filename.endswith(".rst"):
+ self.pdfcreator.createPdf(text=open(obj.filename).read(),
+ output=os.path.join(output_path, "%s.pdf" % obj.slug))
+
+ def generate_context(self):
+ pdf_path = os.path.join(self.output_path, 'pdf')
+ try:
+ os.mkdir(pdf_path)
+ except OSError:
+ pass
+
+ for article in self.context['articles']:
+ self._create_pdf(article, pdf_path)
+
+ for page in self.context['pages']:
+ self._create_pdf(page, pdf_path)
diff --git a/pelican/processors.py b/pelican/processors.py
deleted file mode 100644
index fe21e9f3..00000000
--- a/pelican/processors.py
+++ /dev/null
@@ -1,179 +0,0 @@
-from operator import attrgetter
-from datetime import datetime
-import os
-
-from pelican.utils import update_dict, copytree
-from pelican.contents import Article, Page, is_valid_content
-from pelican.readers import read_file
-
-_DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives')
-
-
-class Processor(object):
-
- def _update_context(self, context, items):
- """Update the context with the given items from the currrent
- processor.
- """
- for item in items:
- value = getattr(self, item)
- if hasattr(value, 'items'):
- value = value.items()
- context[item] = value
-
-
-class ArticlesProcessor(Processor):
-
- def __init__(self, settings=None):
- self.articles = []
- self.dates = {}
- self.tags = {}
- self.categories = {}
-
- def generate_feeds(self, context, generator):
- """Generate the feeds from the current context, and output files."""
-
- generator.generate_feed(self.articles, context, context['FEED'])
-
- if 'FEED_RSS' in context:
- generator.generate_feed(self.articles, context,
- context['FEED_RSS'], feed_type='rss')
-
- for cat, arts in self.categories.items():
- arts.sort(key=attrgetter('date'), reverse=True)
- generator.generate_feed(arts, context,
- context['CATEGORY_FEED'] % cat)
-
- if 'CATEGORY_FEED_RSS' in context:
- generator.generate_feed(arts, context,
- context['CATEGORY_FEED_RSS'] % cat,
- feed_type='rss')
-
-
- def generate_pages(self, context, generator):
- """Generate the pages on the disk"""
-
- templates = generator.get_templates()
- generate = generator.generate_file
- for template in _DIRECT_TEMPLATES:
- generate('%s.html' % template, templates[template], context, blog=True)
- for tag in self.tags:
- generate('tag/%s.html' % tag, templates['tag'], context, tag=tag)
- for cat in self.categories:
- generate('category/%s.html' % cat, templates['category'], context,
- category=cat, articles=self.categories[cat])
- for article in self.articles:
- generate('%s' % article.url,
- templates['article'], context, article=article,
- category=article.category)
-
- def preprocess(self, context, generator):
-
- # build the list of articles / categories / etc.
- files = generator.get_files(generator.path, exclude=['pages',])
- for f in files:
- content, metadatas = read_file(f)
- if 'category' not in metadatas.keys():
- category = os.path.dirname(f).replace(
- os.path.expanduser(generator.path)+'/', '')
-
- if category == generator.path:
- category = context['DEFAULT_CATEGORY']
-
- if category != '':
- metadatas['category'] = unicode(category)
-
- if 'date' not in metadatas.keys() and context['FALLBACK_ON_FS_DATE']:
- metadatas['date'] = datetime.fromtimestamp(os.stat(f).st_ctime)
-
- article = Article(content, metadatas, settings=generator.settings,
- filename=f)
- if not is_valid_content(article, f):
- continue
-
- update_dict(self.categories, article.category, article)
- if hasattr(article, 'tags'):
- for tag in article.tags:
- update_dict(self.tags, tag, article)
- self.articles.append(article)
-
- # sort the articles by date
- self.articles.sort(key=attrgetter('date'), reverse=True)
- self.dates = list(self.articles)
- self.dates.sort(key=attrgetter('date'))
- # and generate the output :)
- self._update_context(context, ('articles', 'dates', 'tags', 'categories'))
-
- def process(self, context, generator):
- self.generate_feeds(context, generator)
- self.generate_pages(context, generator)
-
-
-class PagesProcessor(Processor):
- """Generate pages"""
-
- def __init__(self):
- self.pages = []
-
- def preprocess(self, context, generator):
- for f in generator.get_files(os.sep.join((generator.path, 'pages'))):
- content, metadatas = read_file(f)
- page = Page(content, metadatas, settings=generator.settings,
- filename=f)
- if not is_valid_content(page, f):
- continue
- self.pages.append(page)
-
- context['PAGES'] = self.pages
-
- def process(self, context, generator):
- templates = generator.get_templates()
- for page in self.pages:
- generator.generate_file('pages/%s' % page.url,
- templates['page'], context, page=page)
- self._update_context(context, ('pages',))
-
-
-class StaticProcessor(Processor):
- """copy static paths to output"""
-
- def _copy_paths(self, paths, source, destination, output_path,
- final_path=None):
- for path in paths:
- copytree(path, source, os.path.join(output_path, destination),
- final_path)
-
- def process(self, context, generator):
- self._copy_paths(generator.settings['STATIC_PATHS'], generator.path,
- 'static', generator.output_path)
- self._copy_paths(generator.settings['THEME_PATHS'], generator.theme,
- 'theme', generator.output_path, '.')
-
-
-class PdfProcessor(Processor):
- """Generate PDFs on the output dir, for all articles and pages coming from
- rst"""
- def __init__(self):
- try:
- from rst2pdf.createpdf import RstToPdf
- self.pdfcreator = RstToPdf(breakside=0, stylesheets=['twelvepoint'])
- except ImportError:
- raise Exception("unable to find rst2pdf")
-
- def _create_pdf(self, obj, output_path):
- if obj.filename.endswith(".rst"):
- self.pdfcreator.createPdf(text=open(obj.filename).read(),
- output=os.path.join(output_path, "%s.pdf" % obj.slug))
-
- def process(self, context, generator):
- pdf_path = os.path.join(generator.output_path, 'pdf')
- try:
- os.mkdir(pdf_path)
- except OSError:
- pass
-
- for article in context['articles']:
- self._create_pdf(article, pdf_path)
-
- for page in context['pages']:
- self._create_pdf(page, pdf_path)
diff --git a/pelican/settings.py b/pelican/settings.py
index 79de75cd..431340cc 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -12,7 +12,7 @@ _DEFAULT_CONFIG = {'PATH': None,
'CATEGORY_FEED': 'feeds/%s.atom.xml',
'SITENAME': 'A Pelican Blog',
'DISPLAY_PAGES_ON_MENU': True,
- 'PDF_PROCESSOR': False,
+ 'PDF_GENERATOR': False,
'DEFAULT_CATEGORY': 'misc',
'FALLBACK_ON_FS_DATE': True,
'CSS_FILE': 'main.css',
diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html
index 57f0b22f..aa6f30ec 100644
--- a/pelican/themes/notmyidea/templates/index.html
+++ b/pelican/themes/notmyidea/templates/index.html
@@ -1,6 +1,6 @@
{% extends "base.html" %}
-{% block content %}
{% block content_title %}{% endblock %}
+{% block content %}
{% if articles %}
{% for article in articles %}
{% if loop.index == 1 %}
diff --git a/pelican/utils.py b/pelican/utils.py
index 79bd4e38..c1410039 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -64,6 +64,7 @@ def copytree(path, origin, destination, topath=None):
except OSError:
pass
+
def clean_output_dir(path):
"""Remove all the files from the output directory"""
@@ -73,6 +74,12 @@ def clean_output_dir(path):
except Exception as e:
pass
+
+def get_relative_path(filename):
+ """Return the relative path to the given filename"""
+ return '../' * filename.count('/') + '.'
+
+
def truncate_html_words(s, num, end_text='...'):
"""Truncates HTML to a certain number of words (not counting tags and
comments). Closes opened tags if they were correctly closed in the given
diff --git a/pelican/writers.py b/pelican/writers.py
new file mode 100644
index 00000000..e71156c0
--- /dev/null
+++ b/pelican/writers.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+import os
+from codecs import open
+
+from feedgenerator import Atom1Feed, Rss201rev2Feed
+
+from pelican.utils import get_relative_path
+
+class Writer(object):
+
+ def __init__(self, output_path):
+ self.output_path = output_path
+
+ def write_feed(self, elements, context, filename=None, feed_type='atom'):
+ """Generate a feed with the list of articles provided
+
+ Return the feed. If no output_path or filename is specified, just return
+ the feed object.
+
+ :param articles: the articles to put on the feed.
+ :param context: the context to get the feed metadata.
+ :param output_path: where to output the file.
+ :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))
+
+ feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
+
+ 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)
+
+ 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
+
+ def write_file(self, name, template, context, relative_urls=True,
+ **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 **kwargs: additional variables to pass to the templates
+ """
+ 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
From 9bdb5b8a885e1e79c3579eb60245e3b9966e32c9 Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Fri, 3 Dec 2010 09:28:40 -0800
Subject: [PATCH 0015/2735] A large image causes problem in article excerpt. It
comes on following excerpt area. I think the best thing to do is to disable
display of images in excerpt.
---
pelican/themes/notmyidea/static/css/main.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/themes/notmyidea/static/css/main.css b/pelican/themes/notmyidea/static/css/main.css
index fd443ebe..f0fd028b 100644
--- a/pelican/themes/notmyidea/static/css/main.css
+++ b/pelican/themes/notmyidea/static/css/main.css
@@ -355,7 +355,7 @@ img.left, figure.left {float: right; margin: 0 0 2em 2em;}
}
li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
#content > .hentry {padding: 1em 0;}
-
+.hentry img{display : none ;}
.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
.entry-title a:visited {background-color: #fff;}
From f49c91070a601705292673e0912dd85da2ed45a2 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sun, 5 Dec 2010 19:15:02 +0000
Subject: [PATCH 0016/2735] Add a documentation about themes.
---
README.rst | 45 ++++++++++++++--------
docs/themes.rst | 90 +++++++++++++++++++++++++++++++++++++++++++
pelican/generators.py | 10 +++--
pelican/settings.py | 2 +-
4 files changed, 125 insertions(+), 22 deletions(-)
create mode 100644 docs/themes.rst
diff --git a/README.rst b/README.rst
index 84cd594d..17905c32 100644
--- a/README.rst
+++ b/README.rst
@@ -36,8 +36,8 @@ You can also use a markdown syntax (with a file ending in `.md`)::
Put you content here.
Note that none of those are mandatory: if the date is not specified, pelican will
-rely on the mtime of your file, and the category can also be determined by the
-directory where the rst file is. For instance, the category of
+rely on the mtime of your file, and the category can also be determined by the
+directory where the rst file is. For instance, the category of
`python/foobar/myfoobar.rst` is `foobar`.
Features
@@ -46,8 +46,8 @@ Features
Pelican currently supports:
* blog articles
-* comments, via an external service (disqus). Please notice that while
- it's useful, it's an external service, and you'll not manage the
+* comments, via an external service (disqus). Please notice that while
+ it's useful, it's an external service, and you'll not manage the
comments by yourself. It could potentially eat your data.
* theming support (themes are done using `jinja2 `_)
* PDF generation of the articles/pages (optional).
@@ -55,7 +55,7 @@ Pelican currently supports:
Getting started — Generate your blog
-------------------------------------
-You're ready? Let's go ! You can install pelican in a lot of different ways,
+You're ready? Let's go ! You can install pelican in a lot of different ways,
the simpler one is via `pip `_::
$ pip install pelican
@@ -67,7 +67,7 @@ Then, you have just to launch pelican, like this::
And… that's all! You can see your weblog generated on the `content/` folder.
This one will just generate a simple output, with the default theme. It's not
-really sexy, as it's a simple HTML output (without any style).
+really sexy, as it's a simple HTML output (without any style).
You can create your own style if you want, have a look to the help to see all
the options you can use::
@@ -82,7 +82,7 @@ the command line::
$ pelican -s path/to/your/settingsfile.py path
-Here are the available settings. Please note that all the settings you put in
+Here are the available settings. Please note that all the settings you put in
this file will be passed to the templates as well.
======================= =======================================================
@@ -96,7 +96,7 @@ Setting name what it does ?
`OUTPUT_PATH` Where to output the generated files. Default to
"output"
`SITENAME` Your site name,
-`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
+`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
Templates can follow or not this settings.
`PDF_PROCESSOR` Put True if you want to have PDF versions of your
documents. You will need to install `rst2pdf`.
@@ -106,14 +106,22 @@ Setting name what it does ?
metadata?
`MARKUP` A list of available markup languages you want to use.
moment, only available values are `rst` and `md`.
-`STATIC_PATHS` The static paths you want to copy under "static"
-`FEED` relative url to output the feed. Default is
+`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.
+`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)
-`CATEGORY_FEED` Where to put the categories feeds. default is
- `feeds/%s.atom.xml`
-`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
+`FEED_RSS` relative url to output the rss feed. Default is
+ None (no rss)
+`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')
======================= =======================================================
@@ -124,11 +132,14 @@ Themes
* notmyidea
* simple (a synonym for "full text" :)
-* martyalchin
+* 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).
+Here is `a guide on how to create your theme
+`_
+
The `notmyidea` theme can make good use of the following settings. I recommend
to use them too in your themes.
@@ -142,7 +153,7 @@ Setting name what it does ?
`LINKS` A list of tuples (Title, Url) for links to appear on
the header.
`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
- section.
+ section.
`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
======================= =======================================================
diff --git a/docs/themes.rst b/docs/themes.rst
new file mode 100644
index 00000000..7b7c0de5
--- /dev/null
+++ b/docs/themes.rst
@@ -0,0 +1,90 @@
+How to create themes for pelican
+################################
+
+Pelican uses the great `jinja2 `_ templating engine to
+generate it's HTML output.
+
+Structure
+=========
+
+To make your own theme, you must follow the following structure::
+
+ ├── static
+ │ ├── css
+ │ └── images
+ └── templates
+ ├── archives.html
+ ├── article.html
+ ├── categories.html
+ ├── category.html
+ ├── index.html
+ ├── page.html
+ ├── tag.html
+ └── tags.html
+
+* `static` contains all the static content. It will be copied on the output
+ `theme/static` folder then. I've put the css and image folders, but they are
+ just examples. Put what you need here.
+
+* `templates` contains all the templates that will be used to generate the content.
+ I've just put the mandatory templates here, you can define your own if it helps
+ you to organize yourself while doing the theme.
+
+Templates and variables
+=======================
+
+It's using a simple syntax, that you can embbed into your html pages.
+This document describes which templates should exists on a theme, and which
+variables will be passed to each template, while generating it.
+
+All templates will receive the variables defined in your settings file, if they
+are in caps. You can access them directly.
+
+Common variables
+----------------
+
+============= ===================================================
+Variable Description
+============= ===================================================
+articles That's the list of articles, ordsered desc. by date
+ all the elements are `Article` objects, so you can
+ access their properties (e.g. title, summary, author
+ etc.
+dates The same list of article, but ordered by date,
+ ascending
+tags A dict containing each tags (keys), and the list of
+ relative articles.
+categories A dict containing each category (keys), and the
+ list of relative articles.
+pages The list of pages
+============= ===================================================
+
+category.html
+-------------
+
+============= ===================================================
+Variable Description
+============= ===================================================
+articles The articles of this category
+category The name of the category being processed
+============= ===================================================
+
+article.html
+-------------
+
+============= ===================================================
+Variable Description
+============= ===================================================
+article The article object to be displayed
+category The name of the category of the current article
+============= ===================================================
+
+tag.html
+--------
+
+============= ===================================================
+Variable Description
+============= ===================================================
+tag The name of the tag being processed
+articles Articles related to this tag
+============= ===================================================
diff --git a/pelican/generators.py b/pelican/generators.py
index 34a1b181..8ba776ab 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -110,8 +110,9 @@ class ArticlesGenerator(Generator):
for template in _DIRECT_TEMPLATES:
write('%s.html' % template, templates[template], self.context,
blog=True)
- for tag in self.tags:
- write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag)
+ for tag, articles in self.tags.items():
+ write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag,
+ articles=articles)
for cat in self.categories:
write('category/%s.html' % cat, templates['category'], self.context,
category=cat, articles=self.categories[cat])
@@ -193,7 +194,8 @@ class PagesGenerator(Generator):
class StaticGenerator(Generator):
- """copy static paths to output"""
+ """copy static paths (what you want to cpy, like images, medias etc.
+ to output"""
def _copy_paths(self, paths, source, destination, output_path,
final_path=None):
@@ -204,7 +206,7 @@ class StaticGenerator(Generator):
def generate_output(self, writer):
self._copy_paths(self.settings['STATIC_PATHS'], self.path,
'static', self.output_path)
- self._copy_paths(self.settings['THEME_PATHS'], self.theme,
+ self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
'theme', self.output_path, '.')
diff --git a/pelican/settings.py b/pelican/settings.py
index 431340cc..88d2bde6 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -7,7 +7,7 @@ _DEFAULT_CONFIG = {'PATH': None,
'OUTPUT_PATH': 'output/',
'MARKUP': ('rst', 'md'),
'STATIC_PATHS': ['images',],
- 'THEME_PATHS': ['static',],
+ 'THEME_STATIC_PATHS': ['static',],
'FEED': 'feeds/all.atom.xml',
'CATEGORY_FEED': 'feeds/%s.atom.xml',
'SITENAME': 'A Pelican Blog',
From 44d2968976249f02302ef5f926019eb22f510ae3 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sun, 5 Dec 2010 22:54:42 +0000
Subject: [PATCH 0017/2735] Update the sample config with the one I use.
---
samples/pelican.conf.py | 1 +
samples/test_settings.py | 21 ---------------------
2 files changed, 1 insertion(+), 21 deletions(-)
create mode 120000 samples/pelican.conf.py
delete mode 100644 samples/test_settings.py
diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py
new file mode 120000
index 00000000..f74e77a9
--- /dev/null
+++ b/samples/pelican.conf.py
@@ -0,0 +1 @@
+/home/alexis/projets/notmyidea.org/blog/pelican.conf.py
\ No newline at end of file
diff --git a/samples/test_settings.py b/samples/test_settings.py
deleted file mode 100644
index 85729eb7..00000000
--- a/samples/test_settings.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-PATH = 'samples/content'
-THEME = 'samples/themes/notmyidea'
-AUTHOR = u'Alexis Métaireau'
-
-SITENAME = 'Pelican'
-SITESUBTITLE = u"%s's weblog" % AUTHOR
-SITEURL = 'http://blog.notmyidea.org'
-
-SITEROLL = (('Biologeek', 'http://biologeek.org'),
- ('Filyb', "http://filyb.info/"),
- ('Libert-fr', "http://www.libert-fr.com"),
- ('N1k0', "http://prendreuncafe.com/blog/"),
- (u'Tarek Ziadé', "http://ziade.org/blog"),
- ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
-
-MENUITEMS = (('home', 'http://notmyidea.org'),
- ('contact', 'mailto: alexis notmyidea org'),)
-
-SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
- ('lastfm', 'http://lastfm.com/user/akounet'),)
From f352d16971a6bceddc2318d2dca122dd5a41f9e3 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Sun, 5 Dec 2010 23:11:45 +0000
Subject: [PATCH 0018/2735] Put the real version of the conf file, not the
symbolic link ;)
---
samples/pelican.conf.py | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
mode change 120000 => 100644 samples/pelican.conf.py
diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py
deleted file mode 120000
index f74e77a9..00000000
--- a/samples/pelican.conf.py
+++ /dev/null
@@ -1 +0,0 @@
-/home/alexis/projets/notmyidea.org/blog/pelican.conf.py
\ No newline at end of file
diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py
new file mode 100644
index 00000000..f921ee91
--- /dev/null
+++ b/samples/pelican.conf.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+AUTHOR = u'Alexis Métaireau'
+SITENAME = u"Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_PROCESSOR = False
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+ ('Filyb', "http://filyb.info/"),
+ ('Libert-fr', "http://www.libert-fr.com"),
+ ('N1k0', "http://prendreuncafe.com/blog/"),
+ (u'Tarek Ziadé', "http://ziade.org/blog"),
+ ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+ ('lastfm', 'http://lastfm.com/user/akounet'),
+ ('github', 'http://github.com/ametaireau'),)
From 8cca3d8f01392dfd4156b8329b1c04c09e2d5c3c Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Mon, 6 Dec 2010 06:09:19 -0800
Subject: [PATCH 0019/2735] Original theme force image alignment on the right.
Removed it to choose it with a markup langage.
---
pelican/themes/notmyidea/static/css/main.css | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/pelican/themes/notmyidea/static/css/main.css b/pelican/themes/notmyidea/static/css/main.css
index f0fd028b..0972ca0b 100644
--- a/pelican/themes/notmyidea/static/css/main.css
+++ b/pelican/themes/notmyidea/static/css/main.css
@@ -25,9 +25,7 @@ body {
text-align: left;
}
-article img{
- float: right;
-}
+
/* Headings */
h1 {font-size: 2em }
From 497b725dab61fcd5af54e624b11ed355e2b612e6 Mon Sep 17 00:00:00 2001
From: Florian Preinstorfer
Date: Fri, 10 Dec 2010 19:37:32 +0100
Subject: [PATCH 0020/2735] fixed a spelling error
---
pelican/themes/notmyidea/templates/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html
index aa6f30ec..d01e9928 100644
--- a/pelican/themes/notmyidea/templates/index.html
+++ b/pelican/themes/notmyidea/templates/index.html
@@ -23,7 +23,7 @@
{% if loop.length > 1 %}
-
Others articles
+
Other articles
{% endif %}
From bdbba6bc6859d038c3d0d149f191684cc30845d6 Mon Sep 17 00:00:00 2001
From: Florian Preinstorfer
Date: Fri, 10 Dec 2010 20:28:45 +0100
Subject: [PATCH 0021/2735] fixed generation of archive.html (template simple)
-just one loop needed
-same date format as in index.html (template simple)
---
pelican/themes/simple/templates/archives.html | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/pelican/themes/simple/templates/archives.html b/pelican/themes/simple/templates/archives.html
index 9414f5be..17877390 100644
--- a/pelican/themes/simple/templates/archives.html
+++ b/pelican/themes/simple/templates/archives.html
@@ -3,11 +3,9 @@
Archives for {{ SITENAME }}
-{% for date, articles in dates %}
- {% for article in articles %}
-
{{ article.summary }}
diff --git a/pelican/themes/notmyidea/templates/taglist.html b/pelican/themes/notmyidea/templates/taglist.html
new file mode 100644
index 00000000..4259077a
--- /dev/null
+++ b/pelican/themes/notmyidea/templates/taglist.html
@@ -0,0 +1,2 @@
+{% if article.tags %}
tags: {% for tag in article.tags %}{{ tag }}{% endfor %}
{% endif %}
+{% if PDF_PROCESSOR %}get the pdf{% endif %}
From e0e62d8b8e93cf096c8c18dc66db221772e0d8a0 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Tue, 14 Dec 2010 15:31:11 +0000
Subject: [PATCH 0027/2735] Update the documentation.
---
README.rst | 164 +--------------------------------------
docs/conf.py | 6 +-
docs/getting_started.rst | 87 +++++++++++++++++++++
docs/index.rst | 54 ++++++++++++-
docs/settings.rst | 90 +++++++++++++++++++++
5 files changed, 235 insertions(+), 166 deletions(-)
create mode 100644 docs/getting_started.rst
mode change 120000 => 100644 docs/index.rst
create mode 100644 docs/settings.rst
diff --git a/README.rst b/README.rst
index 027f8198..71d94290 100644
--- a/README.rst
+++ b/README.rst
@@ -9,37 +9,6 @@ Pelican is a simple weblog generator, writen in python.
* Easy to interface with DVCSes and web hooks
* Completely static output, so easy to host anywhere !
-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
-provide by hand some of those informations in your files.
-
-You could provide the metadata in the restructured text files, using the
-following syntax (give your file the `.rst` extension)::
-
- My super title
- ##############
-
- :date: 2010-10-03 10:20
- :tags: thats, awesome
- :category: yeah
- :author: Alexis Metaireau
-
-
-You can also use a markdown syntax (with a file ending in `.md`)::
-
- Date: 2010-12-03
- Title: My super title
-
- Put you content here.
-
-Note that none of those are mandatory: if the date is not specified, pelican will
-rely on the mtime of your file, and the category can also be determined by the
-directory where the rst file is. For instance, the category of
-`python/foobar/myfoobar.rst` is `foobar`.
-
Features
--------
@@ -52,142 +21,14 @@ Pelican currently supports:
* theming support (themes are done using `jinja2 `_)
* PDF generation of the articles/pages (optional).
-Getting started — Generate your blog
--------------------------------------
-
-You're ready? Let's go ! You can install pelican in a lot of different ways,
-the simpler one is via `pip `_::
-
- $ pip install pelican
-
-Then, you have just to launch pelican, like this::
-
- $ pelican /path/to/your/content/
-
-And… that's all! You can see your weblog generated on the `content/` folder.
-
-This one will just generate a simple output, with the default theme. It's not
-really sexy, as it's a simple HTML output (without any style).
-
-You can create your own style if you want, have a look to the help to see all
-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.
-
-Settings
---------
-
-Pelican is configurable thanks a configuration file, that you can pass to
-the command line::
-
- $ pelican -s path/to/your/settingsfile.py path
-
-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 ?
-======================= =======================================================
-`SITEURL` base URL of your website.
-`PATH` path to look at for input files.
-`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)
-`OUTPUT_PATH` Where to output the generated files. Default to
- "output"
-`SITENAME` Your site name,
-`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
- Templates can follow or not this settings.
-`PDF_PROCESSOR` Put True if you want to have PDF versions of your
- documents. You will need to install `rst2pdf`.
-`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
-`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
- (mtime) if it can't get informations from the
- metadata?
-`MARKUP` A list of available markup languages you want to use.
- moment, only available values are `rst` and `md`.
-`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.
-`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)
-`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')
-======================= =======================================================
-
-Themes
-------
-
-3 themes are available. 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).
-
-Here is `a guide on how to create your theme
-`_
-
-The `notmyidea` theme can make good use of the following settings. I recommend
-to use them too in your themes.
-
-======================= =======================================================
-Setting name what it does ?
-======================= =======================================================
-`GITHUB_URL` Your github URL (if you have one), it will then
- use it to create a github ribbon.
-`DISQUS_SITENAME` Pelican can handle disqus comments, specify the
- sitename you've filled in on disqus
-`LINKS` A list of tuples (Title, Url) for links to appear on
- the header.
-`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
- section.
-`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
-======================= =======================================================
-
-In addition, you can use the "wide" version of the `notmyidea` theme, by
-adding that in your configuration::
-
- CSS_FILE = "wide.css"
+Have a look to `the documentation `_ for
+more informations.
Why the name "Pelican" ?
------------------------
Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;)
-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.
-
Source code
-----------
@@ -197,7 +38,6 @@ or via git on http://github.com/ametaireau/pelican/
If you feel hackish, have a look to the `pelican's internals explanations
`_.
-
Feedback !
----------
diff --git a/docs/conf.py b/docs/conf.py
index 0efe92b2..159ddbcc 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -21,14 +21,14 @@ pygments_style = 'sphinx'
# a list of builtin themes.
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
-html_theme = 'flask_small'
+html_theme = 'flask'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
- 'index_logo': 'pelican.png',
- 'github_fork': 'ametaireau/pelican',
+# 'index_logo': 'pelican.png',
+# 'github_fork': 'ametaireau/pelican',
}
# Add any paths that contain custom themes here, relative to this directory.
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
new file mode 100644
index 00000000..df3b71d9
--- /dev/null
+++ b/docs/getting_started.rst
@@ -0,0 +1,87 @@
+Getting started
+###############
+
+Installing
+==========
+
+You're ready? Let's go ! You can install pelican in a lot of different ways,
+the simpler one is via `pip `_::
+
+ $ pip install pelican
+
+If you have the sources, you can install pelican using the distutils command
+install. I recommend to do so in a virtualenv::
+
+ $ virtualenv .
+ $ source bin/activate
+ $ 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.
+
+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
+provide by hand some of those informations in your files.
+
+You could provide the metadata in the restructured text files, using the
+following syntax (give your file the `.rst` extension)::
+
+ My super title
+ ##############
+
+ :date: 2010-10-03 10:20
+ :tags: thats, awesome
+ :category: yeah
+ :author: Alexis Metaireau
+
+
+You can also use a markdown syntax (with a file ending in `.md`)::
+
+ Date: 2010-12-03
+ Title: My super title
+
+ Put you content here.
+
+Note that none of those are mandatory: if the date is not specified, pelican will
+rely on the mtime of your file, and the category can also be determined by the
+directory where the rst file is. For instance, the category of
+`python/foobar/myfoobar.rst` is `foobar`.
+
+Generate your blog
+==================
+
+To launch pelican, just use the `pelican` command::
+
+ $ pelican /path/to/your/content/
+
+And… that's all! You can see your weblog generated on the `content/` folder.
+
+This one will just generate a simple output, with the default theme. It's not
+really sexy, as it's a simple HTML output (without any style).
+
+You can create your own style if you want, have a look to the help to see all
+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.
+
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 120000
index 89a01069..00000000
--- a/docs/index.rst
+++ /dev/null
@@ -1 +0,0 @@
-../README.rst
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..9af7ff6b
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,53 @@
+Pelican
+#######
+
+Pelican is a simple weblog generator, writen in python.
+
+* Write your weblog entries directly with your editor of choice (vim!) and
+ directly in restructured text, or markdown.
+* A simple cli-tool to (re)generate the weblog.
+* Easy to interface with DVCSes and web hooks
+* Completely static output, so easy to host anywhere !
+
+Documentation
+=============
+
+.. toctree::
+ :maxdepth: 2
+
+ getting_started
+ settings
+ themes
+ internals
+
+Features
+========
+
+Pelican currently supports:
+
+* blog articles
+* comments, via an external service (disqus). Please notice that while
+ it's useful, it's an external service, and you'll not manage the
+ comments by yourself. It could potentially eat your data.
+* theming support (themes are done using `jinja2 `_)
+* PDF generation of the articles/pages (optional).
+
+Why the name "Pelican" ?
+========================
+
+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/
+
+Feedback !
+==========
+
+If you want to see new features in Pelican, dont hesitate to tell me, to clone
+the repository, etc. That's open source, dude!
+
+Contact me at "alexis at notmyidea dot org" for any request/feedback !
+
diff --git a/docs/settings.rst b/docs/settings.rst
new file mode 100644
index 00000000..85679eb5
--- /dev/null
+++ b/docs/settings.rst
@@ -0,0 +1,90 @@
+Settings
+########
+
+Specifying the settings
+=======================
+
+Pelican is configurable thanks to a configuration file, that you can pass to
+the command line::
+
+ $ pelican -s path/to/your/settingsfile.py path
+
+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 ?
+======================= =======================================================
+`SITEURL` base URL of your website.
+`PATH` path to look at for input files.
+`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)
+`OUTPUT_PATH` Where to output the generated files. Default to
+ "output"
+`SITENAME` Your site name,
+`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
+ Templates can follow or not this settings.
+`PDF_PROCESSOR` Put True if you want to have PDF versions of your
+ documents. You will need to install `rst2pdf`.
+`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
+`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
+ (mtime) if it can't get informations from the
+ metadata?
+`MARKUP` A list of available markup languages you want to use.
+ moment, only available values are `rst` and `md`.
+`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.
+`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)
+`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')
+======================= =======================================================
+
+Themes
+======
+
+3 themes are available. 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).
+
+Here is `a guide on how to create your theme
+`_
+
+The `notmyidea` theme can make good use of the following settings. I recommend
+to use them too in your themes.
+
+======================= =======================================================
+Setting name what it does ?
+======================= =======================================================
+`GITHUB_URL` Your github URL (if you have one), it will then
+ use it to create a github ribbon.
+`DISQUS_SITENAME` Pelican can handle disqus comments, specify the
+ sitename you've filled in on disqus
+`LINKS` A list of tuples (Title, Url) for links to appear on
+ the header.
+`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
+ section.
+`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
+======================= =======================================================
+
+In addition, you can use the "wide" version of the `notmyidea` theme, by
+adding that in your configuration::
+
+ CSS_FILE = "wide.css"
From 5748a91ad706d25805c10a0a27ea4106008d00ee Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Tue, 14 Dec 2010 15:48:35 +0000
Subject: [PATCH 0028/2735] Add PAGES to the context
---
docs/getting_started.rst | 2 ++
pelican/generators.py | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index df3b71d9..a6eaf79b 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -85,3 +85,5 @@ Pages
If you create a folder named `pages`, all the files in it will be used to
generate static pages.
+Then, use the `DISPLAY_PAGES_ON_MENU` setting, which will add all the pages to
+the menu.
diff --git a/pelican/generators.py b/pelican/generators.py
index 8ba776ab..11f964fb 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -184,13 +184,13 @@ class PagesGenerator(Generator):
self.pages.append(page)
self._update_context(('pages', ))
+ self.context['PAGES'] = self.pages
def generate_output(self, writer):
templates = self.get_templates()
for page in self.pages:
writer.write_file('pages/%s' % page.url, templates['page'],
self.context, page=page)
- self._update_context(('pages',))
class StaticGenerator(Generator):
From 3e9b80e993091163f9088940cb812b4af42d7fee Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Tue, 14 Dec 2010 15:55:26 +0000
Subject: [PATCH 0029/2735] Fix #21: Add a setting for the archive order
(REVERSE_ARCHIVE_ORDER)
---
docs/settings.rst | 2 ++
pelican/generators.py | 2 +-
pelican/settings.py | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/settings.rst b/docs/settings.rst
index 85679eb5..365eb24f 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -50,6 +50,8 @@ Setting name what it does ?
(no rss)
`CSS_FILE` To specify the CSS file you want to load, if it's not
the default one ('main.css')
+`REVERSE_ARCHIVE_ORDER` Reverse the archives order. (True makes it in
+ descending order: the newer first)
======================= =======================================================
Themes
diff --git a/pelican/generators.py b/pelican/generators.py
index 11f964fb..d6077408 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -158,7 +158,7 @@ class ArticlesGenerator(Generator):
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
self.dates = list(self.articles)
- self.dates.sort(key=attrgetter('date'))
+ self.dates.sort(key=attrgetter('date'), reverse=self.context['REVERSE_ARCHIVE_ORDER'])
# and generate the output :)
self._update_context(('articles', 'dates', 'tags', 'categories'))
diff --git a/pelican/settings.py b/pelican/settings.py
index 88d2bde6..bf5afacd 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -16,6 +16,7 @@ _DEFAULT_CONFIG = {'PATH': None,
'DEFAULT_CATEGORY': 'misc',
'FALLBACK_ON_FS_DATE': True,
'CSS_FILE': 'main.css',
+ 'REVERSE_ARCHIVE_ORDER': False,
}
def read_settings(filename):
From 1d2af7aa0117e1ab07b5056fa634f47eeb703dfa Mon Sep 17 00:00:00 2001
From: Florian Preinstorfer
Date: Tue, 14 Dec 2010 23:45:45 +0100
Subject: [PATCH 0030/2735] added a keep commandline switch (-k, --keep)
-keep output directory if keep commandline switch is provided
-added KEEP_OUTPUT_DIRECTORY to settings (default is to delete output
directory before processing)
-updated documentation
---
docs/settings.rst | 2 ++
pelican/__init__.py | 19 +++++++++++--------
pelican/settings.py | 1 +
3 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/docs/settings.rst b/docs/settings.rst
index 365eb24f..e2876f96 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -52,6 +52,8 @@ Setting name what it does ?
the default one ('main.css')
`REVERSE_ARCHIVE_ORDER` Reverse the archives order. (True makes it in
descending order: the newer first)
+`KEEP_OUTPUT_DIRECTORY` Keep the output directory and just update all the generated files.
+ Default is to delete the output directory.
======================= =======================================================
Themes
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 7f37f46c..a26ba8ff 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -9,7 +9,7 @@ from pelican.generators import (ArticlesGenerator, PagesGenerator,
def init_params(settings=None, path=None, theme=None, output_path=None,
- markup=None):
+ markup=None, keep=False):
"""Read the settings, and performs some checks on the environment
before doing anything else.
"""
@@ -25,6 +25,7 @@ def init_params(settings=None, path=None, theme=None, output_path=None,
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']
# find the theme in pelican.theme if the given one does not exists
if not os.path.exists(theme):
@@ -42,14 +43,14 @@ def init_params(settings=None, path=None, theme=None, output_path=None,
if not path:
raise Exception('you need to specify a path to search the docs on !')
- return settings, path, theme, output_path, markup
+ return settings, path, theme, output_path, markup, keep
-def run_generators(generators, settings, path, theme, output_path, markup):
+def run_generators(generators, settings, path, theme, output_path, markup, keep):
"""Run the generators and return"""
context = settings.copy()
- generators = [p(context, settings, path, theme, output_path, markup)
+ generators = [p(context, settings, path, theme, output_path, markup, keep)
for p in generators]
for p in generators:
@@ -57,7 +58,7 @@ def run_generators(generators, settings, path, theme, output_path, markup):
p.generate_context()
# erase the directory if it is not the source
- if output_path not in os.path.realpath(path):
+ if output_path not in os.path.realpath(path) and not keep:
clean_output_dir(output_path)
writer = Writer(output_path)
@@ -67,10 +68,10 @@ def run_generators(generators, settings, path, theme, output_path, markup):
p.generate_output(writer)
-def run_pelican(settings, path, theme, output_path, markup):
+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)
+ params = init_params(settings, path, theme, output_path, markup, delete)
generators = [ArticlesGenerator, PagesGenerator, StaticGenerator]
if params[0]['PDF_GENERATOR']: # param[0] is settings
processors.append(PdfGenerator)
@@ -94,10 +95,12 @@ def main():
' available.')
parser.add_argument('-s', '--settings', dest='settings',
help='the settings of the application. Default to None.')
+ parser.add_argument('-k', '--keep', action='store_true',
+ help='Keep the output directory and just update all the generated files. Default is to delete the output directory.')
args = parser.parse_args()
markup = [a.split()[0] for a in args.markup.split(',')]
- run_pelican(args.settings, args.path, args.theme, args.output, markup)
+ run_pelican(args.settings, args.path, args.theme, args.output, markup, args.keep)
if __name__ == '__main__':
diff --git a/pelican/settings.py b/pelican/settings.py
index bf5afacd..e3c511d3 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -17,6 +17,7 @@ _DEFAULT_CONFIG = {'PATH': None,
'FALLBACK_ON_FS_DATE': True,
'CSS_FILE': 'main.css',
'REVERSE_ARCHIVE_ORDER': False,
+ 'KEEP_OUTPUT_DIRECTORY': False,
}
def read_settings(filename):
From 3c8cafdc0d3a3dc48ec7dc7edb7336920343c661 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 12:45:21 +0000
Subject: [PATCH 0031/2735] Make the long option a bit more verbose for
--keep-output-directory
---
pelican/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index a26ba8ff..a4d76398 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -95,7 +95,7 @@ def main():
' available.')
parser.add_argument('-s', '--settings', dest='settings',
help='the settings of the application. Default to None.')
- parser.add_argument('-k', '--keep', action='store_true',
+ parser.add_argument('-k', '--keep-output-directory', action='store_true',
help='Keep the output directory and just update all the generated files. Default is to delete the output directory.')
args = parser.parse_args()
markup = [a.split()[0] for a in args.markup.split(',')]
From 62bd20a844b794c3dcaaf573d3eb1c983e561766 Mon Sep 17 00:00:00 2001
From: Florian Preinstorfer
Date: Wed, 15 Dec 2010 15:56:38 +0100
Subject: [PATCH 0032/2735] fixed a ValueError with -k option
---
pelican/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index a4d76398..069b0c87 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -95,7 +95,7 @@ def main():
' available.')
parser.add_argument('-s', '--settings', dest='settings',
help='the settings of the application. Default to None.')
- parser.add_argument('-k', '--keep-output-directory', action='store_true',
+ parser.add_argument('-k', '--keep-output-directory', dest='keep', action='store_true',
help='Keep the output directory and just update all the generated files. Default is to delete the output directory.')
args = parser.parse_args()
markup = [a.split()[0] for a in args.markup.split(',')]
From c347e3be42029239cf33e51bb71b34b76f60368b Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 18:01:29 +0000
Subject: [PATCH 0033/2735] Switch back the theme. Order the settings.
---
docs/conf.py | 6 ++---
docs/index.rst | 21 ++++++++-------
docs/settings.rst | 65 +++++++++++++++++++++++++----------------------
3 files changed, 48 insertions(+), 44 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
index 159ddbcc..0efe92b2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -21,14 +21,14 @@ pygments_style = 'sphinx'
# a list of builtin themes.
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
-html_theme = 'flask'
+html_theme = 'flask_small'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
-# 'index_logo': 'pelican.png',
-# 'github_fork': 'ametaireau/pelican',
+ 'index_logo': 'pelican.png',
+ 'github_fork': 'ametaireau/pelican',
}
# Add any paths that contain custom themes here, relative to this directory.
diff --git a/docs/index.rst b/docs/index.rst
index 9af7ff6b..5fd8f8b7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -9,17 +9,6 @@ Pelican is a simple weblog generator, writen in python.
* Easy to interface with DVCSes and web hooks
* Completely static output, so easy to host anywhere !
-Documentation
-=============
-
-.. toctree::
- :maxdepth: 2
-
- getting_started
- settings
- themes
- internals
-
Features
========
@@ -51,3 +40,13 @@ the repository, etc. That's open source, dude!
Contact me at "alexis at notmyidea dot org" for any request/feedback !
+Documentation
+=============
+
+.. toctree::
+ :maxdepth: 2
+
+ getting_started
+ settings
+ themes
+ internals
diff --git a/docs/settings.rst b/docs/settings.rst
index e2876f96..d1b84e0a 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -9,40 +9,17 @@ the command line::
$ pelican -s path/to/your/settingsfile.py path
+Settings are given as the form of a python module (a file). You can have an
+example by looking at `/samples/pelican.conf.py
+`_
+
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 ?
======================= =======================================================
-`SITEURL` base URL of your website.
-`PATH` path to look at for input files.
-`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)
-`OUTPUT_PATH` Where to output the generated files. Default to
- "output"
-`SITENAME` Your site name,
-`DISPLAY_PAGES_ON_MENU` Display or not the pages on the menu of the template.
- Templates can follow or not this settings.
-`PDF_PROCESSOR` Put True if you want to have PDF versions of your
- documents. You will need to install `rst2pdf`.
-`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
-`FALLBACK_ON_FS_DATE` If True, pelican will use the file system dates infos
- (mtime) if it can't get informations from the
- metadata?
-`MARKUP` A list of available markup languages you want to use.
- moment, only available values are `rst` and `md`.
-`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.
-`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)
+`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.
@@ -50,10 +27,38 @@ Setting name what it does ?
(no rss)
`CSS_FILE` To specify the CSS file you want to load, if it's not
the default one ('main.css')
-`REVERSE_ARCHIVE_ORDER` Reverse the archives order. (True makes it in
- descending order: the newer first)
+`DEFAULT_CATEGORY` The default category to fallback on. `misc` by default.
+`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)
`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.
+ 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.
+`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)
======================= =======================================================
Themes
From 2f38fab3e02eb5c2c088db98449c16ffaa4b74ae Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 18:08:25 +0000
Subject: [PATCH 0034/2735] Add some explanations about the theme templates.
---
docs/themes.rst | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/themes.rst b/docs/themes.rst
index 7b7c0de5..340c3d44 100644
--- a/docs/themes.rst
+++ b/docs/themes.rst
@@ -43,6 +43,8 @@ are in caps. You can access them directly.
Common variables
----------------
+All of those settings will be given to all templates.
+
============= ===================================================
Variable Description
============= ===================================================
@@ -62,6 +64,9 @@ pages The list of pages
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
============= ===================================================
@@ -72,6 +77,9 @@ category The name of the category being processed
article.html
-------------
+This template will be processed for each article. .html files will be outputed
+in output/`article_name`.html. Here are the specific variables it gets.
+
============= ===================================================
Variable Description
============= ===================================================
@@ -82,6 +90,9 @@ category The name of the category of the current article
tag.html
--------
+For each tag, this template will be processed. It will create .html files in
+/output/tag/`tag_name`.html
+
============= ===================================================
Variable Description
============= ===================================================
From 6b25943a3ab611ea1fd1e815d57cd26fdca01a33 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 18:09:43 +0000
Subject: [PATCH 0035/2735] Update THANKS
---
THANKS | 1 +
1 file changed, 1 insertion(+)
diff --git a/THANKS b/THANKS
index 168c0398..0604e5db 100644
--- a/THANKS
+++ b/THANKS
@@ -9,3 +9,4 @@ bugs or giving ideas. Thanks to them !
- Nicolas Martin
- David Kulak
- Arnaud Bos
+- nblock (Florian)
From 3a16dcec8c58fb58504f7d76b521b50334fd861b Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 18:13:59 +0000
Subject: [PATCH 0036/2735] Add explanations about each template.
---
docs/themes.rst | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/docs/themes.rst b/docs/themes.rst
index 340c3d44..633acaaf 100644
--- a/docs/themes.rst
+++ b/docs/themes.rst
@@ -13,14 +13,14 @@ To make your own theme, you must follow the following structure::
│ ├── css
│ └── images
└── templates
- ├── archives.html
- ├── article.html
- ├── categories.html
- ├── category.html
- ├── index.html
- ├── page.html
- ├── tag.html
- └── tags.html
+ ├── archives.html // to display archives
+ ├── article.html // processed for each article
+ ├── categories.html // must list all the categories
+ ├── category.html // processed for each category
+ ├── index.html // the index. List all the articles
+ ├── page.html // processed for each page
+ ├── tag.html // processed for each tag
+ └── tags.html // must list all the tags. Can be a tag cloud.
* `static` contains all the static content. It will be copied on the output
`theme/static` folder then. I've put the css and image folders, but they are
From 2bccc298fbfbe20c69c1b395cabf0766d30d61a9 Mon Sep 17 00:00:00 2001
From: Alexis Metaireau
Date: Wed, 15 Dec 2010 18:14:54 +0000
Subject: [PATCH 0037/2735] Settings have to be in caps.
---
docs/settings.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/docs/settings.rst b/docs/settings.rst
index d1b84e0a..27e399b9 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -13,6 +13,9 @@ Settings are given as the form of a python module (a file). You can have an
example by looking at `/samples/pelican.conf.py
`_
+All the settings identifiers must be set in caps, otherwise they will not be
+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.
From 07393cf3b8bda41710137a8a83de3993961c73dc Mon Sep 17 00:00:00 2001
From: Florian Preinstorfer
Date: Wed, 15 Dec 2010 21:50:55 +0100
Subject: [PATCH 0038/2735] sort notmyidea template settings
---
docs/settings.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/settings.rst b/docs/settings.rst
index 27e399b9..33a57155 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -85,15 +85,15 @@ to use them too in your themes.
======================= =======================================================
Setting name what it does ?
======================= =======================================================
-`GITHUB_URL` Your github URL (if you have one), it will then
- use it to create a github ribbon.
`DISQUS_SITENAME` Pelican can handle disqus comments, specify the
sitename you've filled in on disqus
+`GITHUB_URL` Your github URL (if you have one), it will then
+ use it to create a github ribbon.
+`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
`LINKS` A list of tuples (Title, Url) for links to appear on
the header.
`SOCIAL` A list of tuples (Title, Url) to appear in the "social"
section.
-`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics.
======================= =======================================================
In addition, you can use the "wide" version of the `notmyidea` theme, by
From 05f90fec8e36dc00e32d38088e8b23107399fb70 Mon Sep 17 00:00:00 2001
From: solsTiCe d'Hiver
Date: Thu, 16 Dec 2010 11:06:30 +0100
Subject: [PATCH 0039/2735] Add missing word in settings.rst docs
---
docs/settings.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/settings.rst b/docs/settings.rst
index 33a57155..b8eee542 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -43,7 +43,7 @@ Setting name what it does ?
`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.
- moment, only available values are `rst` and `md`.
+ 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.
From 99de8224bb335a13845d52abef41588e6efcc0c4 Mon Sep 17 00:00:00 2001
From: solsTiCe d'Hiver
Date: Thu, 16 Dec 2010 20:44:42 +0100
Subject: [PATCH 0040/2735] Change default for markup
* markup default are already defined in SETTINGS['MARKUP'] so there is no need to define it here
* also change a weird construct
---
pelican/__init__.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 069b0c87..3ff7b460 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -90,15 +90,14 @@ def main():
parser.add_argument('-o', '--output', dest='output',
help='Where to output the generated files. If not specified, a directory'
' will be created, named "output" in the current path.')
- parser.add_argument('-m', '--markup', default='rst, md', dest='markup',
- help='the markup language to use. Currently only ReSTreucturedtext is'
- ' available.')
+ parser.add_argument('-m', '--markup', default='', dest='markup',
+ help='the markup language to use (rst or md).')
parser.add_argument('-s', '--settings', dest='settings',
help='the settings of the application. Default to None.')
parser.add_argument('-k', '--keep-output-directory', dest='keep', action='store_true',
help='Keep the output directory and just update all the generated files. Default is to delete the output directory.')
args = parser.parse_args()
- markup = [a.split()[0] for a in args.markup.split(',')]
+ markup = [a.strip().lower() for a in args.markup.split(',')]
run_pelican(args.settings, args.path, args.theme, args.output, markup, args.keep)
From a322d6c4e8281b699292c4421c2143c59d091cb1 Mon Sep 17 00:00:00 2001
From: Alexander Artemenko
Date: Fri, 17 Dec 2010 00:04:45 +0300
Subject: [PATCH 0041/2735] Lowercase meta field's name before looking the
processor.
---
pelican/readers.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index e9520fe6..0616eecd 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -50,11 +50,12 @@ class MarkdownReader(object):
metadatas = {}
for name, value in md.Meta.items():
+ name = name.lower()
if name in _METADATAS_FIELDS:
meta = _METADATAS_FIELDS[name](value[0])
else:
meta = value[0]
- metadatas[name.lower()] = meta
+ metadatas[name] = meta
return content, metadatas
_EXTENSIONS = {'rst': RstReader, 'md': MarkdownReader} # supported formats
From 3decf7f5195dcab046b7c303ca60bbfac463a381 Mon Sep 17 00:00:00 2001
From: Alexander Artemenko
Date: Fri, 17 Dec 2010 00:05:39 +0300
Subject: [PATCH 0042/2735] Added .gitignore file.
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..c53cdba0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.egg-info
+.*.swp
+.*.swo
From 658e1203b21c87086210b370aecd49c3268ef612 Mon Sep 17 00:00:00 2001
From: Alexander Artemenko
Date: Fri, 17 Dec 2010 00:07:55 +0300
Subject: [PATCH 0043/2735] Simplier metadata processing, using dict's 'get'
method with default value.
---
pelican/readers.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 0616eecd..7ff393c5 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -51,11 +51,9 @@ class MarkdownReader(object):
metadatas = {}
for name, value in md.Meta.items():
name = name.lower()
- if name in _METADATAS_FIELDS:
- meta = _METADATAS_FIELDS[name](value[0])
- else:
- meta = value[0]
- metadatas[name] = meta
+ metadatas[name] = _METADATAS_FIELDS.get(
+ name, lambda x:x
+ )(value[0])
return content, metadatas
_EXTENSIONS = {'rst': RstReader, 'md': MarkdownReader} # supported formats
From bc5a19a37baccddd5719a3b6586ce001b1e3187c Mon Sep 17 00:00:00 2001
From: Alexander Artemenko
Date: Fri, 17 Dec 2010 00:32:12 +0300
Subject: [PATCH 0044/2735] Slugify is broken for non-ascii titles. Now slug
could be redefined, using the metadata.
---
pelican/contents.py | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 0caceed0..cfc5e442 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -20,6 +20,12 @@ class Page(object):
if 'AUTHOR' in settings:
self.author = settings['AUTHOR']
+ if not hasattr(self, 'slug'):
+ self.slug = slugify(self.title)
+
+ if not hasattr(self, 'url'):
+ self.url = '%s.html' % self.slug
+
if filename:
self.filename = filename
@@ -29,14 +35,6 @@ class Page(object):
if not hasattr(self, prop):
raise NameError(prop)
- @property
- def url(self):
- return '%s.html' % self.slug
-
- @property
- def slug(self):
- return slugify(self.title)
-
@property
def summary(self):
return truncate_html_words(self.content, 50)
From 1a31c74d5c7b34c4cdb483e527d116f63c25b5f2 Mon Sep 17 00:00:00 2001
From: Alexander Artemenko
Date: Fri, 17 Dec 2010 00:32:27 +0300
Subject: [PATCH 0045/2735] .gitignore updated.
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index c53cdba0..29ac491f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.egg-info
.*.swp
.*.swo
+*.pyc
From d61b108a0e26955a145f73a28a505b753f5ec938 Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Fri, 17 Dec 2010 09:25:19 +0100
Subject: [PATCH 0046/2735] Creation of tag feeds (atom & rss)
---
pelican/generators.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/pelican/generators.py b/pelican/generators.py
index d6077408..a841a850 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -100,6 +100,14 @@ class ArticlesGenerator(Generator):
self.settings['CATEGORY_FEED_RSS'] % cat,
feed_type='rss')
+ if 'TAG_FEEDS' in self.settings:
+ for tag, arts in self.tags.items():
+ arts.sort(key=attrgetter('date'), reverse=True)
+ writer.write_feed(arts, self.context, self.settings['TAG_FEED'] % tag)
+
+ if 'TAG_FEED_RSS' in self.settings:
+ writer.write_feed(arts, self.context, self.settings['TAG_FEED_RSS'] % tag, feed_type='rss')
+
def generate_pages(self, writer):
"""Generate the pages on the disk
@@ -111,7 +119,7 @@ class ArticlesGenerator(Generator):
write('%s.html' % template, templates[template], self.context,
blog=True)
for tag, articles in self.tags.items():
- write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag,
+ write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag,
articles=articles)
for cat in self.categories:
write('category/%s.html' % cat, templates['category'], self.context,
From 8352b6136747f670c2af47e45da76140a09dba1d Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Fri, 17 Dec 2010 09:29:01 +0100
Subject: [PATCH 0047/2735] Creation of tag template page
---
pelican/themes/notmyidea/templates/tag.html | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pelican/themes/notmyidea/templates/tag.html b/pelican/themes/notmyidea/templates/tag.html
index e69de29b..68cdcba6 100644
--- a/pelican/themes/notmyidea/templates/tag.html
+++ b/pelican/themes/notmyidea/templates/tag.html
@@ -0,0 +1,2 @@
+{% extends "index.html" %}
+{% block title %}{{ SITENAME }} - {{ tag }}{% endblock %}
From 7a9bbc44910001535c83e672b0067373cc446da8 Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Fri, 17 Dec 2010 09:31:20 +0100
Subject: [PATCH 0048/2735] Add the category name in the title page
---
pelican/themes/notmyidea/templates/category.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/pelican/themes/notmyidea/templates/category.html b/pelican/themes/notmyidea/templates/category.html
index 09763b9a..56f8e93e 100644
--- a/pelican/themes/notmyidea/templates/category.html
+++ b/pelican/themes/notmyidea/templates/category.html
@@ -1 +1,2 @@
{% extends "index.html" %}
+{% block title %}{{ SITENAME }} - {{ category }}{% endblock %}
From 01af09fd23b0ae20093e22547be70075e0bddfe9 Mon Sep 17 00:00:00 2001
From: Freeculture
Date: Fri, 17 Dec 2010 09:34:20 +0100
Subject: [PATCH 0049/2735] Add RSS feed if exists in the template
---
pelican/themes/notmyidea/templates/base.html | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html
index 99d6d380..03ff1bb1 100644
--- a/pelican/themes/notmyidea/templates/base.html
+++ b/pelican/themes/notmyidea/templates/base.html
@@ -4,7 +4,10 @@
{% block title %}{{ SITENAME }}{%endblock%}
-
+
+ {% if FEED_RSS %}
+
+ {% endif %}
T#=lrfRoSzUw4!JfnPg_B
z9+D)!ID>@EKZV)-{h%`Ds*_bg1cD0K${)npa?K1>Et(&XJ#wnOy?$7fKdt!nP>r00
z3>ctR+Ig+wsPKRdPUV7bU|oE+G6&55qJCgkDm=>2`+}P|&KB!Kl$nl708Uv$yN7k^
zYRlb^h-4Y5`zarXApLLIKA!h3&|}ny9bMwhG`IqUD^VV%fv|Ba{gE{g0(AdN*f}AK
z*36J=ecsYTeW=TKhP&{P?tCQ$twg3d{!(2Z`KS}I(!)*Ud=HBKG!3Dooj2WyHdRsN
zsfx%h(mX>#ybf=qO)jL4+*rRPO`R7gG3qb{vDEH3u;u{u>f}>j-$aW8`<}1r16G-N
z!V?3#ukq6NlvmGEYGY_o>WJpVL5>r$cZOj>)hrs>Nszj#`Cy0M{EX2tqeY|4%M>S)
zeuT@o`Xs{V(|eP>0uzySEf3hsU_T@xqg!Nz*TPvT>vJ>A;LP4ETXKPmkp(ZM@m^Cc
zCp8z}#*&*_nj8(@SC{f$XsGB55#g7yQz)MV-k$
zep&JR^FM-)4oN-RH1{Qny;JkzY$if5Jph7N+d&3hDtmMxQ_!ZYBRCs(=R*sP0NN#$
z(;vTZHb|y*mqBrjvCf_0VN6Zih)tB*uV+k?uRd+Fk*(gJ<={OzQ3j!oNWpUI9x0?u
z=fej2_1TYWx%!GDa)$XZ=ol6>UIkrz$j7-Roo`zh(?#1cfx-E&e
zu;-(CB|F$nwyBVOrK_$O4URxHNYRkP7>!R-8)841GOJeg4
za%_L$C&1R6n7qp_n6
zaX|@s_+|~+PoM5Vjf;vW^09*ID)o8=sjTp+ZBH#PZi=T%fE6W8thAqr{h*!rFjeZj
z-{qU~0TrPx`3N&~)NkN$!xf5nJ0<3TS=*rv(eBhE;@6jE-rbC{(ca{AiS
z=tDud0z`^!;dcQ+=WUbc71`*7Lr;e1jfpKw`!RN6sDC53ViF$~M6t@J@J2qA#qA?#
zl-Q1ZJJp>2*YY{~2HATAZ!~W=pUAX@BpauXTXfjFA8tn&!2m27BmUv)4EDuQNh)p$
z72_VW_mXYFDwV{54RP$*Kcj2gFPEM2=SfF$~(^Ioa4WM9Hv7gBzx97)2AWV
z(*dU>TlM?~jkFabnMYLZz}4(N`+j6u$89P?UGAeFZ`5B(?NKchwZK?Vxjm7k8^aU(
z2c*q}uurj8yFQ@f+|SXeKgW0~8sb3ZuQHd!PaI>;dk@yR^#HHgo|bZrYMP!KlWbPm
zy!KmzqFX9xlcdK9+(5LKUcs2=GKJ1qzIg<-YqqHXo%xyu&3H+!lCSTL5bM1YXI}jc
zbO2JMGmnu`jcYk!!R99$jcy0IQ^$egURm}Q=$K{wbVb~={d4QE@e@@e9QHBw1V0y}
z%yQmX6LJmbTTcp0J~Uw8ip%#VJyb5+(=`8PwMo7aN0eTk-QVT}939ySr-bwIlZdjoz8I;55`S$XNb`DxvPtW;-e9
zUQDub3gSy-*lOqE)r?O73sY&lR1TGu_hedd53O{yR9rMksW>i2x~FLFr}4!hGzx>$
z?8%MysnRQ(AO8(--PEz|IHa6=LIX>N@gmAw40@da2XY7UN&l*KUH1`w0Y4Rt0mkpv%zp#wD85Wn{ynSHH#M!3PR#)2C$@b7
z!RCXW54kajr^5XrAkTvDsGd+1=O8<8VH)qls$7Bj`I(y{wSNO8
ztl9YOjE|6JMs8ny7}m=e1lnzA&XrHj9k^fTzA@+o@zPR)yrJ^n1|2~7)Q`1sPB~#x
zoXWyL!BJP+jz*DB1ao4)YQamqR*g!JmwhZ+(aaQ+ep9FupaAUcQ&UhBn5=;Jn|BqI
zg(T7zPnubpz&J*+4|g1D*WeciQ#v~$f$?HTS3H{J=0{uv$07T4-qrb+%YD6ZF(9d^
z&b^3fNy8bNW8Un!ZdiQX%9@(M+iof-5*KE;$58oQUc&+>X_DeFn^AjpPwMMQZdBJXS9_L^q`d?y1s5qG(
zoK-xZW2h^!O!HdJ@h!fhB_awo*t(H|H$of#v(DS^p3_B(!MBt=?|rwRJT)wE}-RH60am4+>*5E+tn?-$h=|^K}_zI?@B$5RA
zUkrsW^MjX-_tY-cZhwRY(I;{^&=)+%Unf^U|4iXz0>dOA-uLP|rE1~=NoLdWK`nT#
z`(5nKCd+g{N~5Sq4%{>5>ea3wgY9wiyEw{(RvrIKsr7*pXdAW)UbIo0GDF>iyGlFO
zk8cda`UQ$5(&=}Bp%YKdT+MS-l%CF(DP2n&jZa&UZ-KX!NZYrx4J47T(?7`=PcY`B
zNvTd-54CG{4l%yh`dCBHohueMa~w{50dPG`91YEMg~P`;Bcn(GljVg#gRWE{95Nh(
zs5um=0(;2O`NY>WASyXX0t-z~*=mK7TUJuOe|CQA?ls8d|2^|^4QGW(wRxU+L_M{W
z!H#Z3I3}_6N!x1?`=0m1i`6MD0q#5?e8JJy=)nn|kG$!P-@tR74R7hZYi!vnyLf0e
zBKPP`-hKVZN;+Mdg)FvjbuMii!<5(ldV_k4sPV{f(u;uv>H|h%QqbK(X{*%=a%6OQ
z;kNuxRBC`>Ad1E|(-99Yr-$@GNOBehwUyr&m^N`i=c1>Y^yy{N=gK~Mqz4B_58?^x
z9pl{RP*SC8QnE_z-&MrguEy8?kZ9{Xd7*olF)VCqB#Ol;kLglVk^W0S`=jb4H-EPj
zt;pTnKh1Q!8WMnSnuf*?*FW%^73aoJ#23h5zlp)N=s|4OMRVRcbt-g@q%&W9xQWC)
zQlmGv$TUbb%ru^^->0;!=zk$!RfOs5^m%%IU}hT9`cvEyQ+m_2#()G($whf>bLO9T
z4XG`eOpw`U^W#DBTbB-^nyyCVo+aPaj|5dtXP~WkTrcy!)BFMJeM*JjZ});$YrdC?
z>7&+i1|>f_K1taNyvVzN^!JUuUs5AkQ>~4%x#)Tu8WRoo-+<({e7#ZCvN6ToJ9#msHh*SE
z2Q-5MtShMGquwC|A*akv*;+LqX8XC-gRxRAsM|J8PW+nJZs=;U)L3j^ePIf*qdMYaU9ZTHPPVND!W5J_ZZH)
z3V}u+f8+2HEDvEhLPGW`&HKn71KeIFnf7qRi%grNJ8ueD>Q7JHm*t>k;iJeDkpa-=
zpa{C8H#d$cb-#)5!dbtMhV2^rf%%!Q?gP2FGi{>0qcSa@8d2!Lt+y>Ao_Q?SRQ_ap
zly#n&ixlc;yeuSB8S|Qe#OakE8nDVbQdwesO-g6O2yF;)`o6RZS_e74uyoUayAp5D
zb7JP{LT?LVQZ7|F=wbA>r+aU;Dq2`6_@=u}mStn&3*P`uo=c-g&(7_xYI1+Vvw%MR
znzT;o;qkwYQRmxg7X~JD`Vh8RBD|xE11O2;=pE6WEg|e$0|@6F68>09=e30n;a@TH
z<4O@e&fNRKX8`bs30=~63WZeKiTw%&pR6U&XH?TND*=;K=`!+CZ0MzC@Xu>C^p6kA
zubb8;*+zq+dVRMCkL*@Jn+LxRw#G~)RLpDh)F5{?-)7|pl@l~3YmFa8B}4*UT50+q
zNuUy~VWglj38bNlD@-O6jwKmAn4L!>72{vlv$s5A$qk(TyLYP_QO&&Lt_ZeGxy0V?
zF!;ylw5U}2((=u7KY0uK`)F(!v*L%-KD+vIp*o?YX8Vx(SLRsmKI2@Zy_*)!Xxcl%
zhWupzgOX<`O_lq2c*yYi0
zT@L(EMQ*>Po7}9-Uct?RswS$Edzq++!dFTjQ-@|YuATX6m6meSB=xPhgztiE8i%yV
zIdsL*J|2PsW;jY@KUqZ|bLB4NU5z*E*UJfiGgdV1H2+kQmeb-HbrP-c(391!`g4KW
z<5wMLFWyKFr#kT(ans|$-oc3^y{(+s
zFKAsJb&WJ)ww`45o<}{H6#i;NO}KWC
z9Cf*sk7ay_G&6}+H#VUreJ@hlQy#I8b^d)KIYwLS-GG{|F|fWF504N|+xFauF_|jA
zHgwmme{T=f0GY4yx0leaTuy(Oo~w5`j-cWjo^koQy39nAW7SN|CJ6tao=_o8!hF9}
zo-HtlKYBMQ%y?YCqtYWB6m-;-=k-psAd;HA##l!-lP^tJTQ3N9DqZ^xq_H)uJEDoc
z{zGo?HIE9C`uU{0KKwI-a>jEQ852MMg7K9qf2p0gIlkw_t7%B~QQI|5e23;*$)Nt?
z5F6eob*26dsFTBORvk9(JYb8~%Lh=C&Pi`FAB;nsjqLg*nUyd8UY2($zCO!E5YOD_yxdd2f`=n!E}4q*DNDDzgN#t~QDjEYB*5CPRG86;EitX0v~wdzoN@o?8o-^H@TbKrGtW1mDC
z^y%J?qS#yMm43r|N{&dB{1iyV&R$`Y(=H_Mb9k;%IlsSSq?ExVF6C65uM}NZqxFme
zUEUKSpzM2Fyx1)as=a?$ioas_#E2dsfa$)IO^jA_jCcW#wCc~aC=|5nlzvY|Vu
z?V7umR?|9{T3PN#TJ&q@kA*tE2Anc*#4qOP6KR4F1Li#isV+L8kL>r^-?8)jV-auK
zyk|;tI^8CEPVuGV<||n)uQ~9Ev%Xoq82f^oY1Si1D;#&q{WlB@4)e4fG_AY;7=u?{
zCb({O{ABODYa5{DT%G@T(lrU+1
zQr3WVRehnhp%&h3QG*}x54RBM+K-esXq@M2?IDW&>nvg>44UZ-w|XTfMS+s@AT2;8
zI0a_k-?@)VwQEG(Hq)=VR*|Pm`{)Q)v8SF^h1wRk(Qou#SF*AmFDLEYi&8<$%n;>BnEr&~
zbDDRHhJ0fo$`
zDY`2HXto6FpQNVtb+;ZvvGG*42~Pk034u*zG1MK$r_WF707loYyl&T=I_d8gINSNN
zr_3a6$(MaUXvHfCM{?`I
zo-4^$tM6mhe|VP0JLq}uKx!|fpc+dq=5-#xqjl>2+q)-cUuDE6Twu?yw*Ll@C@I9S
zSo5>r2R59b_g4*HgX5_nJ8#Tcujp6m=#<;ZQ$gvN0Zh@n5|rtYyv6SW>l(cIHxR=3
z9x?W2lGoJa#xOlS5;`xY%0}dQ}tF*9s(M>;$lJ3hy70M1zn7}4}K<$dv
z2c*=jcFm3(7b|zGbTmO#ye=O>bi7M})X>SDUywS4%LKMV
zI7p{V4kln3(LQWo2oid5dxrES`U-e%?e^c!=MHQia_5U|M|uLOn8D~0f~HzJLaLo{
z64}_hJy43$W8(HRs0_8|qAJBhdxA1}@7MBS=vpA=`-cMYF)gb3R3&}3xH5v9m}_et
zP(7IZDovdfzUqI(!+Wu6e{4NaKrljUouDZo>mRXDLA5kNh2ZDc_Z#5x(&(6&5HpJ#LcPZE53yv_
zqAJvBBdGRaoG~<OC~Ky_+PLkWAIy#dcX9-o@(CYx
z+~k2_=><0F0x9B;*K*t?YlYp{qMaWX&0)x~TFOW{?dv`UH5ZQZ|CCi!89|Ma`LX*y
z@m~3G&s{Kc_8TqReiOj+w35Ecd#{3jub}-qHk}A}s%;;KmURZ`y-DKzeQBS;@ZCep
zqT%Iq%^V;zWl2FO*=jb125v-}Z#m|_TeSnC)zALABlt1ShAk_f5JjBW$2D#aT-`E;
z^Urpqkt4u?4b7s)O{h@X+0ljU1v#vc*u_iVEim~QGd$2$T~j%>!gsgC8O0X}sp`^g
z#)BH{A@b>aW{aTG^|>~pVqRaHf@X(8q`Z6r<4MGy9~9SnKmOT~v;Qq`^sooT?i_vX
z&}xRV2!BkMCY0}3K#ytKFMLiz=i3LS_V@HZ$QV&r@Wr!LqRY%vnoIovr-x(LVC
z;tVioP?ENEqvvf;d7^@w_J_auMM&Q(6sA?wsXwf@STrBnSe6D937r0&t6;I~9v{C|
zM+n5*G03-L9TypU5T~NC?;YCd<7)Elf3#$##420A9>L+fD~erjG;y_^tjw!wnv6z8
zjq3`Uo6i{AoZIm%4@JiE5R}Sy=wTB{9Fs$wL}l`yM7z+eajbz@sR4X_lXTQ)_IqbD
z<%_F89_UEbAJm%EHGIdb@RKns_awV1wX>Wly|7@;znA|)%Gf{Ts?#->C&P0%g^x9t
z@H&}gZ@}^$J0e28MWWq&fK5x14hT#4Oo2a`-S8SHK?zq%$_e_uCF$~ind0cfX2j%3B#nKPTvet9-_av5
z8d$#zVJsM&?d-U4VNvf3?(kZw#g`Er(CA2=I-O&6(FliNj!_jL^A6t#kdF)EuN8iD
z?18Lp=k^NOdVITv$1wN<;mvYLO^~lL=dl77%8Yxf`OYn{+l3w`U9Ga1)x4;o(*aEb
zN~C=cl=Gzp;(Oy97Gv@5C3ydPT7z~IKW>w0OtH^t43CS%YDS)aY`oB9zOEAxC1;%8
z`LO9TNLC#;@b4N$vr3;43}+jDC_(aSV=ja}7Mq9Bm(vhkgEFN`h`kGckp9m1ntyY=
zQD2T}mY@wv@xRz&?3va(64ge$Wz=WuTg^Ynm4V3fdG4Zl9v^E{>TeF2sAYn24W_lAFvoOS1VpjNx9b40=LPF@Gq803N5g&@=>s7
z?(k|(-tswQxN&W0d4l#;!|$~tDyz!zH(&&E4-Kg8VKW@R+PW3-6Z?vRS4sbUH{9SH
zE8kyMyK!}W6^JONR_QlqNpd*8@VW0t;B~b=I)e;)9^P===RAo3otcymJ2H86rxEwX
zhgitiSQ$`;F+oc07V|1jM0-cDM&g_zF=Gl?t+)59P*snAR$bJu5A0ZC)$7v0;%c^l
z$YXEDGUlzBzYd)iH)ejSXUDp;teou}>4a_CxktjSwHz)^xLs_WFI
z#{jbh=?RA-Vlf)J*iqc861Ik4b^nI4(-Ds%)G=kO_jLu8Is$RN9YJd{Lv?C;Qo$yw
zNJ++{XxnvV^zDmG)9C`E4r}91O+ZggfmEcVVs5gBpu%zn^h(q`Eqem290YiMC+w(Z
z9PimwWg(-CDC$V^&XLzoOzBv(;Jr#=T_!}Eq07#{yP&+rp;^4<@PYGbw7)bY&d
z%1bMJu*X}7ieJp%LjJU2-ZAGagN#>tlI#dI&KL`?Vp_7ExHR*OIoI1Shf49>TrM@G
zC5S!d7HBn&^#uLC;xLuPf&~bF@>MAXr)>Y>Iu+_SeseLY%?}+!=a44VuRi8dyHYk2bHTe-wK%#_Xt-nFMMzQ>Ys%MXO)bW!_Iq0;f8`dj
z%Ew~iwlJLw%A+|fEOd4{1u1lw*KvAfArv3=zQQ`NuZ1mKJzPJYx!ql_%SlwSZYRss
zxp&jbuDvnvYlT&>5K*9s?D1kMpx$L8d@q^g&*reo-o}W$Qe`ruE+}0*VL0#XZMtTn
z=FRmhV#wSFQswl;1(=r&44&WQn$41l?2dXb3}bt`qio?2VdX4WT5k(hD}b^$au=1FlLcXpW{
z=pJ3%Rgg$E>>=LvH6ll5u}l>VwZ2xc!dP)^(UT8)R$
zma%U}IqzzJ-N?z;<+{sEqo@0X)21V6eR2V)4?2nFT^j=7Es&}MXg=OhKZ;@fG
zv3#ix6}f%Bygh#(@T>b`u;VxIz&k!IKLrr*-%eQZ(kgt*qo~%}uc)>>4d(+jPA~DU
zZ9*I&kyf?dTLF%^)9m}ntL@T!@v)8Wwd%=iL}QEy!{R3;nYe_}+SM#nZwTVZCtj1a
zBuD_c@M|e$yt1O#mS1?yjl>7;sSVi=heM^=K=S1)SG@AoyHZdmX1{4q7kc(L<^ahR
z0{uNw?|9v%_QTOTqGQvh%hGR9yRj}rAV7jtD%PTvvs^Ie+9u`oWf%uCVzPnF^2kP<
zF}LNzF-l&
z0ure2Ch^gelEb>iuhN%RBQ1CC9=J(m6SrQ2qAM*!&U=1`2eL+Kn74cTgNOz12_6zZ}6QpcH)D!e$m^;j?Sm_0f~Kr%oRa
z9F=U&St>nZ!LZ~Y%5{=i`}rN~96hP7A6ithSy+E2!ymH!^a$$2>!~(5W+|f+jvef3CNIs$E2`xKRR^YUW%D3ApIfBe?B~7
z5q*!3QWoXPx^kQ*Y?yxu-V`7uV@~`^T(_~GlJlQTjP^LhxC`R!B0>{511~pJ3K)go
zlGn2!Rfk7!T$GvsuUt5f6K(f0ED4tVB$?cGhpj)socb5bWnX=8yPh}9h9iAuoi5$J
zoqx@v`0%UWK-IQGtVqVaLLGg9Dqm)dPjX(B(d(*lAe>3}s!{5;=ozhi`5QP1k1-K4
z5nKObX6VYR7u9ZuX4l_OXmpO~ybrC6ghAi;TW&pKTCiMvhwv*)eKXjcG9hcGdYKUZ
zINYht`R!ea&`e(p@2II+wQ1EbNnlysqR^?#RAGidyZu_wO@u@)@Q$CI6(2v+zgA?c
zAZgOCsp!D}>YU4E@3w
z(;c9v;JgLQsKN2FKr-glAE(xBp4GW7Htu*U@U&-j*RNVj1Z(tm|
z)$PXvSguPK*$U%$|B3FRC`{6ZIOFhL%cZ;Ro6xKArMXGYLjbZ1!u<`@nijl{a?nmh
z*IXJpyL*D7FfA$>_UkFjU(Eu%&%TieoAWJcJ^$X*Z_~H7d1T*zzh|5?fIs*Bk*Yfv
zb+*jG9nE_7SuPfb!Bjp<1YPAiQVR~s{01JLNqOm;^{$arWE@jy%<^5N7I{e~UWNYA
z^Ih(qX0XkWF9N4Gnov0=e8uZiXr7j6z_RJz&DYhP^s~b^D!Sk{Dx9d3ctb=LfOS`p
z^t|)qH}EAf(i7xxsxGQCwzpnnT=kaB!vAJpQ!^83+(YMFXeSmADpt*Ws
z8zB<@gMR~pM09!7R;!rvR{ixZ!fG-S3BI=12`{z2@-1`S&c)@&h}t0zZYLrs30JC{
zUx$gbmw3b0-+KRTr^&-s$=aT)y!RogyGy{1(9w32Hoq`A;j
zP3i@LhG%e}s322D`Wfa=6tDb_#mr<_g{ON4M=lzsbn;hOJxlMVyo8yb8ht*$B67<3
zScall7Za@qBOdNBD|j7xmT)FbM)O4_tCxVA>Ma36voK#CsY(P*zEk$XrLyy~C7EWf
zOtik%MH=Zbhssukm&*T0T=(}?G5Ya}F|-ik-QS8I%=Rtl;@w%7Skl>tdkUtzdY8BO
z&dBvUwL|E3@sY|~A4`N8CXw`=Ta6DJJB#Uanv7|+6wn=)(wF^JVq$;&*nGXa@$`tU
zdUkx~p(XaVw+xG!c}b@-$-B~h6Xb`lm2r7ntw3(KfcJL)s$|89?UY!Ms(05ph6?zXZtme%Jef~iP|wvmq*%5=2w{WQ%GR17CFB={y$^C{u%n3OKb93
zF`l+c9OY~@Pl8EEnd|-g-$*z~`uFSW_gw7uk-%+ovCffxFdJ$r>8Id}#2jAUZ}^tF
zz7y4XnO+!^cEi7=)$#^II@Sbhmngb>AD^yur|fe2p%~&!y_#wP-C#qhZJ&{XtwRvu
znn(IC`|lE5$ge2tw8KQrq`u?=F99}j=*(lu;!$xY?{>OpPfJ^mXV0((WG2Ch=aIFP
z(f@~poAfiGiPFV6+YVm$GqVmv!c=W#=#V6~r
zQOYhTbD+9Fbg{%e*5Oy`dhfeme_WWIgyo(7k;(ZAzCL64pToLJR1|t~mxHDI`+AV6
zI_eaF=@A`Rq3dUbc8@U<8p)Oz+P9+Pae`^q^B}
zp|2AurX~1@-+;pp2a3#D^SuGlaqZ3qW`?pP+`>hPs+?X)R>?;r{-vh}!^+(2pNuJe
zK9~J?5rf~;trK2q;-HlX)I9r)>30R=TkoaaQj$jB=-TQm;Rf@kWM`R@l3@K_lqN8o
zFuD{riZdunR+kt~`w#JVBWs5QuPK)Y{x#22YAAxV20wdi$!>L1?KkjUY9i)lv+<8-
zICHbTC!RS)QYl@M*{42}Shy!9hWah*XhNnjfwTj%;NoyyqT-bc-~U|Lk^gPkOunq7
z31{c?wz#1%Kq12pdvi&>EK+|3YvO|m_D->2k7H*LUDvY*IthAZz1x!sQ9|s{QLb#y
zka)?LnC(L53_-n}i}!^B4hgme2T~_EjE_H0MPef-!qnSNx7?Rt?iTOTkVRIeQn>Dp
zn3n4$qgoWJ?KGDvLp)I!FJ?L?2f4RJnVqD2QkB)xRSNasZVa4docX(o;=gDt!y`-m
zGLiS43LiNHP=O)eidl;O5*;9Rt0^$%5F{*>6xHiFzilqsYRk#alqQC&ldngSl-^`Q
zWJ2Hy%`B`zRyL%}h^D4Ox_rzSr1P@OOkEVdtrgm`B3!E5x}_)ri^RXK8kd~+3jVXJ
z=e=(D*El4kUUGn)RF*c;=(+bYF7ft+jhR^TCp)n&@O?B|7BV3mZUJr|hG=@E5|A`|
z0`Gj#^z#S(b?-TGIOhy>tZhIsT8Bkb6pD;0{1
zN%_5UNXX^cTKrIIt~^Vo!ozuPN!bK#tE=Sd`vn0hAawC73&+t--Z{~K=&L9|${=0w
zVtOHu<&G$fQL{TY*>Rce@{$j&xXkMX=Ek^;nQkqwW;zybbKVFhg(HSH@hb(EoR$Hf
zll5BOWb^81dK(M}SH%3hgGBE#}OlSpQB4`UVqI
zi~)F$6UjfQEVBO7nC7*bZo#Hoc@IY`agUK&Q_z&0h30Df#GkI#Plm_*=$!3YKNK9~
z{+({V9sK%*$w;lZPq#$Ur|?k1(u{c8xiBVQrhjeS#@W&AN@G
z(Cp@PE<-(;$GcY-VA~l??Hn~4TS<`bs`GZFU9`u^anQ#f@XV}=!&~7D>?`wakfwBo
zGEut0*G3Q$XUk}Ke&`4IdM(V9;{aBOgQnmP2%vA=N|uTX|vbK$FD&RDFYDg
z)fj__{ph$BgajvLZ_8(82m5<)y<(MixvO#(6%QdM!NovyL{g2L_
z6z92b6)!z&Q-QE^@yx^kH@VD?83)7^cS?e*T-;WOXrlbXaB0`f^35?h#?stMow76+
z6H5wz{dYr|&
zSzXfh3-VopP%NS6PL&(c0>)7?LMBIYM+g<84CD9o$6+LvQJf
zdZ+u%ksO8h{Ek{x8+WcJLo3!@JB02qAK))qLs%K`B}-{zhoMSq6B>}lmDlh`Cd
zLXvuuPs2XmNXriOQ7@*~7(1AO{Ia?shB{q+##p^XoeWUvEBOJDo!?nM-Pxv;R`U@-
zi`ba&$k$=qmKlda?pb8uwgGJ1O@E{MiE~+-?E^3ZMo6CEJ{wkwWG!u^ird#X58m+>
zMLalex+L2h5-P5%$+~z=d=ZnvCkJo_I;~M=06!L$
z`uNNv;9JRg_i;-0>6RiU_=n?_?GxzBLP)fa+|B>R-kZlm-M0PXpIPkN*qH{Sh8U5x
zP#9zxh6Y2@BoY}~W-M*O*!LwFdl~x}OA<9I8QHhUT2e_UBqd8J`ptD+_w#-3KYssx
z@B8__UiWkT=a18T&gD4H^H|=;`*>-KRdj}AU6$Y15!n4c@idf6ffJ+!o{lGW6TS5#
zpW28fijTenZgs3p;YQ*zlr%^|?0pq`Qmx))^2~&5PFg*$%9h?$eP#3RNBoUtpa3su
zFZ#fyaoH-&flu?_a0ek_asj^q_bZ{{s|M^M|8Q|dr-DMNfID{B~3?B=`Z
zO}YGa_#{3e_xhlT*10k5uni9)!rC#Rh+`G}
zMCyfJe$uRCE5+wnLPcfhuqC&>czMew&wKT~y^!N5!%MkLYw~}A?|)|
zZZxHp9&&>EG3OcxMpm|msp}Qc=hKK3MZw@e#Ua&R2155kkZc@J<|pr
zSvF?vjPZrKNR5;mA79G}|C;s8M?-%$Q54h~AOON%s6ve3MnHoa{^jW5`N}IeD`LPn
zfRXTPkQF#m!&UT5M+FlfeY(j535skqdopjcARDY5m>}fu%}$pLq?SgK7Jsz{!-h}c
zn}#!t7{kwV)-=J-b;g^Ia_k;uojpXG1Y6=|NBj!Fbtia#uGgYF6n00L7Yjapw0K6;
zC=`M%n@99Q0ZPQyx0sIU6qe@4BKmfi`!HNX-tGK+zsih_C0-{tc7~;^mW~t=q6yynltO
zDZJ6ERcgrFLOP&gwUzy`uksIa@RjdDMrZO
z{F%;ruf5+qt;X#ISW6)bqcT{*D!c}BU#N)}Gy-Dt($sgS=XStF@B-GS
z4Jf>I;GxNLj1N3iO{h`P^2G){7!NGI@^<>Jyo(;X&)?n2xi^186GcFV#B1o6Up-sR
zedKx{Q@7a7J{uPDH&m_Y7u>lC9miyptKch6pk4jzfcvN#o#EWhpnGb2(
zm~-Q((W}9hPu%P`kc&*=vz6WcAeY3Tx5vjomIg`Dt==@|r`~B`yH3i}|HTCdrPzGj
z01(Rx$$s7LAtCyN3{$#L1n#aJvQ%N=m
zGKRxnQ*)a5%Rg%bUrJDlF|$i!r8!cld9G3fV3Z>E!rcGI%Cq0;!Lp@S{I2XN{7C&uddz5gLlmT1z>%PF`{;#JophA(HDD
zOf!(W8;>D^fP%Z!W=m%l#y`4yyn$+}{a(kNT8wz+o`X4AD=83uJ%V_XXBy@)BtY3z
zme2hF%8}?Ju{6gkaAMET-M8{!TvKcZN_cs5ziB>+SK4`wdzhaPnB?t}VC^XBfJ9H)
zQLG#qNYkB$-Y9))S?a|{wLBwq80-ggN-W5D8ezHHLh+Rc{<;g;Jq7(X&!4Gz*hzz;
z>=XwZ(rmw$`~;LQ^KDPTu-(CEhO)w=P0b(`O;r%%e290W+8Gz0#u9K0Kw`hYt?v&y
zz1W$5((WBExpcL(%1Vq=W@K%_{zX<476~+u!Cc6g+zg1ATH0vf6fJRUdlLW6`~SAcP{}&a~;PHQZ%jvwN^PznVVDn
z)2AsGT|#snJ!LN;oAX{W(+j0eaV(a{sLah&|5`<;zTv&sj>C;L`;NgZjfX{}X>Gm&
zI-6+);~-}x5`}C=kKv-1tB?l_sZp-=SCdGi{_bHXm!WrE-q*ZQrg_i2K#L5h^nO
zM!rj0YkHK{@hx%$T~McAAaQnAP?h`WcoOYfB{S}9(Jz?xkCzq*w?pgqQT!?W^VgjdbHd>XN~$gBjerPH$lAGWSNn!ti2%c!H2R$Om0!^E}gt_r`&w>s2vdYgcQ->03M6TDgmsCEwasOR+L66e0=hCHIVt2)i
zONd7%B$cl2&m~SfO;)DeG0zdQp&{%JR-K*BXtfkT8*O*bU>C_VEIma)pQiH8Cuq#`|@@k
zuaL@Up!+5_h>(s^9FyzD0mAFPUGFk5vWG~yt!SdECX0pRa~3iv;B7Ddg+vqQAI&W`
z%EQe`bx$1S;)TR^Bi)l{|Bl?;PD9Ou!;^uBMP%)UI73ey4^;V-uS_&Cj?gLv
zes%5fA>}pb!;WrawNQ|I_Q3s
zgA7qFQCF4MR29_Tq@5pM<10ifJq+-fQyrT_F$G>g
zDRcw3_DqRtLp6QhXQgKv8<~ng-{}0VSOP<}ms3Ll@R|t}@PUeq+J73ExWJ^<^GY-Q
z5*e@&VUkMaO*0^MAeh?u(^S}7l?dN^1toEsF9O9tCfo1DvEBPrd;Le$C8sc6d63Z3
zsM}{gB(!bBy$jcZkB9Qi+ZN0qhRv;oR8`rGk1@tt9MA95%DAHPzdG{t=S2mnOt!Xi!k(0A*w!R>GScYx3@q3kcz#nDf3QWr-qQJZ6gNRSBbV&y~x4qyd>$3G7X-z
zsX_84q`7Iv&Gz(b7`Jl0s0)S#-8Eb>7z$JkPSLKljRqpy0}-e2DvVaOBg;BAWYp&t
zU#bx)t|ZUG@~&Bl{PbJ0w5EPkq}Rwe1eq+7*W`<_hp~{8ay%P`XRSNLT0#u%%ka)+=b4Cc
z4domVo3C8I>4o66i=YxAAFe9gC^N~V-UlpoJl2EC5^g!iB_q*25&EHo|8D8&xR(iY
z+T~9nM{MrJCYnAgRUHPJ=r#zasUl$|VTe9|Qqh=TJrG$J7>i<2zN)(iK^&
zf4F)vQB?cL5-9o|O`b&s`SS}Z?>+jMpWe+rS)P=sSEErralZz+i$hGNf_|eCp|zbZ
z>R6-t{Ih>0E6RE02n7==!u$ZbrRDHe0|5rh_oj)(5N!-v$ref?=G9!YwJ&pst=Jx&
zNf1zFc_tG>8T{peZkTeLf9UD33teK9jd%lNGWa5cJHef|_@kXMlR?eZ3?7yT0%67@
zR&uzGniowFR^r3@b@Mb@^Me~y^k-Y@0eRQ=7w*wk`3KKDfaIN_nI-ETjra)1(-&M9
z-f)@O$~?bu0U1dq?42=W+q%xP_~YlD%r<7s3rY{*f%a)lIViWJU_%X|+V;fZ8ir_X
zK@loK+w03X
zgL0=r^XZZpzl|aDhFqca%v3fAgAxk(&PvLTJ3
z5z=pNPe@{|%{SP#d1_6K0zDzUgh2BfkOWMTW{2UsuNaowaC4fI-38soc=O8aAmaSu
zF%cqC?KhB-kC=GrH7Ssn0_A~d$bcf+#Bck9N(8EVR%gdW9PB-YY;6;Sdv2L7nu&t^
zNTi(!?o19pNraZ^<}qC0mPYMReYA-8WqAA)tI88Y&tJ?luahR3hMZSY`2=yjjZfxYi
zjLm*}*Yj%_^x@tQj}P3Wn&Wmi4s4`j9gpcAIcm)tjQP-%`R1vNbBz_wC#Oj%Ukux}
z|2}w~3F{h2IV^)g6$oI>`_Bx0-zX3+5Y|-^~J8P+uxsL&2AN1IhGbH2@3#nh!*c
zj=Fem#-b^Pyn?j;3LP05Iw7e5pb(5Gj;tA^f#7);{c-;R!&(fwcX}~wT1Ut^yZrbP
z$jZ>7a&=lZVN*6zKZMw_!7wga#~iQC%0Id~cgM>%-!ceQ>~ZqB&m;sY;)Qndf3RbF1<%viY^TH>F0vF1^2uzxJQ~+{WC(oJ)K-v6f%<~>$J4AqW!M=H)gtXk@i#<*PqVb(go+S660?zLcjW+y2iaQ0
zkB+EUT(jn@*2-;pRAu+B#@Dnx((p9c<5G{coC~7@7pLFM&XPVeJafz0UV&53epBWo
z_T7MxL|+W*dJH?G@^h=2do~8F>oW8Ela}D7F&bB+KN75%M
zg7xjx(qRBmQmf}$F~*!ov_MX9=UNz2ano?+y<$J^I+oTRirKHVtj^o1OLYssg!$-0
z-UC#;&tSX8IeC#re7*97?P=Y!*T9%A!JhA!<6~+DvH57J!?}KqWo*Oz1P-4ret$(Z
zRJ*de6{%6ap*>(mD7Il99~B>|hCVDRceOZpLc=Y&wFIzXK+%$I%Su}OBf-T8Vr2Dz
zr130WFV(CyL0qiNq~8GQD59J5&ad!m!OU>#trvEQ`-^1+^DSSw!iU$8!`1Sh@x!%DDR#%My0
zpi0xzl;NHn0e3e(Z5(mJ=UwDZV=s7_`GMf~ILlz>hcI8fe;raeH~HZl==;%r#gm2R
z)hFyeJrr5J6IkxjZMrC_Bwxt;s?2Spq$NH&=U<=Y@7LeW8i(8Sg
z#<+={-jxcdtQoM0gTW0xp+9V6y!|2~_UxnYriTbTA?Wq{%hz002iZf{AwbZ&9N=(2
z@1^`n$9N4%{&eEehTQlL%u@Ce^>vGub8V&0iC-NDcD_d3I^)+LA)$y9VaE%qjszRz
zQ);|=$k(Xw&J1N?ztehLNCOF4CDB<=**_`Ovj!;*d=MX_`)KwU##now1vYx@yoEc8
zrdbIEMjez6O&ndP!X>xHzGOV*z-eBcpXNjd>kjeBL
z>1ffdaY)9*$PHG4o7-Zr$)$O~njDx2aL~0WU>clL?IATD;-hY*+cYrGClI;27VrXj
z+T5xl9a0MP(<={RYak<~foPElP`s|i0e#lPK1`VlW@zYZWNf6?(GeM-T(IZ`D`L+P
z=Hnx8hGHz`Z)scee4a85a1OCedI}rYeNTlTLIHNc^uY@2?6Gt2itYqYTGR+>$LUU+
z8ezX{X!6-I8hGxGo>(Csi56BSYPRyvf=?s0_jx;D@4*%j&$0KU2a@W{A_%ocNWBZX
zQ(xd8q^Y~m(o~1xB7MeQLH=M@eqEbhi`ZKlm!k^IZbNTVJ%@sgJd~@-&efi&2}#PE
zQWEyd=s-c`47cle>gZmhPQ|;DQav)p%x<%SEQp%as*@Jgj3KwLu3BRV+p)G&E2WtPsVHJV~v^yG1X-_%Ao$6(4d%Lqf{$gGFCsQ84
z+|-fM8cc2S$SYIcS?7OV4Cafti4|uTlZnMiv^!fJtMK~suQE&0<_730fI6{5V=RlLgK2!IYFFgz^r9&fO
z@5<$HJaPjvBmP$XPG)YKA=X&a-9!q=W(GqXV!N2yCy^?ixE#NtLO%5q+XqnEWcZ4o
z&P=dQRAN7}-KOEwDW>^%eHAXi6k>mcV^g=ppGOk{v6U+nVeMQ8H>D5hoT8(@F0L#8
z#)}Kqwm#Sc+u`iP4LvrDkL#^XKg?9&%s&F}`|jsv+{mSK=vKs{RUKy`6Ox89uZxK*
zJCDTTp)p>s6Q#NKiYJOEiV*4h%vTyW6gql4S~dBZEmg}n4@H_!i2Jyv(ZG#GI0y4o
z<|f&MsHaxRY>qQF8=cL21UFTY46_IZh1%$Mq5L;+rl?9S&n7}cL#(kBc)}1V<+^2a
z$yo&=QyWZF5*Lj!&%^W?)oH>`9_l-3mpO%)Z%~Ugm^m9Zia*+cM8C)L&*uhuL#ZEd
z@72;(NrXQ_%45_M!LwIT`i745hG+Bxr=j#(8K^ULAg0!TQL9W9yssRkm#aoqv2R1v
z3-r&Iq-YKehhG{(%!TPjhDo4Mx&gj*PB7DGqhz
zEgv8A*6u)oRYx;pBi(Xo?Y)5t(@5P=bUAH&w^6SIl@IgoGetinG`yYdbJFc*jA8H*
zX3?`!?;v(#XyP$MG8g(NPTFWZIc+xWDM2=p#nq-SP)+0FZgSd;xrrosxV(?&`_lgb
zbc}k!J-@O_<^#~~k_|+|%v6t>4zVpf(9XeT6OTvUyGCNjyl$p)5{hl3oZ`y6qhqy_
zOv2@c`vspeb@iUiQ>kB!!rDL#Df^($s#^z
z5zu%GIC3-OHEWFn!)j8oRZN2uzv@}Ic)~Q{r@f~{YHo~O
zc9ytB_2cRJpNUzVlJwT4zH*qfzfI#E--m8Aqjxq2WPMN$1QH9!xR~7`F_cX8c?J58
zv;fTi9&dn=;TRAC|uu?REr}}o0Yp8+#fJustL3%?Zi(`l_qt(BCTP@`xLBx
zj2jUrla*>9ldkZxrxpaFwB+y~KH#mN?f)*=)Y$grScTdhtiAEd3|6qamS^Dtz$KHJ
zc3n3+*01e{tIAhfij!K|OMrZf-Mk$Vk(Pu}f5gd8)+dSatkltAOi=`@!9fW_112
zO+Fwec%de+o9R?n9BfvD$2!YIRgQz%87EwA?Mv{z$%@mt`mCldu^ey!wd%#n>+Es=
zZNYk0{pniF?YL#tOz!he7AXpkkOoDcDVs3BLCeVU$ZVaYf0TKIm~lk#W9RD5qj)DS
zB`01;n8TpgYs<`vB%4PqvCY%otd&J1Vyp$j
zb~nE4yI_3rXI{><7Qao7V4d#AtOuLXhHe+g8Z=>EcmNK&Frv>_<~&*;+D|(ql8+lR;0t6?OVG6nr#c8;NE&U%6&GlutPOA~|h9oW%j`
zx@oqn2z$p!Z7n