From 95b44ef8bff2d6c73713ca0aef0abf33848ee168 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 18 Jun 2011 18:59:44 +0200 Subject: [PATCH 001/219] Add Pygments support to the default sourcecode directive. It is now possible to have highlited sourcecode using the ``::`` syntax. --- pelican/rstdirectives.py | 1 + samples/content/super_article.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index 4849c9c2..3d7c2fe7 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -35,3 +35,4 @@ class Pygments(Directive): return [nodes.raw('', parsed, format='html')] directives.register_directive('code-block', Pygments) +directives.register_directive('sourcecode', Pygments) diff --git a/samples/content/super_article.rst b/samples/content/super_article.rst index 03273fad..1dfd8e34 100644 --- a/samples/content/super_article.rst +++ b/samples/content/super_article.rst @@ -26,7 +26,7 @@ And here comes the cool stuff_. :width: 600 px :alt: alternate text -.. code-block:: python +:: >>> from ipdb import set_trace >>> set_trace() From eee004cdb5988a1f520143d60222a4d6c396d61f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 18 Jun 2011 19:02:57 +0200 Subject: [PATCH 002/219] update the documentation about code highlighting --- docs/getting_started.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 2187dbb9..a59bc8cf 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -159,6 +159,14 @@ right way your block codes. To do so, you have to use the following syntax:: The identifier is one of the lexers available `here `_. +You also can use the default `::` syntax:: + + :: + + your code goes here + +It will be assumed that your code is witten in python. + Autoreload ---------- From 6ac802bcf11ab61b814bb801b1aa750c1c63c74c Mon Sep 17 00:00:00 2001 From: joe di castro Date: Tue, 21 Jun 2011 03:23:41 -0700 Subject: [PATCH 003/219] Enable Markdown Extra by default. --- pelican/readers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/readers.py b/pelican/readers.py index c597ff8d..4c8acd2a 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -103,7 +103,7 @@ class MarkdownReader(Reader): def read(self, filename): """Parse content and metadata of markdown files""" text = open(filename) - md = Markdown(extensions = ['meta', 'codehilite']) + md = Markdown(extensions = ['meta', 'codehilite', 'extra']) content = md.convert(text) metadata = {} From 0183882cf78b000745e93d8ac87e2b985e6c26c4 Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Wed, 22 Jun 2011 12:22:04 -0500 Subject: [PATCH 004/219] ignoring tags file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 721af351..8e0f0658 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ docs/fr/_build build dist output +tags From a6dc53fe20316523a990e2424fb693a3bfc8a94f Mon Sep 17 00:00:00 2001 From: Borgar Date: Sun, 12 Jun 2011 20:35:48 +0000 Subject: [PATCH 005/219] Adding a date format with seconds. --- pelican/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/utils.py b/pelican/utils.py index 8e48c2e9..2fb76670 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -15,7 +15,7 @@ def get_date(string): If no format matches the given date, raise a ValuEerror """ formats = ['%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M', '%Y-%m-%d', '%Y/%m/%d', - '%d/%m/%Y', '%d.%m.%Y', '%d.%m.%Y %H:%M'] + '%d/%m/%Y', '%d.%m.%Y', '%d.%m.%Y %H:%M', '%Y-%m-%d %H:%M:%S'] for date_format in formats: try: return datetime.strptime(string, date_format) From f2ea886ed207531468930479abeb43d6a7a33a4d Mon Sep 17 00:00:00 2001 From: Borgar Date: Fri, 17 Jun 2011 19:11:44 +0000 Subject: [PATCH 006/219] Rewrote URL reformatter. This attempts to fix several issues: 1. The regexp that's supposed to catch href's doesn't work at all and even if it did match anything, it has too many parentheses for the following loop. 2. When a relative URL is replaced then it is done globally on the text and not per instance. So this `/foo/bar` will incorrectly get reformatted to `./static/foo/bar`. 3. Query parameter URLs are rewritten but shouldn't: `` gets rewritten to `` 4. The joiner is producing too many slashes: `"." + "static" + "/files/image.png"` => `./static//files/image.png`. --- pelican/writers.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pelican/writers.py b/pelican/writers.py index 1f560c01..bc6f6f2a 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -166,24 +166,23 @@ class Writer(object): """ content = input._content - hrefs = re.compile(r'<\s*[^\>]*href\s*=(^!#)\s*(["\'])(.*?)\1') - srcs = re.compile(r'<\s*[^\>]*src\s*=\s*(["\'])(.*?)\1') + hrefs = re.compile(r""" + (?P<\s*[^\>]* # match tag with src and href attr + (?:href|src)\s*=\s* + ) + (?P["\']) # require value to be quoted + (?![#?]) # don't match fragment or query URLs + (?![a-z]+:) # don't match protocol URLS + (?P.*?) # the url value + \2""", re.X) - matches = hrefs.findall(content) - matches.extend(srcs.findall(content)) - relative_paths = [] - for found in matches: - found = found[1] - if found not in relative_paths: - relative_paths.append(found) + def replacer(m): + relative_path = m.group('path') + dest_path = os.path.normpath( os.sep.join( (get_relative_path(name), + "static", relative_path) ) ) + return m.group('markup') + m.group('quote') + dest_path + m.group('quote') - for relative_path in relative_paths: - if not ":" in relative_path: # we don't want to rewrite protocols - dest_path = os.sep.join((get_relative_path(name), "static", - relative_path)) - content = content.replace(relative_path, dest_path) - - return content + return hrefs.sub(replacer, content) if context is None: return From a7d5a9a420fa9269b9555317ad8ef3d7556e3fc5 Mon Sep 17 00:00:00 2001 From: Borgar Date: Fri, 17 Jun 2011 19:09:27 +0000 Subject: [PATCH 007/219] Don't rewrite content URLs unless RELATIVE_URLS is True. --- pelican/writers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/writers.py b/pelican/writers.py index bc6f6f2a..13ab9647 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -111,7 +111,8 @@ class Writer(object): localcontext['SITEURL'] = get_relative_path(name) localcontext.update(kwargs) - self.update_context_contents(name, localcontext) + if relative_urls: + self.update_context_contents(name, localcontext) # check paginated paginated = paginated or {} From 53e25ce2cf7e1af51ba98ce309e5af4a1a59a1fe Mon Sep 17 00:00:00 2001 From: Borgar Date: Fri, 17 Jun 2011 19:08:20 +0000 Subject: [PATCH 008/219] Fixed typo in docs: missing plural s in RELATIVE_URLS. --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 9c596da3..482a5350 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -51,7 +51,7 @@ Setting name (default value) what does it do? `PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions of your documents. You will need to install `rst2pdf`. -`RELATIVE_URL` (``True``) Defines if pelican should use relative urls or +`RELATIVE_URLS` (``True``) Defines if pelican should use relative urls or not. `SITEURL` base URL of your website. Note that this is not a way to tell pelican to use relative urls From 0a26fcbd0d77e87f5e8d56941782de2e9059250f Mon Sep 17 00:00:00 2001 From: Massimo Santini Date: Thu, 30 Jun 2011 23:46:29 +0200 Subject: [PATCH 009/219] Avoiding to generate 1/1 if pagination is set to false --- pelican/themes/notmyidea/templates/pagination.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pelican/themes/notmyidea/templates/pagination.html b/pelican/themes/notmyidea/templates/pagination.html index 9cce0237..74699c4e 100644 --- a/pelican/themes/notmyidea/templates/pagination.html +++ b/pelican/themes/notmyidea/templates/pagination.html @@ -1,3 +1,4 @@ +{% if WITH_PAGINATION %}

{% if articles_page.has_previous() %} {% if articles_page.previous_page_number() == 1 %} @@ -11,3 +12,4 @@ » {% endif %}

+{% endif %} \ No newline at end of file From d8da5ce5909e014b8bb8ee4a5d52c6b8f4993801 Mon Sep 17 00:00:00 2001 From: Massimo Santini Date: Thu, 30 Jun 2011 23:49:09 +0200 Subject: [PATCH 010/219] Genetare authors pages with all their posts --- pelican/generators.py | 18 ++++++++++++++++-- .../notmyidea/templates/article_infos.html | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) mode change 100755 => 100644 pelican/generators.py diff --git a/pelican/generators.py b/pelican/generators.py old mode 100755 new mode 100644 index a397c002..b3495643 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -98,6 +98,7 @@ class ArticlesGenerator(Generator): self.dates = {} self.tags = defaultdict(list) self.categories = defaultdict(list) + self.authors = defaultdict(list) super(ArticlesGenerator, self).__init__(*args, **kwargs) self.drafts = [] @@ -182,6 +183,14 @@ class ArticlesGenerator(Generator): paginated={'articles': articles, 'dates': dates}, page_name='category/%s' % cat) + author_template = self.get_template('author') + for aut, articles in self.authors: + dates = [article for article in self.dates if article in articles] + write('author/%s.html' % aut, author_template, self.context, + author=aut, articles=articles, dates=dates, + paginated={'articles': articles, 'dates': dates}, + page_name='author/%s' % aut) + for article in self.drafts: write('drafts/%s.html' % article.slug, article_template, self.context, article=article, category=article.category) @@ -229,7 +238,7 @@ class ArticlesGenerator(Generator): for article in self.articles: # only main articles are listed in categories, not translations self.categories[article.category].append(article) - + self.authors[article.author].append(article) # sort the articles by date self.articles.sort(key=attrgetter('date'), reverse=True) @@ -269,7 +278,12 @@ class ArticlesGenerator(Generator): # order the categories per name self.categories = list(self.categories.items()) self.categories.sort(reverse=self.settings.get('REVERSE_CATEGORY_ORDER')) - self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud')) + + self.authors = list(self.authors.items()) + self.authors.sort() + + self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud', 'authors')) + def generate_output(self, writer): diff --git a/pelican/themes/notmyidea/templates/article_infos.html b/pelican/themes/notmyidea/templates/article_infos.html index 3a028a1c..e1803be8 100644 --- a/pelican/themes/notmyidea/templates/article_infos.html +++ b/pelican/themes/notmyidea/templates/article_infos.html @@ -5,7 +5,7 @@ {% if article.author %}
- By {{ article.author }} + By {{ article.author }}
{% endif %}

In {{ article.category }}. {% if PDF_PROCESSOR %}get the pdf{% endif %}

From 9d9355c5972934db005af005d0b4453dbf60fcec Mon Sep 17 00:00:00 2001 From: Michael Yanovich Date: Fri, 1 Jul 2011 16:22:21 -0400 Subject: [PATCH 011/219] added ability for pelican to handle non-ascii characters when logging --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 7112fcec..4b743d52 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -152,7 +152,7 @@ def main(): else: pelican.run() except Exception, e: - log.critical(str(e)) + log.critical(unicode(e)) if __name__ == '__main__': From 3cd84ab3962f6df97ba62d30ab00db09574edb19 Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 14:41:39 -0500 Subject: [PATCH 012/219] moving tests dir --- {pelican/tests => tests}/__init__.py | 0 {pelican/tests => tests}/test_contents.py | 0 {pelican/tests => tests}/test_readers.py | 0 {pelican/tests => tests}/test_settings.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {pelican/tests => tests}/__init__.py (100%) rename {pelican/tests => tests}/test_contents.py (100%) rename {pelican/tests => tests}/test_readers.py (100%) rename {pelican/tests => tests}/test_settings.py (100%) diff --git a/pelican/tests/__init__.py b/tests/__init__.py similarity index 100% rename from pelican/tests/__init__.py rename to tests/__init__.py diff --git a/pelican/tests/test_contents.py b/tests/test_contents.py similarity index 100% rename from pelican/tests/test_contents.py rename to tests/test_contents.py diff --git a/pelican/tests/test_readers.py b/tests/test_readers.py similarity index 100% rename from pelican/tests/test_readers.py rename to tests/test_readers.py diff --git a/pelican/tests/test_settings.py b/tests/test_settings.py similarity index 100% rename from pelican/tests/test_settings.py rename to tests/test_settings.py From dc6934be43d196c1cbf824088bff4292ccbc41b2 Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 15:15:21 -0500 Subject: [PATCH 013/219] all test pass --- pelican/settings.py | 13 +++++---- tests/content/article_with_metadata.rst | 11 +++++++ tests/default_conf.py | 38 +++++++++++++++++++++++++ tests/test_readers.py | 6 ++-- tests/test_settings.py | 5 ++-- 5 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 tests/content/article_with_metadata.rst create mode 100755 tests/default_conf.py diff --git a/pelican/settings.py b/pelican/settings.py index 1b32395c..125d0bb0 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -55,6 +55,13 @@ def read_settings(filename): if key.isupper(): context[key] = tempdict[key] + # Make the paths relative to the settings file + for path in ['PATH', 'OUTPUT_PATH']: + if path in context: + if context[path] is not None and not os.path.isabs(context[path]): + # FIXME: + context[path] = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(filename), context[path]))) + # if locales is not a list, make it one locales = context['LOCALE'] @@ -74,11 +81,5 @@ def read_settings(filename): else: log.warn("LOCALE option doesn't contain a correct value") - # Make the paths relative to the settings file - for path in ['PATH', 'OUTPUT_PATH']: - if path in context: - if context[path] is not None and not os.path.isabs(context[path]): - context[path] = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(filename), context[path]))) - # set the locale return context diff --git a/tests/content/article_with_metadata.rst b/tests/content/article_with_metadata.rst new file mode 100644 index 00000000..3410885e --- /dev/null +++ b/tests/content/article_with_metadata.rst @@ -0,0 +1,11 @@ + +This is a super article ! +######################### + +:tags: foo, bar, foobar +:date: 2010-12-02 10:14 +:category: yeah +:author: Alexis Métaireau +:summary: + Multi-line metadata should be supported + as well as **inline markup**. diff --git a/tests/default_conf.py b/tests/default_conf.py new file mode 100755 index 00000000..2796d561 --- /dev/null +++ b/tests/default_conf.py @@ -0,0 +1,38 @@ +# -*- 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_GENERATOR = False +REVERSE_CATEGORY_ORDER = True +LOCALE = "" +DEFAULT_PAGINATION = 2 + +FEED_RSS = 'feeds/all.rss.xml' +CATEGORY_FEED_RSS = 'feeds/%s.rss.xml' + +LINKS = (('Biologeek', 'http://biologeek.org'), + ('Filyb', "http://filyb.info/"), + ('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'),) + +# global metadata to all the contents +DEFAULT_METADATA = (('yeah', 'it is'),) + +# static paths will be copied under the same name +STATIC_PATHS = ["pictures",] + +# A list of files to copy from the source to the destination +FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),) + +# foobar will not be used, because it's not in caps. All configuration keys +# have to be in caps +foobar = "barbaz" diff --git a/tests/test_readers.py b/tests/test_readers.py index 5a255209..bf7504fb 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -5,7 +5,7 @@ import datetime from pelican import readers CUR_DIR = os.path.dirname(__file__) -CONTENT_PATH = os.path.join(CUR_DIR, '..', '..', 'samples', 'content') +CONTENT_PATH = os.path.join(CUR_DIR, 'content') def _filename(*args): return os.path.join(CONTENT_PATH, *args) @@ -13,9 +13,9 @@ def _filename(*args): class RstReaderTest(unittest2.TestCase): - def test_metadata(self): + def test_article_with_metadata(self): reader = readers.RstReader() - content, metadata = reader.read(_filename('super_article.rst')) + content, metadata = reader.read(_filename('article_with_metadata.rst')) expected = { 'category': 'yeah', 'author': u'Alexis Métaireau', diff --git a/tests/test_settings.py b/tests/test_settings.py index 39680a9f..8391e8c6 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -4,12 +4,11 @@ import os from pelican.settings import read_settings, _DEFAULT_CONFIG SETTINGS = os.sep.join([os.path.dirname(os.path.abspath(__file__)), - "../../samples/pelican.conf.py"]) + "default_conf.py"]) class SettingsTest(TestCase): - - def test_read_settings(self): + def test_read_settings_from_file(self): # providing a file, it should read it, replace the default values and append # new values to the settings, if any settings = read_settings(SETTINGS) From d19b0c975b3b53741ed7407f53de8478b5a9bd7e Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 15:53:27 -0500 Subject: [PATCH 014/219] test works with python2.5 --- tests/test_contents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_contents.py b/tests/test_contents.py index d013da2f..df2c00e6 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -1,3 +1,4 @@ +from __future__ import with_statement from unittest2 import TestCase from pelican.contents import Page From 53c19cc9a094348110be7b75849d0ac43555c414 Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 15:54:24 -0500 Subject: [PATCH 015/219] adding tox --- .gitignore | 1 + tox.ini | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 8e0f0658..5bb92c09 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build dist output tags +.tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..462abf09 --- /dev/null +++ b/tox.ini @@ -0,0 +1,12 @@ +[tox] +envlist = py25,py26,py27 + +[testenv] +commands=py.test +deps = + Jinja2 + Pygments + docutils + feedgenerator + unittest2 + pytest From 3958cbcce36ac9b6aeef9e5dc1bbe94c25697e71 Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 15:55:26 -0500 Subject: [PATCH 016/219] removing reqs.txt file: replaced by tox --- dev_requirements.txt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 dev_requirements.txt diff --git a/dev_requirements.txt b/dev_requirements.txt deleted file mode 100644 index acafebc0..00000000 --- a/dev_requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -Jinja2==2.5.5 -Pygments==1.4 -docutils==0.7 -feedgenerator==1.2.1 -unittest2 From 77aac049fdf0f73534a455af01d4dfe16249538f Mon Sep 17 00:00:00 2001 From: Mario Rodas Date: Sat, 2 Jul 2011 23:47:22 -0500 Subject: [PATCH 017/219] improved test_setting.py --- tests/test_settings.py | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/test_settings.py b/tests/test_settings.py index 8391e8c6..e32fcef4 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,33 +1,33 @@ -from unittest2 import TestCase -import os +import unittest2 +from os.path import dirname, abspath, join from pelican.settings import read_settings, _DEFAULT_CONFIG -SETTINGS = os.sep.join([os.path.dirname(os.path.abspath(__file__)), - "default_conf.py"]) -class SettingsTest(TestCase): +class TestSettingsFromFile(unittest2.TestCase): + """Providing a file, it should read it, replace the default values and + append new values to the settings, if any + """ + def setUp(self): + self.PATH = abspath(dirname(__file__)) + default_conf = join(self.PATH, 'default_conf.py') + self.settings = read_settings(default_conf) - def test_read_settings_from_file(self): - # providing a file, it should read it, replace the default values and append - # new values to the settings, if any - settings = read_settings(SETTINGS) + def test_overwrite_existing_settings(self): + self.assertEqual(self.settings.get('SITENAME'), u"Alexis' log") + self.assertEqual(self.settings.get('SITEURL'), + 'http://blog.notmyidea.org') - # overwrite existing settings - self.assertEqual(settings.get('SITENAME'), u"Alexis' log") - - # add new settings - self.assertEqual(settings.get('SITEURL'), 'http://blog.notmyidea.org') - - # keep default settings if not defined - self.assertEqual(settings.get('DEFAULT_CATEGORY'), + def test_keep_default_settings(self): + """keep default settings if not defined""" + self.assertEqual(self.settings.get('DEFAULT_CATEGORY'), _DEFAULT_CONFIG['DEFAULT_CATEGORY']) - # do not copy keys not in caps - self.assertNotIn('foobar', settings) + def test_dont_copy_small_keys(self): + """do not copy keys not in caps.""" + self.assertNotIn('foobar', self.settings) - - def test_empty_read_settings(self): - # providing no file should return the default values + def test_read_empty_settings(self): + """providing no file should return the default values.""" settings = read_settings(None) self.assertDictEqual(settings, _DEFAULT_CONFIG) From dd5aeb678a3130724992ad5e8dee2faa01102d5a Mon Sep 17 00:00:00 2001 From: Massimo Santini Date: Sat, 16 Jul 2011 00:44:36 +0200 Subject: [PATCH 018/219] Added author.html and authors.html templates and modified the docs accordingly --- pelican/themes/notmyidea/templates/author.html | 2 ++ pelican/themes/notmyidea/templates/authors.html | 0 2 files changed, 2 insertions(+) create mode 100644 pelican/themes/notmyidea/templates/author.html create mode 100644 pelican/themes/notmyidea/templates/authors.html diff --git a/pelican/themes/notmyidea/templates/author.html b/pelican/themes/notmyidea/templates/author.html new file mode 100644 index 00000000..0b372902 --- /dev/null +++ b/pelican/themes/notmyidea/templates/author.html @@ -0,0 +1,2 @@ +{% extends "index.html" %} +{% block title %}{{ SITENAME }} - {{ author }}{% endblock %} diff --git a/pelican/themes/notmyidea/templates/authors.html b/pelican/themes/notmyidea/templates/authors.html new file mode 100644 index 00000000..e69de29b From d7f85712dbd61fe4a5fd2f110e414c3def0615d2 Mon Sep 17 00:00:00 2001 From: Massimo Santini Date: Sat, 16 Jul 2011 00:44:49 +0200 Subject: [PATCH 019/219] Added author.html and authors.html templates and modified the docs accordingly --- docs/themes.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/themes.rst b/docs/themes.rst index 83e184f0..223215e4 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -20,6 +20,8 @@ To make your own theme, you must follow the following structure:: └── templates ├── archives.html // to display archives ├── article.html // processed for each article + ├── author.html // processed for each author + ├── authors.html // must list all the authors ├── categories.html // must list all the categories ├── category.html // processed for each category ├── index.html // the index. List all the articles @@ -85,6 +87,32 @@ dates_page The current page of articles, ordered by date, page_name 'index'. Useful for pagination links. =================== =================================================== +author.html +------------- + +This template will be processed for each of the existing authors, and will +finally remain at output/author/`author_name`.html. + +If pagination is active, next pages will remain at +output/author/`author_name``n`.html. + +=================== =================================================== +Variable Description +=================== =================================================== +author The name of the author being processed. +articles Articles of this author. +dates Articles of this author, but ordered by date, + ascending. +articles_paginator A paginator object of article list. +articles_page The current page of articles. +dates_paginator A paginator object of article list, ordered by date, + ascending. +dates_page The current page of articles, ordered by date, + ascending. +page_name 'author/`author_name`'. Useful for pagination + links. +=================== =================================================== + category.html ------------- From bafa2154ff4c0c1e3cce81faa1e57a771629c070 Mon Sep 17 00:00:00 2001 From: Skami18 Date: Tue, 19 Jul 2011 12:18:14 +0200 Subject: [PATCH 020/219] Added the `author.html` template to the `simple` theme. --- pelican/themes/simple/templates/author.html | 7 +++++++ pelican/themes/simple/templates/index.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 pelican/themes/simple/templates/author.html diff --git a/pelican/themes/simple/templates/author.html b/pelican/themes/simple/templates/author.html new file mode 100644 index 00000000..e9f78702 --- /dev/null +++ b/pelican/themes/simple/templates/author.html @@ -0,0 +1,7 @@ +{% extends "index.html" %} + +{% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %} +{% block content_title %} +

Articles by {{ author }}

+{% endblock %} + diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index 0e4ef141..0b4593a0 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -11,7 +11,7 @@

{{ article.title }}

{{ article.summary }}
From 81722f65b8a9f27cc5bacacdbf7e1744653988cf Mon Sep 17 00:00:00 2001 From: Skami18 Date: Tue, 19 Jul 2011 12:31:18 +0200 Subject: [PATCH 021/219] Template from the `simple` themes can now be extended from the other themes Templates from the `simple` themes can be used in the other themes using the `extends` keyword: {% extends "simple/index.html" %} This does not affect the behavior of Pelican:, so there is no need to modify the existing themes. --- TODO | 1 - pelican/generators.py | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index 9928ced2..9829c9c5 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ * 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. * Make the program support UTF8-encoded files as input (and later: any encoding?) * Add status support (draft, published, hidden) * Add a serve + automatic generation behaviour. diff --git a/pelican/generators.py b/pelican/generators.py index b3495643..5b51320d 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -8,7 +8,7 @@ import os import math import random -from jinja2 import Environment, FileSystemLoader +from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader from jinja2.exceptions import TemplateNotFound from pelican.utils import copy, get_relative_path, process_translations, open @@ -32,7 +32,10 @@ class Generator(object): self._templates = {} self._templates_path = os.path.expanduser(os.path.join(self.theme, 'templates')) self._env = Environment( - loader=FileSystemLoader(self._templates_path), + loader=ChoiceLoader([ + FileSystemLoader(self._templates_path), + PrefixLoader({'simple' : FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), "themes", "simple", "templates"))}) + ]), extensions=self.settings.get('JINJA_EXTENSIONS', []), ) @@ -49,6 +52,7 @@ class Generator(object): try: self._templates[name] = self._env.get_template(name + '.html') except TemplateNotFound: + debug('self._env.list_templates(): {0}'.format(self._env.list_templates())) raise Exception('[templates] unable to load %s.html from %s' % ( name, self._templates_path)) return self._templates[name] From 3f844ee5d8d16fb88dc09588d7b0d54ecbe9f95c Mon Sep 17 00:00:00 2001 From: Skami18 Date: Thu, 21 Jul 2011 18:41:25 +0200 Subject: [PATCH 022/219] Removed an URL-related bug in templates --- pelican/themes/simple/templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index 0b4593a0..fd8545df 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -8,7 +8,7 @@
    {% for article in articles_page.object_list %}
  1. -

    {{ article.title }}

    +

    {{ article.title }}

    {{ article.locale_date }} {% if article.author %}
    By {{ article.author }}
    {% endif %} From dc8b168fc1b062070ed512018b1d45a0804a8ec0 Mon Sep 17 00:00:00 2001 From: Skami18 Date: Thu, 21 Jul 2011 18:43:18 +0200 Subject: [PATCH 023/219] Improved the menu in themes/simple/templates/index.html. Added a #menu id --- pelican/themes/simple/templates/base.html | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index 0477053d..172f1ea6 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -9,11 +9,20 @@ - {% if categories %}
      - {% for category, articles in categories %} -
    • {{ category }}
    • +
    {% block content %} {% endblock %}
    From 55bc68438005359a524dfe37c9cf2964a5e31084 Mon Sep 17 00:00:00 2001 From: Skami18 Date: Thu, 21 Jul 2011 19:10:38 +0200 Subject: [PATCH 024/219] Added a `head` block to the `simple` theme. The html lang attribute of the `simple` theme now depends of the `DEFAULT_LANG` variable. --- pelican/themes/simple/templates/base.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index 172f1ea6..dc266773 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -1,8 +1,10 @@ - + - {% block title %}{{ SITENAME }}{%endblock%} + {% block head %} + {% block title %}{{ SITENAME }}{% endblock title %} + {% endblock head %} From d77f657aa757644e30d7e1f26af9d61692b6de23 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 27 May 2011 05:22:55 +0800 Subject: [PATCH 025/219] add dotclear importer - a bit ugly but it works ! some issues remain with end of lines --- tools/importer.py | 179 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 145 insertions(+), 34 deletions(-) diff --git a/tools/importer.py b/tools/importer.py index 719084b9..612767b3 100755 --- a/tools/importer.py +++ b/tools/importer.py @@ -21,30 +21,112 @@ def wp2fields(xml): title = item.title.contents[0] content = item.fetch('content:encoded')[0].contents[0] filename = item.fetch('wp:post_name')[0].contents[0] - + raw_date = item.fetch('wp:post_date')[0].contents[0] date_object = time.strptime(raw_date, "%Y-%m-%d %H:%M:%S") date = time.strftime("%Y-%m-%d %H:%M", date_object) - + author = item.fetch('dc:creator')[0].contents[0].title() categories = [(cat['nicename'],cat.contents[0]) for cat in item.fetch(domain='category')] - + tags = [tag.contents[0].title() for tag in item.fetch(domain='tag', nicename=None)] - - yield (title, content, filename, date, author, categories, tags) + + yield (title, content, filename, date, author, categories, tags, "html") + +def dc2fields(file): + """Opens a Dotclear export file, and yield pelican fields""" + in_cat = False + in_post = False + category_list = {} + posts = [] + + with open(file, 'r', encoding='utf-8') as f: + + for line in f: + # remove final \n + line = line[:-1] + + if line.startswith('[category'): + in_cat = True + elif line.startswith('[post'): + in_post = True + elif in_cat: + fields = line.split('","') + if not line: + in_cat = False + else: + # remove 1st and last "" + fields[0] = fields[0][1:] + # fields[-1] = fields[-1][:-1] + category_list[fields[0]]=fields[2] + elif in_post: + if not line: + in_post = False + break + else: + posts.append(line) + + print "%i posts read." % len(posts) + + for post in posts: + fields = post.split('","') + + # post_id = fields[0][1:] + # blog_id = fields[1] + # user_id = fields[2] + cat_id = fields[3] + # post_dt = fields[4] + # post_tz = fields[5] + post_creadt = fields[6] + # post_upddt = fields[7] + # post_password = fields[8] + post_type = fields[9] + post_format = fields[10] + post_url = fields[11] + post_lang = fields[12] + post_title = fields[13] + post_excerpt = fields[14] + post_excerpt_xhtml = fields[15] + post_content = fields[16] + post_content_xhtml = fields[17] + # post_notes = fields[18] + # post_words = fields[19] + # post_status = fields[20] + # post_selected = fields[21] + # post_position = fields[22] + # post_open_comment = fields[23] + # post_open_tb = fields[24] + # nb_comment = fields[25] + # nb_trackback = fields[26] + # post_meta = fields[27] + # redirect_url = fields[28][:-1] + + author = "" + categories = "" + if cat_id: + categories = category_list[cat_id] + tags = "" + + if post_format == "markdown": + content = post_excerpt + post_content + else: + content = post_excerpt_xhtml + post_content_xhtml + + yield (post_title, content, post_url, post_creadt, author, categories, tags, post_format) + def feed2fields(file): """Read a feed and yield pelican fields""" import feedparser d = feedparser.parse(file) for entry in d.entries: - date = (time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed) + date = (time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed) if hasattr(entry, "updated_parsed") else None) author = entry.author if hasattr(entry, "author") else None tags = [e['term'] for e in entry.tags] if hasattr(entry, "tags") else None slug = slugify(entry.title) - yield (entry.title, entry.description, slug, date, author, [], tags) + yield (entry.title, entry.description, slug, date, author, [], tags, "html") def build_header(title, date, author, categories, tags): @@ -53,43 +135,68 @@ def build_header(title, date, author, categories, tags): if date: header += ':date: %s\n' % date if categories: - header += ':category: %s\n' % ', '.join(categories) + header += ':category: %s\n' % categories if tags: header += ':tags: %s\n' % ', '.join(tags) header += '\n' return header +def build_markdown_header(title, date, author, categories, tags): + """Build a header from a list of fields""" + header = 'Title: %s\n' % title + if date: + header += 'Date: %s\n' % date + if categories: + header += 'Category: %s\n' % categories + if tags: + header += 'Tags: %s\n' % ', '.join(tags) + header += '\n' + return header def fields2pelican(fields, output_path): - for title, content, filename, date, author, categories, tags in fields: - html_filename = os.path.join(output_path, filename+'.html') - - if(len(categories) == 1): - rst_filename = os.path.join(output_path, categories[0][0], filename+'.rst') - if not os.path.isdir(os.path.join(output_path, categories[0][0])): - os.mkdir(os.path.join(output_path, categories[0][0])) + for title, content, filename, date, author, categories, tags, markup in fields: + if markup == "markdown": + md_filename = os.path.join(output_path, filename+'.md') + header = build_markdown_header(title, date, author, categories, tags) + + # content.replace('\r\n', '\n') + + with open(md_filename, 'w', encoding='utf-8') as fp: + fp.write(header+content) + else: + filename = os.path.basename(filename) + html_filename = os.path.join(output_path, filename+'.html') + + # if(len(categories) == 1): + # rst_filename = os.path.join(output_path, categories[0][0], filename+'.rst') + # if not os.path.isdir(os.path.join(output_path, categories[0][0])): + # os.mkdir(os.path.join(output_path, categories[0][0])) + # else: rst_filename = os.path.join(output_path, filename+'.rst') - with open(html_filename, 'w', encoding='utf-8') as fp: - fp.write(content) - - os.system('pandoc --from=html --to=rst -o %s %s' % (rst_filename, - html_filename)) - - os.remove(html_filename) - - with open(rst_filename, 'r', encoding='utf-8') as fs: - content = fs.read() - with open(rst_filename, 'w', encoding='utf-8') as fs: - categories = [x[1] for x in categories] - header = build_header(title, date, author, categories, tags) - fs.write(header + content) + with open(html_filename, 'w', encoding='utf-8') as fp: + fp.write(content) + + print rst_filename + os.system('pandoc --normalize --reference-links --from=html --to=rst -o %s %s' % (rst_filename, + html_filename)) + + os.remove(html_filename) + + with open(rst_filename, 'r', encoding='utf-8') as fs: + content = fs.read() + with open(rst_filename, 'w', encoding='utf-8') as fs: + # categories = [x[1] for x in categories] + header = build_header(title, date, author, categories, tags) + fs.write(header + content) def main(input_type, input, output_path): if input_type == 'wordpress': fields = wp2fields(input) + elif input_type == 'dotclear': + fields = dc2fields(input) elif input_type == 'feed': fields = feed2fields(input) @@ -102,17 +209,21 @@ if __name__ == '__main__': "Be sure to have pandoc installed") parser.add_argument(dest='input', help='The input file to read') - parser.add_argument('--wpfile', action='store_true', dest='wpfile', + parser.add_argument('--wpfile', action='store_true', dest='wpfile', help='Wordpress XML export') - parser.add_argument('--feed', action='store_true', dest='feed', + parser.add_argument('--dotclear', action='store_true', dest='dotclear', + help='Dotclear export') + parser.add_argument('--feed', action='store_true', dest='feed', help='feed to parse') - parser.add_argument('-o', '--output', dest='output', default='output', + parser.add_argument('-o', '--output', dest='output', default='output', help='Output path') - args = parser.parse_args() - + args = parser.parse_args() + input_type = None if args.wpfile: input_type = 'wordpress' + elif args.dotclear: + input_type = 'dotclear' elif args.feed: input_type = 'feed' else: From b9aa3d0bfd6904dbdd1ef4e078905449a6a38d91 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 22 Jul 2011 01:56:23 +0200 Subject: [PATCH 026/219] add a changelog with features per version --- CHANGELOG | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..57056826 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,80 @@ +3.0 + +* dotclear importer +* Markdown extensions +* Theme extensions +* Plugins support + +2.7 + +* Uses logging rather than echoing to stdout +* Support custom jinja filters +* Compatibility with python 2.5 +* Add a theme manager +* Packaged for debian +* Add draft support + +2.6 + +* changes in the output directory structure +* makes templates easier to work with / create +* Add RSS support (was only atom previously) +* Add tag support for the feeds +* Enhance the documentation +* Add another theme (brownstone) +* Add translations +* Add a way to use "cleaner urls" with a rewrite url module (or equivalent) +* Add a tag cloud +* Add an autoreloading feature: the blog is automatically regenerated each time a modification is detected +* Translate the documentation in french +* import a blog from an rss feed +* Pagination support +* Add skribit support + +2.5 + +* import from wordpress +* add some new themes (martyalchin / wide-notmyidea) +* first bug report ! +* linkedin support +* added a FAQ +* google analytics support +* twitter support +* use relative urls not static ones + +2.4 + +* minor themes changes +* add disqus support (so we have comments) +* another code refactoring +* add config settings about pages +* blog entries can also be generated in pdf + +2.3 + +* markdown support + +2.2 + +* Prettify output +* Manages static pages as well + +2.1 + +* Put the notmyidea theme by default + +2.0 + +* Refactoring to be more extensible +* Change into the setting variables + +1.2 + +* Add a debug option +* Add feeds per category +* Use filsystem to get dates if no metadata provided +* Add pygment support + +1.1: + +* first working version From fc2843306f803d1aabf449b60e9840053f4fb06f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 22 Jul 2011 02:11:18 +0200 Subject: [PATCH 027/219] Add a settings.py sample into the documentation. Fixes #118 --- docs/settings.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index 482a5350..7eb1b12a 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -287,3 +287,9 @@ Setting name (default value) what does it do? ================================================ ===================================================== .. _pelican-themes: :doc:`pelican-themes` + +Example settings +================ + +.. literalinclude:: ../samples/pelican.conf.py + :language: python From 53b295b980404f1c0326bbdd5e477340f3850460 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 22 Jul 2011 02:12:10 +0200 Subject: [PATCH 028/219] Update the features proposed by pelican on the doc. --- docs/fr/index.rst | 1 + docs/index.rst | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/fr/index.rst b/docs/fr/index.rst index dca85fac..9fd0c0f0 100644 --- a/docs/fr/index.rst +++ b/docs/fr/index.rst @@ -54,3 +54,4 @@ Documentation parametres_article astuces faq + pelican-themes diff --git a/docs/index.rst b/docs/index.rst index 2897bc63..cbc6bb19 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,12 +14,16 @@ Features Pelican currently supports: -* blog articles +* blog articles and simple pages * 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 `_) + comments by yourself. It could potentially eat your data. (optional) +* easy theming (themes are done using `jinja2 `_) * PDF generation of the articles/pages (optional). +* publication of articles in various languages +* RSS/Atom feeds +* wordpress/dotclear or RSS imports +* integration with various tools: twitter/google analytics/skribit (optional) Why the name "Pelican" ? ======================== @@ -56,5 +60,6 @@ A french version of the documentation is available at :doc:`fr/index`. settings themes internals + pelican-themes faq contribute From 17e7fb450952010fde3777298bcb9c69e86ace7c Mon Sep 17 00:00:00 2001 From: Skami18 Date: Fri, 22 Jul 2011 18:45:41 +0200 Subject: [PATCH 029/219] =?UTF-8?q?Some=20changes=20in=20the=20templates?= =?UTF-8?q?=20loading=20system=20=E2=80=94=20documentation=20will=20be=20u?= =?UTF-8?q?pdated=20soon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The `simple` theme can now be extended with the syntax `{% extends "!simple/index.html" %}` instead of `{% extends "simple/index.html" %}` to avoid conflicts with a `simple/` folder. - If a template is missing in a theme, it will be replaced by the corresponding template of the `simple` theme, so it's possible to make a new theme with only two file: a `base.html` file that extends the `base.html` file of the `simple` theme, and a CSS stylesheet, for example. --- pelican/generators.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 5b51320d..d91283f3 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -31,13 +31,16 @@ class Generator(object): # templates cache self._templates = {} self._templates_path = os.path.expanduser(os.path.join(self.theme, 'templates')) + simple_loader = FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), "themes", "simple", "templates")) self._env = Environment( loader=ChoiceLoader([ FileSystemLoader(self._templates_path), - PrefixLoader({'simple' : FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), "themes", "simple", "templates"))}) + simple_loader, # implicit inheritance + PrefixLoader({'!simple' : simple_loader}) # explicit inheritance ]), extensions=self.settings.get('JINJA_EXTENSIONS', []), ) + debug('self._env.list_templates(): {0}'.format(self._env.list_templates())) # get custom Jinja filters from user settings custom_filters = self.settings.get('JINJA_FILTERS', {}) @@ -52,7 +55,6 @@ class Generator(object): try: self._templates[name] = self._env.get_template(name + '.html') except TemplateNotFound: - debug('self._env.list_templates(): {0}'.format(self._env.list_templates())) raise Exception('[templates] unable to load %s.html from %s' % ( name, self._templates_path)) return self._templates[name] From 52df0dc47ab07cb6b44438993e2d361312b4b374 Mon Sep 17 00:00:00 2001 From: Skami18 Date: Sat, 23 Jul 2011 14:53:55 +0200 Subject: [PATCH 030/219] Added the documentation for template inheritance Requires a re-reading by someone speaking English better than me... --- docs/_static/theme-basic.zip | Bin 0 -> 1449 bytes docs/themes.rst | 96 +++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 docs/_static/theme-basic.zip diff --git a/docs/_static/theme-basic.zip b/docs/_static/theme-basic.zip new file mode 100644 index 0000000000000000000000000000000000000000..d1e4754a44fcedc4c1c16f34b3782c225d13da3c GIT binary patch literal 1449 zcmWIWW@h1H0D)14dG;9W((8wS6ac%z{v7~nSlXJ z1ifa@?NLjPW7=i{?%>6U=(#wrR2My!>G1rlT`v1U_xwrQE(L(%f)Tjl0irVcqV9wdeM| zwVw5WTWqfF5yn$XK6f;kTIT2;dZJdt<=683ZE3CjyUv8GiV3#2*Urp$td2bTa^u~( zTj~+vbUhI@oOt2BD=tYafd%(8UroP>%kc!aUgmd{=n_OXCb<|I-Y-^b`Ayx2)hI}K zFHQY!m-4{A#)^@F;SUo7gA_KSic2bUQuTm_f&<)inU+bm9tK6L!WXNgaq zH*?M2re0ff@K_P&?Z}*|*CeD?X8d7Tc_TsNVXm%ik8Z#fH?ECG89HUMG8XN!3$6^2 z+O0D0qgU_BQm?voMIrLvZ`z&?d1)Zy_{i*g?9Ov<8qc;C=Vo`Nl}A znLqev^PF&5skrlA*&&tSOZ#){TT3#z1N^z!vXzYXEXdc~#HG`7S5JBU=@WU9mCtofH4rmK2RR&CybLe@`RGfvuzJzi+Mb+X1Yvsv6LW?VT@0-7fU7~VR9n6P}w3dxsfv4CtAX0Ajw%K>f{avlYm3d*Bc zO~sW%A*KQo5W|wjE-a=Z)11OPn40F4A?7(7N|W*1~5 z*P|N=$vi+~L74}uvAD7hazOmTW-L-h0-6uXNEqg`vH?Sofei?`f!VPGSk^Ky00373 BtK + + {% extends "index.html" %} + + +Example +------- + +With this system, it is possible to create a theme with just two file. + +base.html +""""""""" + +The first file is the ``templates/base.html`` template: + +.. code-block:: html+jinja + + {% extends "!simple/base.html" %} + + {% block head %} + {{ super() }} + + {% endblock %} + + +1. On the first line, we extends the ``base.html`` template of the ``simple`` theme, so we don't have to rewrite the entire file. +2. On the third line, we open the ``head`` block, that has already been defined in the ``simple`` theme +3. On the fourth line, the function ``super()`` keeps the content previously inserted in the ``head`` block. +4. On the fifth line, we append a stylesheet to the page +5. On the last line, we close the ``head`` block. + +This file will be extended by all the others templates, so the stylesheet will be included in all pages. + +style.css +""""""""" + +The second file is the ``static/css/style.css`` CSS stylesheet: + +.. code-block:: css + + body { + font-family : monospace ; + font-size : 100% ; + background-color : white ; + color : #111 ; + width : 80% ; + min-width : 400px ; + min-height : 200px ; + padding : 1em ; + margin : 5% 10% ; + border : thin solid gray ; + border-radius : 5px ; + display : block ; + } + + a:link { color : blue ; text-decoration : none ; } + a:hover { color : blue ; text-decoration : underline ; } + a:visited { color : blue ; } + + h1 a { color : inherit !important } + h2 a { color : inherit !important } + h3 a { color : inherit !important } + h4 a { color : inherit !important } + h5 a { color : inherit !important } + h6 a { color : inherit !important } + + pre { + margin : 2em 1em 2em 4em ; + } + + #menu li { + display : inline ; + } + + #post-list { + margin-bottom : 1em ; + margin-top : 1em ; + } + +Download +"""""""" + +You can download this example theme :download:`here <_static/theme-basic.zip>`. From 09d7847c7ec29331edcdb4a68fd3c7649769c44f Mon Sep 17 00:00:00 2001 From: Skami18 Date: Sat, 23 Jul 2011 15:01:46 +0200 Subject: [PATCH 031/219] Modified the sphinx settings It seems the manpages ``pelican-themes.en.1`` and ``pelican-themes.fr.1`` were only used for the Debian packages. Unforunately, ``dh_manpages`` seems to support installation of manpages depending of their languages, so i've changed the settings and only ``pelican-themes.1`` is generated, this avoids having to rename the manpages before packaging... The documentation about making themes for Pelican is now generated in the file ``pelican-theming.1``. --- docs/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 3cd6c061..4c4530e2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,7 +40,7 @@ man_pages = [ ('index', 'pelican', u'pelican documentation', [u'Alexis Métaireau'], 1), ('pelican-themes', 'pelican-themes', u'A theme manager for Pelican', - [u'Mickaël Raybaud'], 'en.1'), - ('fr/pelican-themes', 'pelican-themes', u'Un gestionnaire de thèmes pour Pelican', - [u'Mickaël Raybaud'], 'fr.1') + [u'Mickaël Raybaud'], 1), + ('themes', 'pelican-theming', u'How to create themes for Pelican', + [u'The Pelican contributors'], 1) ] From 6611107e4856ebef8e63c9aae6e12a122ed0367a Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 23 Jul 2011 16:06:58 +0200 Subject: [PATCH 032/219] little english review on theme extension --- docs/themes.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/themes.rst b/docs/themes.rst index 3d43895f..2ebfcf49 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -209,11 +209,11 @@ You can take a look at notmyidea default theme for working example. Inheritance =========== -The last version of Pelican supports inheritance from the ``simple`` theme, so you can reuse the templates of the ``simple`` theme in your own themes: +Since version 3, pelican supports inheritance from the ``simple`` theme, so you can reuse the templates of the ``simple`` theme in your own themes: -If one of the mandatory files in the ``templates/`` directory of your theme is missing, it will be replaced by the corresponding template from the ``simple`` theme, so if the HTML structure of a template of the ``simple`` theme is right for you, you don't have to rewrite it from scratch. +If one of the mandatory files in the ``templates/`` directory of your theme is missing, it will be replaced by the matching template from the ``simple`` theme, so if the HTML structure of a template of the ``simple`` theme is right for you, you don't have to rewrite it from scratch. -You can also extend templates of the ``simple`` themes in your own themes by using the ``{% extends %}`` directove as in the following example: +You can also extend templates of the ``simple`` themes in your own themes by using the ``{% extends %}`` directive as in the following example: .. code-block:: html+jinja @@ -225,7 +225,7 @@ You can also extend templates of the ``simple`` themes in your own themes by usi Example ------- -With this system, it is possible to create a theme with just two file. +With this system, it is possible to create a theme with just two files. base.html """"""""" @@ -242,13 +242,13 @@ The first file is the ``templates/base.html`` template: {% endblock %} -1. On the first line, we extends the ``base.html`` template of the ``simple`` theme, so we don't have to rewrite the entire file. -2. On the third line, we open the ``head`` block, that has already been defined in the ``simple`` theme +1. On the first line, we extend the ``base.html`` template of the ``simple`` theme, so we don't have to rewrite the entire file. +2. On the third line, we open the ``head`` block which has already been defined in the ``simple`` theme 3. On the fourth line, the function ``super()`` keeps the content previously inserted in the ``head`` block. 4. On the fifth line, we append a stylesheet to the page 5. On the last line, we close the ``head`` block. -This file will be extended by all the others templates, so the stylesheet will be included in all pages. +This file will be extended by all the other templates, so the stylesheet will be linked from all pages. style.css """"""""" From b892f758a8745667d784b75966f6c4ebfae3ab83 Mon Sep 17 00:00:00 2001 From: Skami18 Date: Tue, 26 Jul 2011 17:45:15 +0200 Subject: [PATCH 033/219] Pelican show a warning instead of raising an exception if the author of an article is not found an there is no settings file --- pelican/contents.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pelican/contents.py b/pelican/contents.py index 30477493..b7bc2502 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -2,6 +2,7 @@ from pelican.utils import slugify, truncate_html_words from pelican.log import * from pelican.settings import _DEFAULT_CONFIG +from os import getenv class Page(object): """Represents a page @@ -34,6 +35,9 @@ class Page(object): if not hasattr(self, 'author'): if 'AUTHOR' in settings: self.author = settings['AUTHOR'] + else: + self.author = getenv('USER', 'John Doe') + warning("Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author).decode("utf-8")) # manage languages self.in_default_lang = True From 068a3d2f1db914082a00a944c4332796e37d7a75 Mon Sep 17 00:00:00 2001 From: mviera Date: Wed, 27 Jul 2011 01:35:06 +0200 Subject: [PATCH 034/219] Year and month in URL. Issue #145 --- docs/settings.rst | 4 ++++ pelican/generators.py | 12 ++++++++++++ pelican/settings.py | 1 + pelican/themes/notmyidea/templates/article.html | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 7eb1b12a..99899058 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -61,6 +61,10 @@ Setting name (default value) what does it do? on the output path "static". By default, pelican will copy the 'images' folder to the output folder. +`PERMALINK_STRUCTURE` (``'/%Y/%m/'``) Allows to render URLs for articles sorted by date, + in case you specify a format as specified in the + example. Also, you can specify any other word + that you want. ================================================ ===================================================== diff --git a/pelican/generators.py b/pelican/generators.py index d91283f3..aafff068 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -7,6 +7,7 @@ from collections import defaultdict import os import math import random +import urlparse from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader from jinja2.exceptions import TemplateNotFound @@ -159,6 +160,17 @@ class ArticlesGenerator(Generator): # in writer, articles pass first article_template = self.get_template('article') for article in chain(self.translations, self.articles): + add_to_url = u'' + if self.settings.has_key('PERMALINK_STRUCTURE'): + permalink_structure = self.settings.get('PERMALINK_STRUCTURE') + permalink_structure = permalink_structure.lstrip('/') + try: + add_to_url = article.date.strftime(permalink_structure) + except: + pass + + article.url = urlparse.urljoin(add_to_url, article.url) + article.save_as = urlparse.urljoin(add_to_url, article.save_as) write(article.save_as, article_template, self.context, article=article, category=article.category) diff --git a/pelican/settings.py b/pelican/settings.py index 125d0bb0..dc87728d 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -42,6 +42,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'DEFAULT_METADATA': (), 'FILES_TO_COPY': (), 'DEFAULT_STATUS': 'published', + 'PERMALINK_STRUCTURE': '' } def read_settings(filename): diff --git a/pelican/themes/notmyidea/templates/article.html b/pelican/themes/notmyidea/templates/article.html index 14fffe4d..6615b63a 100644 --- a/pelican/themes/notmyidea/templates/article.html +++ b/pelican/themes/notmyidea/templates/article.html @@ -3,7 +3,7 @@ {% block content %}
    -

    {{ article.title }}

    {% include 'twitter.html' %}

    From 2609004719d2b8bfec9a4d614e6e3d92ec21091a Mon Sep 17 00:00:00 2001 From: mviera Date: Fri, 29 Jul 2011 01:11:35 +0200 Subject: [PATCH 035/219] I change PERMALINK_STRUCTURE to ARTICLE_PERMALINK_STRUCTURE and updating the settings.rst documentation. Also I have implemented other options to this setting, such as the category, the date, the author, this kind of things. Finally, I have setted the ARTICLE_PERMALINK_STRUCTURE option as null in pelican.conf.py sample file. --- docs/settings.rst | 18 +++++++++++++++--- pelican/generators.py | 13 +++++++++---- pelican/settings.py | 2 +- samples/pelican.conf.py | 3 +++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 99899058..ba7ebd72 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -61,10 +61,22 @@ Setting name (default value) what does it do? on the output path "static". By default, pelican will copy the 'images' folder to the output folder. -`PERMALINK_STRUCTURE` (``'/%Y/%m/'``) Allows to render URLs for articles sorted by date, +`ARTICLE_PERMALINK_STRUCTURE` (``'/%Y/%m/'``) Allows to render URLs for articles sorted by date, in case you specify a format as specified in the - example. Also, you can specify any other word - that you want. + example. It follows the python datetime directives: + * %Y: Year with century as a decimal number. + * %m: Month as a decimal number [01,12]. + * %d: Day of the month as a decimal number [01,31]. + + Check the python datetime documentation + at http://bit.ly/cNcJUC for more information. + + Also, you can use any metadata in the + restructured text files: + * category: '%(category)s' + * author: '%(author)s' + * tags: '%(tags)s' + * date: '%(date)s' ================================================ ===================================================== diff --git a/pelican/generators.py b/pelican/generators.py index aafff068..bc2bd574 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -13,6 +13,7 @@ from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader from jinja2.exceptions import TemplateNotFound from pelican.utils import copy, get_relative_path, process_translations, open +from pelican.utils import slugify from pelican.contents import Article, Page, is_valid_content from pelican.readers import read_file from pelican.log import * @@ -161,14 +162,18 @@ class ArticlesGenerator(Generator): article_template = self.get_template('article') for article in chain(self.translations, self.articles): add_to_url = u'' - if self.settings.has_key('PERMALINK_STRUCTURE'): - permalink_structure = self.settings.get('PERMALINK_STRUCTURE') - permalink_structure = permalink_structure.lstrip('/') + if self.settings.has_key('ARTICLE_PERMALINK_STRUCTURE'): + article_permalink_structure = self.settings.get('ARTICLE_PERMALINK_STRUCTURE') + article_permalink_structure = article_permalink_structure.lstrip('/') try: - add_to_url = article.date.strftime(permalink_structure) + add_to_url = article.date.strftime(article_permalink_structure) except: pass + add_to_url = add_to_url % article.__dict__ + add_to_url = [slugify(i) for i in add_to_url.split('/')] + add_to_url = os.path.join(*add_to_url) + article.url = urlparse.urljoin(add_to_url, article.url) article.save_as = urlparse.urljoin(add_to_url, article.save_as) write(article.save_as, diff --git a/pelican/settings.py b/pelican/settings.py index dc87728d..af6b1bd9 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -42,7 +42,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'DEFAULT_METADATA': (), 'FILES_TO_COPY': (), 'DEFAULT_STATUS': 'published', - 'PERMALINK_STRUCTURE': '' + 'ARTICLE_PERMALINK_STRUCTURE': '/%Y/%m/' } def read_settings(filename): diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 2796d561..9b074416 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -9,6 +9,9 @@ PDF_GENERATOR = False REVERSE_CATEGORY_ORDER = True LOCALE = "" DEFAULT_PAGINATION = 2 +# Allows to construct an url like /2011/07/sample-post.html +# See documentation for more info. +ARTICLE_PERMALINK_STRUCTURE = '' FEED_RSS = 'feeds/all.rss.xml' CATEGORY_FEED_RSS = 'feeds/%s.rss.xml' From a13ae91f0bba5b0ce94fb58b39c713f26ccf2a1c Mon Sep 17 00:00:00 2001 From: mviera Date: Fri, 29 Jul 2011 22:08:21 +0200 Subject: [PATCH 036/219] Adding more documentation about ARTICLE_PERMALINK_STRUCTURE in settings. Also, i deleted the try except because strftime never raises an exception. Issue #145 I set the ARTICLE_PERMALINK_STRUCTURE option as null in settings.py and remove it from pelican sample config file. --- docs/settings.rst | 18 +++++++++++++++--- pelican/generators.py | 11 +++++------ pelican/settings.py | 2 +- samples/pelican.conf.py | 3 --- 4 files changed, 21 insertions(+), 13 deletions(-) mode change 100644 => 100755 pelican/generators.py diff --git a/docs/settings.rst b/docs/settings.rst index ba7ebd72..2bdc891f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -61,13 +61,19 @@ Setting name (default value) what does it do? on the output path "static". By default, pelican will copy the 'images' folder to the output folder. -`ARTICLE_PERMALINK_STRUCTURE` (``'/%Y/%m/'``) Allows to render URLs for articles sorted by date, - in case you specify a format as specified in the - example. It follows the python datetime directives: +`ARTICLE_PERMALINK_STRUCTURE` (``''``) Empty by default. Allows to render URLs for + articles sorted by date, in case you specify a + format as specified in the example. + It follows the python datetime directives: * %Y: Year with century as a decimal number. * %m: Month as a decimal number [01,12]. * %d: Day of the month as a decimal number [01,31]. + Note: if you specify a datetime directive, it will + be substituted using the date metadata field into + the rest file. if the date is not specified, pelican + will rely on the mtime of your file. + Check the python datetime documentation at http://bit.ly/cNcJUC for more information. @@ -77,6 +83,12 @@ Setting name (default value) what does it do? * author: '%(author)s' * tags: '%(tags)s' * date: '%(date)s' + + Example usage: + * '/%Y/%m/' it will be something like + '/2011/07/sample-post.html'. + * '/%Y/%(category)s/' it will be something like + '/2011/life/sample-post.html'. ================================================ ===================================================== diff --git a/pelican/generators.py b/pelican/generators.py old mode 100644 new mode 100755 index bc2bd574..8d6de666 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -162,14 +162,13 @@ class ArticlesGenerator(Generator): article_template = self.get_template('article') for article in chain(self.translations, self.articles): add_to_url = u'' - if self.settings.has_key('ARTICLE_PERMALINK_STRUCTURE'): - article_permalink_structure = self.settings.get('ARTICLE_PERMALINK_STRUCTURE') + if 'ARTICLE_PERMALINK_STRUCTURE' in self.settings: + article_permalink_structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] article_permalink_structure = article_permalink_structure.lstrip('/') - try: - add_to_url = article.date.strftime(article_permalink_structure) - except: - pass + # try to substitute any python datetime directive + add_to_url = article.date.strftime(article_permalink_structure) + # try to substitute any article metadata in rest file add_to_url = add_to_url % article.__dict__ add_to_url = [slugify(i) for i in add_to_url.split('/')] add_to_url = os.path.join(*add_to_url) diff --git a/pelican/settings.py b/pelican/settings.py index af6b1bd9..db2984e9 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -42,7 +42,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'DEFAULT_METADATA': (), 'FILES_TO_COPY': (), 'DEFAULT_STATUS': 'published', - 'ARTICLE_PERMALINK_STRUCTURE': '/%Y/%m/' + 'ARTICLE_PERMALINK_STRUCTURE': '' } def read_settings(filename): diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 9b074416..2796d561 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -9,9 +9,6 @@ PDF_GENERATOR = False REVERSE_CATEGORY_ORDER = True LOCALE = "" DEFAULT_PAGINATION = 2 -# Allows to construct an url like /2011/07/sample-post.html -# See documentation for more info. -ARTICLE_PERMALINK_STRUCTURE = '' FEED_RSS = 'feeds/all.rss.xml' CATEGORY_FEED_RSS = 'feeds/%s.rss.xml' From 73ba522508c78554a7e03dd9fa1a2ad7a11fddad Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 29 Jul 2011 23:35:57 +0300 Subject: [PATCH 037/219] Thanks Manuel :) --- THANKS | 1 + 1 file changed, 1 insertion(+) diff --git a/THANKS b/THANKS index 39f54e61..2a20100e 100644 --- a/THANKS +++ b/THANKS @@ -16,3 +16,4 @@ bugs or giving ideas. Thanks to them ! - Marcus Fredriksson - Günter Kolousek - Simon Liedtke +- Manuel F. Viera From cd277672d0b28f97cad24eea3325fd7c2d8dece8 Mon Sep 17 00:00:00 2001 From: Borgar Date: Tue, 2 Aug 2011 23:29:34 +0000 Subject: [PATCH 038/219] Add a setting to limit feeds to a max number of items. This adds FEED_MAX_ITEMS, which dictates the maximum number of items allowed in a feed. --- docs/settings.rst | 2 ++ pelican/settings.py | 1 + pelican/writers.py | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 2bdc891f..e4e2feaf 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -115,6 +115,8 @@ Setting name (default value) what does it do? `TAG_FEED` (``None``, ie no tag feed) relative url to output the tags atom feed. It should be defined using a "%s" matchin the tag name `TAG_FEED_RSS` (``None``, ie no RSS tag feed) relative url to output the tag RSS feed +`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feeds are + unrestricted by default. ================================================ ===================================================== .. [2] %s is the name of the category. diff --git a/pelican/settings.py b/pelican/settings.py index db2984e9..e8c4fd21 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -15,6 +15,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'FEED': 'feeds/all.atom.xml', 'CATEGORY_FEED': 'feeds/%s.atom.xml', 'TRANSLATION_FEED': 'feeds/all-%s.atom.xml', + 'FEED_MAX_ITEMS': '', 'SITENAME': 'A Pelican Blog', 'DISPLAY_PAGES_ON_MENU': True, 'PDF_GENERATOR': False, diff --git a/pelican/writers.py b/pelican/writers.py index 13ab9647..4685cff9 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -58,8 +58,11 @@ class Writer(object): feed = self._create_new_feed(feed_type, context) - for item in elements: - self._add_item_to_the_feed(feed, item) + max_items = len(elements) + if self.settings['FEED_MAX_ITEMS']: + max_items = min(self.settings['FEED_MAX_ITEMS'], max_items) + for i in xrange(max_items): + self._add_item_to_the_feed(feed, elements[i]) if filename: complete_path = os.path.join(self.output_path, filename) From 3b37b4263344e8ec87c1ca0727eed05a4094ad14 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 3 Aug 2011 17:04:39 +0200 Subject: [PATCH 039/219] some improvements for importers: - for dotclear: remove seconds in date, remove \\n - fix categories for wordpress (categories is a list a string) - refactor a bit the code between markdown and rst --- tools/importer.py | 63 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/tools/importer.py b/tools/importer.py index 612767b3..d7a5c98c 100755 --- a/tools/importer.py +++ b/tools/importer.py @@ -27,7 +27,9 @@ def wp2fields(xml): date = time.strftime("%Y-%m-%d %H:%M", date_object) author = item.fetch('dc:creator')[0].contents[0].title() - categories = [(cat['nicename'],cat.contents[0]) for cat in item.fetch(domain='category')] + + categories = [cat.contents[0] for cat in item.fetch(domain='category')] + # caturl = [cat['nicename'] for cat in item.fetch(domain='category')] tags = [tag.contents[0].title() for tag in item.fetch(domain='tag', nicename=None)] @@ -101,16 +103,22 @@ def dc2fields(file): # post_meta = fields[27] # redirect_url = fields[28][:-1] + # remove seconds + post_creadt = ':'.join(post_creadt.split(':')[0:2]) + author = "" - categories = "" + categories = [] + tags = [] + if cat_id: - categories = category_list[cat_id] - tags = "" + categories = [category_list[id].strip() for id in cat_id.split(',')] if post_format == "markdown": content = post_excerpt + post_content else: content = post_excerpt_xhtml + post_content_xhtml + content = content.replace('\\n', '') + post_format = "html" yield (post_title, content, post_url, post_creadt, author, categories, tags, post_format) @@ -135,7 +143,7 @@ def build_header(title, date, author, categories, tags): if date: header += ':date: %s\n' % date if categories: - header += ':category: %s\n' % categories + header += ':category: %s\n' % ', '.join(categories) if tags: header += ':tags: %s\n' % ', '.join(tags) header += '\n' @@ -147,7 +155,7 @@ def build_markdown_header(title, date, author, categories, tags): if date: header += 'Date: %s\n' % date if categories: - header += 'Category: %s\n' % categories + header += 'Category: %s\n' % ', '.join(categories) if tags: header += 'Tags: %s\n' % ', '.join(tags) header += '\n' @@ -156,40 +164,39 @@ def build_markdown_header(title, date, author, categories, tags): def fields2pelican(fields, output_path): for title, content, filename, date, author, categories, tags, markup in fields: if markup == "markdown": - md_filename = os.path.join(output_path, filename+'.md') + ext = '.md' header = build_markdown_header(title, date, author, categories, tags) - - # content.replace('\r\n', '\n') - - with open(md_filename, 'w', encoding='utf-8') as fp: - fp.write(header+content) - else: - filename = os.path.basename(filename) - html_filename = os.path.join(output_path, filename+'.html') + ext = '.rst' + header = build_header(title, date, author, categories, tags) - # if(len(categories) == 1): - # rst_filename = os.path.join(output_path, categories[0][0], filename+'.rst') - # if not os.path.isdir(os.path.join(output_path, categories[0][0])): - # os.mkdir(os.path.join(output_path, categories[0][0])) - # else: - rst_filename = os.path.join(output_path, filename+'.rst') + # TODO: add options to put files in directories by categories + # if(len(categories) == 1): + # out_filename = os.path.join(output_path, categories[0], filename+'.rst') + # if not os.path.isdir(os.path.join(output_path, categories[0])): + # os.mkdir(os.path.join(output_path, categories[0])) + # else: + + filename = os.path.basename(filename) + out_filename = os.path.join(output_path, filename+ext) + print out_filename + + if markup == "html": + html_filename = os.path.join(output_path, filename+'.html') with open(html_filename, 'w', encoding='utf-8') as fp: fp.write(content) - print rst_filename - os.system('pandoc --normalize --reference-links --from=html --to=rst -o %s %s' % (rst_filename, + os.system('pandoc --normalize --reference-links --from=html --to=rst -o %s %s' % (out_filename, html_filename)) os.remove(html_filename) - with open(rst_filename, 'r', encoding='utf-8') as fs: + with open(out_filename, 'r', encoding='utf-8') as fs: content = fs.read() - with open(rst_filename, 'w', encoding='utf-8') as fs: - # categories = [x[1] for x in categories] - header = build_header(title, date, author, categories, tags) - fs.write(header + content) + + with open(out_filename, 'w', encoding='utf-8') as fs: + fs.write(header + content) def main(input_type, input, output_path): From 69636a9da0368e625a2ba5aaa5dd1737e7741ef5 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 3 Aug 2011 19:11:54 +0200 Subject: [PATCH 040/219] importer: add an option to put files in directories with category names --- tools/importer.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tools/importer.py b/tools/importer.py index d7a5c98c..29409f17 100755 --- a/tools/importer.py +++ b/tools/importer.py @@ -161,7 +161,7 @@ def build_markdown_header(title, date, author, categories, tags): header += '\n' return header -def fields2pelican(fields, output_path): +def fields2pelican(fields, output_path, dircat=False): for title, content, filename, date, author, categories, tags, markup in fields: if markup == "markdown": ext = '.md' @@ -170,15 +170,17 @@ def fields2pelican(fields, output_path): ext = '.rst' header = build_header(title, date, author, categories, tags) - # TODO: add options to put files in directories by categories - # if(len(categories) == 1): - # out_filename = os.path.join(output_path, categories[0], filename+'.rst') - # if not os.path.isdir(os.path.join(output_path, categories[0])): - # os.mkdir(os.path.join(output_path, categories[0])) - # else: - filename = os.path.basename(filename) - out_filename = os.path.join(output_path, filename+ext) + + # option to put files in directories with categories names + if dircat and (len(categories) == 1): + catname = categories[0] + out_filename = os.path.join(output_path, catname, filename+'.rst') + if not os.path.isdir(os.path.join(output_path, catname)): + os.mkdir(os.path.join(output_path, catname)) + else: + out_filename = os.path.join(output_path, filename+ext) + print out_filename if markup == "html": @@ -187,7 +189,7 @@ def fields2pelican(fields, output_path): with open(html_filename, 'w', encoding='utf-8') as fp: fp.write(content) - os.system('pandoc --normalize --reference-links --from=html --to=rst -o %s %s' % (out_filename, + os.system('pandoc --normalize --reference-links --from=html --to=rst -o "%s" "%s"' % (out_filename, html_filename)) os.remove(html_filename) @@ -199,7 +201,7 @@ def fields2pelican(fields, output_path): fs.write(header + content) -def main(input_type, input, output_path): +def main(input_type, input, output_path, dircat=False): if input_type == 'wordpress': fields = wp2fields(input) elif input_type == 'dotclear': @@ -207,7 +209,7 @@ def main(input_type, input, output_path): elif input_type == 'feed': fields = feed2fields(input) - fields2pelican(fields, output_path) + fields2pelican(fields, output_path, dircat=dircat) if __name__ == '__main__': @@ -224,6 +226,8 @@ if __name__ == '__main__': help='feed to parse') parser.add_argument('-o', '--output', dest='output', default='output', help='Output path') + parser.add_argument('--dir-cat', action='store_true', dest='dircat', + help='Put files in directories with categories name') args = parser.parse_args() input_type = None @@ -236,4 +240,4 @@ if __name__ == '__main__': else: print "you must provide either --wpfile or --feed options" exit() - main(input_type, args.input, args.output) + main(input_type, args.input, args.output, dircat=args.dircat) From a9bb789b14550d28287aacecee3d6dc5afabc819 Mon Sep 17 00:00:00 2001 From: mviera Date: Wed, 3 Aug 2011 21:59:22 +0200 Subject: [PATCH 041/219] Adding markdown module as requirement. Even I install pelican in a new virtualenv and i tried to execute the pelican command it shows an error such as 'No module md'. Issue #145 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e2bd4c60..3f836293 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import sys VERSION = "2.7.2" # find a better way to do so. -requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils'] +requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'markdown'] if sys.version_info < (2,7): requires.append('argparse') From 319b553f69ea602f106c652cf2c472fcd28b9375 Mon Sep 17 00:00:00 2001 From: mviera Date: Wed, 3 Aug 2011 22:00:41 +0200 Subject: [PATCH 042/219] I developed the ARTICLE_PERMALINK_STRUCTURE option into the wrong method, because urls in feed are not being modified. Now, it's fixed. Issue #145. --- pelican/generators.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 8d6de666..f6ac35a0 100755 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -161,20 +161,6 @@ class ArticlesGenerator(Generator): # in writer, articles pass first article_template = self.get_template('article') for article in chain(self.translations, self.articles): - add_to_url = u'' - if 'ARTICLE_PERMALINK_STRUCTURE' in self.settings: - article_permalink_structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] - article_permalink_structure = article_permalink_structure.lstrip('/') - - # try to substitute any python datetime directive - add_to_url = article.date.strftime(article_permalink_structure) - # try to substitute any article metadata in rest file - add_to_url = add_to_url % article.__dict__ - add_to_url = [slugify(i) for i in add_to_url.split('/')] - add_to_url = os.path.join(*add_to_url) - - article.url = urlparse.urljoin(add_to_url, article.url) - article.save_as = urlparse.urljoin(add_to_url, article.save_as) write(article.save_as, article_template, self.context, article=article, category=article.category) @@ -247,6 +233,21 @@ class ArticlesGenerator(Generator): if not is_valid_content(article, f): continue + add_to_url = u'' + if 'ARTICLE_PERMALINK_STRUCTURE' in self.settings: + article_permalink_structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] + article_permalink_structure = article_permalink_structure.lstrip('/') + + # try to substitute any python datetime directive + add_to_url = article.date.strftime(article_permalink_structure) + # try to substitute any article metadata in rest file + add_to_url = add_to_url % article.__dict__ + add_to_url = [slugify(i) for i in add_to_url.split('/')] + add_to_url = os.path.join(*add_to_url) + + article.url = urlparse.urljoin(add_to_url, article.url) + article.save_as = urlparse.urljoin(add_to_url, article.save_as) + if article.status == "published": if hasattr(article, 'tags'): for tag in article.tags: From e8cac3bffff684c1b99f773c64bd69a0dd0b0d6a Mon Sep 17 00:00:00 2001 From: mviera Date: Wed, 3 Aug 2011 22:01:45 +0200 Subject: [PATCH 043/219] I change article.slug to article.url for always referencing the disqus thread to the correct url. Issue #145 --- pelican/themes/notmyidea/templates/comments.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/notmyidea/templates/comments.html b/pelican/themes/notmyidea/templates/comments.html index ea693afb..bb033c0f 100644 --- a/pelican/themes/notmyidea/templates/comments.html +++ b/pelican/themes/notmyidea/templates/comments.html @@ -1 +1 @@ -{% if DISQUS_SITENAME %}

    There are comments.

    {% endif %} +{% if DISQUS_SITENAME %}

    There are comments.

    {% endif %} From 7b7695509df31485ac799bfbe2f94b327b98d1cf Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 3 Aug 2011 22:06:10 +0200 Subject: [PATCH 044/219] importer - add documentation --- docs/importer.rst | 56 +++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 7 +++--- tools/importer.py | 8 +++---- 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 docs/importer.rst diff --git a/docs/importer.rst b/docs/importer.rst new file mode 100644 index 00000000..35cd095c --- /dev/null +++ b/docs/importer.rst @@ -0,0 +1,56 @@ +================================= + Import from other blog software +================================= + +Description +=========== + +``importer.py`` is a command line tool for converting articles from other +software to ReStructuredText. The supported formats are: + +- Wordpress XML export +- Dotclear export +- RSS/ATOM feed + +The conversion from HTML to ReStructuredText relies on `pandoc +`_. For Dotclear, if the source posts are +written with Markdown syntax, they will not be converted (as Pelican also +supports Markdown). + +Usage +""""" + +| importer.py [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT] +| [--dir-cat] +| input + +Optional arguments: +""""""""""""""""""" + + -h, --help show this help message and exit + --wpfile Wordpress XML export + --dotclear Dotclear export + --feed Feed to parse + -o OUTPUT, --output OUTPUT + Output path + --dir-cat Put files in directories with categories name + +Examples +======== + +for Wordpress:: + + $ python2 tools/importer.py --wpfile -o ~/output ~/posts.xml + +for Dotclear:: + + $ python2 tools/importer.py --dotclear -o ~/output ~/backup.txt + + +Tests +===== + +To test the module, one can use sample files: + +- for Wordpress: http://wpcandy.com/made/the-sample-post-collection +- for Dotclear: http://themes.dotaddict.org/files/public/downloads/lorem-backup.txt diff --git a/docs/index.rst b/docs/index.rst index cbc6bb19..9aba5609 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -42,8 +42,8 @@ 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! You can -also join the team at `#pelican on irc.freenode.org -`_ +also join the team at `#pelican on irc.freenode.org +`_ (or if you don't have any IRC client, using `the webchat `_) for quick feedback. @@ -55,11 +55,12 @@ A french version of the documentation is available at :doc:`fr/index`. .. toctree:: :maxdepth: 2 - + getting_started settings themes internals pelican-themes + importer faq contribute diff --git a/tools/importer.py b/tools/importer.py index 29409f17..44f02fbd 100755 --- a/tools/importer.py +++ b/tools/importer.py @@ -214,8 +214,8 @@ def main(input_type, input, output_path, dircat=False): if __name__ == '__main__': parser = argparse.ArgumentParser( - description="Transform even feed or XML files to rst files." - "Be sure to have pandoc installed") + description="Transform feed, Wordpress or Dotclear files to rst files." + "Be sure to have pandoc installed") parser.add_argument(dest='input', help='The input file to read') parser.add_argument('--wpfile', action='store_true', dest='wpfile', @@ -223,7 +223,7 @@ if __name__ == '__main__': parser.add_argument('--dotclear', action='store_true', dest='dotclear', help='Dotclear export') parser.add_argument('--feed', action='store_true', dest='feed', - help='feed to parse') + help='Feed to parse') parser.add_argument('-o', '--output', dest='output', default='output', help='Output path') parser.add_argument('--dir-cat', action='store_true', dest='dircat', @@ -238,6 +238,6 @@ if __name__ == '__main__': elif args.feed: input_type = 'feed' else: - print "you must provide either --wpfile or --feed options" + print "you must provide either --wpfile, --dotclear or --feed options" exit() main(input_type, args.input, args.output, dircat=args.dircat) From a32dc03616b33af9b1ec4f58b9e99a20b6be829c Mon Sep 17 00:00:00 2001 From: mviera Date: Wed, 3 Aug 2011 22:11:16 +0200 Subject: [PATCH 045/219] Keeping simple theme up-to-date too. Issue #145 --- pelican/themes/simple/templates/article.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 0bfed07a..30d31fb6 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %}
    -

    {{ article.title }}

    +

    {{ article.title }}

    {{ article.locale_date }} From ae4a3cb6da831b7fae33a6e0e7a860313f46ec0e Mon Sep 17 00:00:00 2001 From: mviera Date: Thu, 4 Aug 2011 00:31:58 +0200 Subject: [PATCH 046/219] I have been talking with alexis and he doesn't want 'markdown' to be installed by default if it's not needed. Issue #145 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3f836293..e2bd4c60 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import sys VERSION = "2.7.2" # find a better way to do so. -requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'markdown'] +requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils'] if sys.version_info < (2,7): requires.append('argparse') From a5f47cfae9766b8fabc9cd21b17c14b92a34e730 Mon Sep 17 00:00:00 2001 From: Borgar Date: Wed, 15 Jun 2011 23:48:54 +0000 Subject: [PATCH 047/219] Allow overriding reader extensions. This adds an extensions setting all readers in the style of [ext]_EXTENSIONS. So for the MarkdownReader, who's extension is "md", the setting read is MD_EXTENSIONS. The settings allow overriding the default options passed through the readers. In the case of Markdown the default values are ['codehilite','extra'], but user may change this through the setting: MD_EXTENSIONS = ['footnotes','abbr','codehilite'] --- docs/settings.rst | 4 ++++ pelican/generators.py | 2 +- pelican/readers.py | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index e4e2feaf..d6b05cab 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -46,6 +46,10 @@ Setting name (default value) what does it do? `MARKUP` (``('rst', 'md')``) A list of available markup languages you want to use. For the moment, only available values are `rst` and `md`. +`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the markdown processor + will use. Refer to the extensions chapter in the + Python-Markdown documentation for a complete list of + supported extensions. `OUTPUT_PATH` (``'output/'``) Where to output the generated files. `PATH` (``None``) path to look at for input files. `PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions diff --git a/pelican/generators.py b/pelican/generators.py index f6ac35a0..2862ec6d 100755 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -211,7 +211,7 @@ class ArticlesGenerator(Generator): files = self.get_files(self.path, exclude=['pages',]) all_articles = [] for f in files: - content, metadata = read_file(f) + content, metadata = read_file(f, settings=self.settings) # if no category is set, use the name of the path as a category if 'category' not in metadata.keys(): diff --git a/pelican/readers.py b/pelican/readers.py index 4c8acd2a..418753fa 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -32,7 +32,7 @@ def _process_metadata(name, value): class Reader(object): enabled = True - + extensions = None class _FieldBodyTranslator(HTMLTranslator): @@ -99,11 +99,12 @@ class RstReader(Reader): class MarkdownReader(Reader): enabled = bool(Markdown) extension = "md" + extensions = ['codehilite', 'extra'] def read(self, filename): """Parse content and metadata of markdown files""" text = open(filename) - md = Markdown(extensions = ['meta', 'codehilite', 'extra']) + md = Markdown(extensions=set(self.extensions+['meta'])) content = md.convert(text) metadata = {} @@ -133,13 +134,16 @@ class HtmlReader(Reader): _EXTENSIONS = dict((cls.extension, cls) for cls in Reader.__subclasses__()) -def read_file(filename, fmt=None): +def read_file(filename, fmt=None, settings=None): """Return a reader object using the given format.""" if not fmt: fmt = filename.split('.')[-1] if fmt not in _EXTENSIONS.keys(): raise TypeError('Pelican does not know how to parse %s' % filename) reader = _EXTENSIONS[fmt]() + settings_key = '%s_EXTENSIONS' % fmt.upper() + if settings and settings_key in settings: + reader.extensions = settings[settings_key] if not reader.enabled: raise ValueError("Missing dependencies for %s" % fmt) return reader.read(filename) From 7f1cad26bac87ffd29ec57e84776df8b3d036d49 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Thu, 11 Aug 2011 11:21:18 +0200 Subject: [PATCH 048/219] typos --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9aba5609..41c7b269 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ Pelican ####### -Pelican is a simple weblog generator, writen in python. +Pelican is a simple weblog generator, written in python. * Write your weblog entries directly with your editor of choice (vim!) and directly in restructured text, or markdown. @@ -18,7 +18,7 @@ Pelican currently supports: * 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. (optional) -* easy theming (themes are done using `jinja2 `_) +* easy theming (themes are done using `jinja2 `_) * PDF generation of the articles/pages (optional). * publication of articles in various languages * RSS/Atom feeds From e9f1bb98532417765af818d485051428bb67b000 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Thu, 11 Aug 2011 11:24:33 +0200 Subject: [PATCH 049/219] add ideas --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 9829c9c5..ca29204d 100644 --- a/TODO +++ b/TODO @@ -5,3 +5,5 @@ * Recompile only the changed files, not all. * Add a way to make the coffee (or not) * Add a sitemap generator. +* read templates from the templates folder per default +* add support of github via ghg import From c73ae0217033f96c4493d8258bfa6188940c7426 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Thu, 11 Aug 2011 22:42:20 +0200 Subject: [PATCH 050/219] pep8ize --- pelican/readers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/readers.py b/pelican/readers.py index 418753fa..f62d35a9 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -104,7 +104,7 @@ class MarkdownReader(Reader): def read(self, filename): """Parse content and metadata of markdown files""" text = open(filename) - md = Markdown(extensions=set(self.extensions+['meta'])) + md = Markdown(extensions=set(self.extensions + ['meta'])) content = md.convert(text) metadata = {} From 1edbe044a28cd31e5ccde0f67569f5c5dcbfe210 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Thu, 11 Aug 2011 23:34:53 +0200 Subject: [PATCH 051/219] Include the "pelican-quickstart" script. This ease the creation of a new blog. I modified slightly the code wrote by @Skami18 here: https://gist.github.com/1025236/dfa695e67482477907c79ae709ab827b20b18b04 This commit also renames the import script to "pelican-import". Fixes #129. --- docs/getting_started.rst | 19 +- docs/importer.rst | 15 +- docs/settings.rst | 2 +- pelican/__init__.py | 4 +- setup.py | 2 +- tools/{importer.py => pelican-import} | 0 tools/pelican-quickstart | 269 ++++++++++++++++++++++++++ 7 files changed, 290 insertions(+), 21 deletions(-) rename tools/{importer.py => pelican-import} (100%) create mode 100755 tools/pelican-quickstart diff --git a/docs/getting_started.rst b/docs/getting_started.rst index a59bc8cf..f10afa68 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -83,6 +83,13 @@ the options you can use:: $ pelican --help +Kickstart a blog +---------------- + +You also can use the `pelican-quickstart` script to start a new blog in +seconds, by just answering few questions. Just run `pelican-quickstart` and +you're done! (Added in pelican 3) + Pages ----- @@ -95,16 +102,8 @@ the menu. Importing an existing blog -------------------------- -It is possible to import wordpress themes and RSS themes using a script which -is living in `tools`: importer. - -You can call it this way for a wordpress import:: - - $ python importer.py --wpfile /your/wordpress/export -o output_dir - -And like this for an import from an RSS feed:: - - $ python importer.py --feed http://your/rss/feed -o output_dir +It is possible to import your blog from dotclear, wordpress and an RSS feed using +a simple script. See :ref:`import`. Translations ------------ diff --git a/docs/importer.rst b/docs/importer.rst index 35cd095c..6e5734ae 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -1,3 +1,5 @@ +.. _import: + ================================= Import from other blog software ================================= @@ -5,7 +7,7 @@ Description =========== -``importer.py`` is a command line tool for converting articles from other +``pelican-import`` is a command line tool for converting articles from other software to ReStructuredText. The supported formats are: - Wordpress XML export @@ -20,9 +22,9 @@ supports Markdown). Usage """"" -| importer.py [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT] -| [--dir-cat] -| input +| pelican-import [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT] +| [--dir-cat] +| input Optional arguments: """"""""""""""""""" @@ -40,12 +42,11 @@ Examples for Wordpress:: - $ python2 tools/importer.py --wpfile -o ~/output ~/posts.xml + $ pelican-import --wpfile -o ~/output ~/posts.xml for Dotclear:: - $ python2 tools/importer.py --dotclear -o ~/output ~/backup.txt - + $ pelican-import --dotclear -o ~/output ~/backup.txt Tests ===== diff --git a/docs/settings.rst b/docs/settings.rst index d6b05cab..7cfb9442 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -200,7 +200,7 @@ Setting name (default value) what does it do? Theming ======= -Theming is addressed in a dedicated section (see :ref:`theming-pelican`). +Theming is addressed in a dedicated section (see :ref:`theming-pelican`). However, here are the settings that are related to theming. ================================================ ===================================================== diff --git a/pelican/__init__.py b/pelican/__init__.py index 4b743d52..b68d6aa9 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -9,7 +9,7 @@ from pelican.utils import clean_output_dir, files_changed from pelican.writers import Writer from pelican import log -VERSION = "2.7.2" +__version__ = "2.7.2" class Pelican(object): @@ -112,7 +112,7 @@ def main(): help='Show only critical errors') parser.add_argument('-D', '--debug', action='store_const', const=log.DEBUG, dest='verbosity', help='Show all message, including debug messages') - parser.add_argument('--version', action='version', version=VERSION, + parser.add_argument('--version', action='version', version=__version__, help='Print the pelican version and exit') parser.add_argument('-r', '--autoreload', dest='autoreload', action='store_true', help="Relaunch pelican each time a modification occurs on the content" diff --git a/setup.py b/setup.py index e2bd4c60..4574fdd6 100755 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( packages = ['pelican'], include_package_data = True, install_requires = requires, - scripts = ['bin/pelican', 'tools/pelican-themes'], + scripts = ['bin/pelican', 'tools/pelican-themes', 'tools/pelican-import', 'tools/pelican-quickstart'], classifiers = ['Development Status :: 5 - Production/Stable', 'Environment :: Console', 'License :: OSI Approved :: GNU Affero General Public License v3', diff --git a/tools/importer.py b/tools/pelican-import similarity index 100% rename from tools/importer.py rename to tools/pelican-import diff --git a/tools/pelican-quickstart b/tools/pelican-quickstart new file mode 100755 index 00000000..fe7bb31a --- /dev/null +++ b/tools/pelican-quickstart @@ -0,0 +1,269 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # + +import os, sys, argparse, string +from pelican import __version__ + +TEMPLATES={ + 'Makefile' : ''' +PELICAN=$pelican +PELICANOPTS=$pelicanopts + +BASEDIR=$basedir +INPUTDIR=$$(BASEDIR)/src +OUTPUTDIR=$$(BASEDIR)/output +CONFFILE=$$(BASEDIR)/pelican.conf.py + +FTP_HOST=$ftp_host +FTP_USER=$ftp_user +FTP_TARGET_DIR=$ftp_target_dir + +SSH_HOST=$ssh_host +SSH_USER=$ssh_user +SSH_TARGET_DIR=$ssh_target_dir + +DROPBOX_DIR=$dropbox_dir + +help: +\t@echo 'Makefile for a pelican Web site ' +\t@echo ' ' +\t@echo 'Usage: ' +\t@echo ' make html (re)generate the web site ' +\t@echo ' make clean remove the generated files ' +\t@echo ' ftp_upload upload the web site using FTP ' +\t@echo ' ssh_upload upload the web site using SSH ' +\t@echo ' dropbox_upload upload the web site using Dropbox ' +\t@echo ' ' + + +html: $$(OUTPUTDIR)/index.html +\t@echo 'Done' + +$$(OUTPUTDIR)/%.html: +\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) + +clean: +\trm -r $$(OUTPUTDIR)/* + +dropbox_upload: $$(OUTPUTDIR)/index.html +\tcp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR) + +ssh_upload: $$(OUTPUTDIR)/index.html +\tscp -rv $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST)/$$(SSH_TARGET_DIR) + +ftp_upload: $$(OUTPUTDIR)/index.html +\tlftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUT_DIR)/* $$(FTP_TARGET_DIR) ; quit" + +.PHONY: html help clean ftp_upload ssh_upload dropbox_upload + ''', + + 'pelican.conf.py': '''#!/usr/bin/env python +# -*- coding: utf-8 -*- # + +AUTHOR = u"$author" +SITENAME = u"$sitename" +SITEURL = '/' + +DEFAULT_LANG='$lang' + +# Blogroll +LINKS = ( + ('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'), + ('Python.org', 'http://python.org'), + ('Jinja2', 'http://jinja.pocoo.org'), + ('You can modify those links in your config file', '#') + ) + +# Social widget +SOCIAL = ( + ('You can add links in your config file', '#'), + ) + +WITH_PAGINATION = $with_pagination +DEFAULT_PAGINATION = $default_pagination + + + ''' +} + +CONF = { + 'pelican' : 'pelican', + 'pelicanopts' : None, + 'basedir': '.', + 'ftp_host': 'localhost', + 'ftp_user': 'anonymous', + 'ftp_target_dir': '/', + 'ssh_host': 'locahost', + 'ssh_user': 'root', + 'ssh_target_dir': '/var/www', + 'dropbox_dir' : '~/Dropbox/Public/', + 'with_pagination' : True, + 'default_pagination' : 7, + 'lang': 'en' +} + + +class _dict(dict): + def __init__(self, *args, **kwargs): + dict.__init__(self, *args, **kwargs) + + def __getitem__(self, i): + return dict.get(self,i,None) + + def has_key(k): + return True + + +def ask(question, answer=str, default=None, l=None): + if answer == str: + r = '' + while True: + if default: + r = raw_input('> {0} [{1}] '.format(question, default)) + else: + r = raw_input('> {0} '.format(question, default)) + + r = r.strip() + + if len(r) <= 0: + if default: + r = default + break + else: + print('You must enter something') + else: + if l and len(r) != l: + print('You must enter a {0} letters long string'.format(l)) + else: + break + + return r + + elif answer == bool: + r = None + while True: + if default is True: + r = raw_input('> {0} (Y/n) '.format(question)) + elif default is False: + r = raw_input('> {0} (y/N) '.format(question)) + else: + r = raw_input('> {0} (y/n) '.format(question)) + + r = r.strip().lower() + + if r in ('y', 'yes'): + r = True + break + elif r in ('n', 'no'): + r = False + break + elif not r: + r = default + break + else: + print("You must answer `yes' or `no'") + return r + elif answer == int: + r = None + while True: + if default: + r = raw_input('> {0} [{1}] '.format(question, default)) + else: + r = raw_input('> {0} '.format(question)) + + r = r.strip() + + if not r: + r = default + break + + try: + r = int(r) + break + except: + print('You must enter an integer') + return r + else: + raise NotImplemented('Arguent `answer` must be str, bool or integer') + + +def main(): + parser = argparse.ArgumentParser(description="A kickstarter for pelican") + parser.add_argument('-p', '--path', default=".", + help="The path to generate the blog into") + parser.add_argument('-t', '--title', default=None, metavar="title", + help='Set the title of the website') + parser.add_argument('-a', '--author', default=None, metavar="author", + help='Set the author name of the website') + parser.add_argument('-l', '--lang', default=None, metavar="lang", + help='Set the default lang of the website') + + args = parser.parse_args() + + + print('''Welcome to pelican-quickstart v{v}. + +This script will help you creating a new Pelican based website. + +Please answer the following questions so this script can generate the files needed by Pelican. + + '''.format(v=__version__)) + + CONF['basedir'] = os.path.abspath(ask('Where do you want to create your new Web site ?', answer=str, default=args.path)) + CONF['sitename'] = ask('How will you call your Web site ?', answer=str, default=args.title) + CONF['author'] = ask('Who will be the author of this Web site ?', answer=str, default=args.author) + CONF['lang'] = ask('What will be the default language of this Web site ?', str, args.lang or CONF['lang'], 2) + + CONF['with_pagination'] = ask('Do you want to enable article pagination ?', bool, CONF['with_pagination']) + + if CONF['with_pagination']: + CONF['default_pagination'] = ask('So how many articles per page do you want ?', int, CONF['default_pagination']) + + mkfile = ask('Do you want to generate a Makefile to easily manage your website ?', bool, True) + + if mkfile: + if ask('Do you want to upload your website using FTP ?', answer=bool, default=False): + CONF['ftp_host'] = ask('What is the hostname of your FTP server ?', str, CONF['ftp_host']) + CONF['ftp_user'] = ask('What is your username on this server ?', str, CONF['ftp_user']) + CONF['ftp_traget_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ftp_target_dir']) + + if ask('Do you want to upload your website using SSH ?', answer=bool, default=False): + CONF['ssh_host'] = ask('What is the hostname of your SSH server ?', str, CONF['ssh_host']) + CONF['ssh_user'] = ask('What is your username on this server ?', str, CONF['ssh_user']) + CONF['ssh_traget_dir'] = ask('Where do you want to put your website on this server ?', str, CONF['ssh_target_dir']) + + if ask('Do you want to upload your website using Dropbox ?', answer=bool, default=False): + CONF['dropbox_dir'] = ask('Where is your Dropbox directory ?', str, CONF['dropbox_dir']) + + try: + os.makedirs(os.path.join(CONF['basedir'], 'src')) + except OSError, e: + print('Error: {0}'.format(e)) + + try: + os.makedirs(os.path.join(CONF['basedir'], 'output')) + except OSError, e: + print('Error: {0}'.format(e)) + + conf = string.Template(TEMPLATES['pelican.conf.py']) + try: + with open(os.path.join(CONF['basedir'], 'pelican.conf.py'), 'w') as fd: + fd.write(conf.safe_substitute(CONF)) + fd.close() + except OSError, e: + print('Error: {0}'.format(e)) + + if mkfile: + Makefile = string.Template(TEMPLATES['Makefile']) + + try: + with open(os.path.join(CONF['basedir'], 'Makefile'), 'w') as fd: + fd.write(Makefile.safe_substitute(CONF)) + fd.close() + except OSError, e: + print('Error: {0}'.format(e)) + + print('Done. Your new project is available at %s' % CONF['basedir']) + +if __name__ == '__main__': + main() From 4a6bf81b2ef75116d1bef0c62e645c71ead5c946 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Thu, 11 Aug 2011 23:50:04 +0200 Subject: [PATCH 052/219] remove skribit support. fixes #154 --- docs/fr/themes.rst | 16 ----- docs/index.rst | 2 +- docs/settings.rst | 58 ------------------- docs/themes.rst | 16 ----- pelican/themes/notmyidea/templates/base.html | 3 - .../templates/skribit_tab_script.html | 14 ----- .../templates/skribit_widget_script.html | 8 --- 7 files changed, 1 insertion(+), 116 deletions(-) delete mode 100644 pelican/themes/notmyidea/templates/skribit_tab_script.html delete mode 100644 pelican/themes/notmyidea/templates/skribit_widget_script.html diff --git a/docs/fr/themes.rst b/docs/fr/themes.rst index 53e6374e..c6f45300 100644 --- a/docs/fr/themes.rst +++ b/docs/fr/themes.rst @@ -169,19 +169,3 @@ dates_pages La page actuelle d'articles, ordonnée par date croissante. page_name 'tag/`nom du tag`'. =================== =================================================== - -Inclure le script skribit -========================= - -Pour pouvoir supporter les scripts skribit dans vos thèmes, vous devez -faire ceci : - - * Copier `skribit_tab_script.html` et `skribit_widget_script.html` dans - votre dossier de templates. - * Ajouter {% include 'skribit_tab_script' %} dans votre pour - ajouter le support de l'onglet de suggestions. - * Ajouter {% include 'skribit_widget_script' %} là où vous le souhaitez - pour ajouter le widget dans la sidebar. - -Vous pouvez regarder le thème par défault (notmyidea) pour voir un -exemple de thème fonctionnel. diff --git a/docs/index.rst b/docs/index.rst index 41c7b269..1e0a56f8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,7 @@ Pelican currently supports: * publication of articles in various languages * RSS/Atom feeds * wordpress/dotclear or RSS imports -* integration with various tools: twitter/google analytics/skribit (optional) +* integration with various tools: twitter/google analytics (optional) Why the name "Pelican" ? ======================== diff --git a/docs/settings.rst b/docs/settings.rst index 7cfb9442..d08e24da 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -262,64 +262,6 @@ adding that in your configuration:: CSS_FILE = "wide.css" -Skribit -------- - -Skribit has two ways to display suggestions : as a sidebar widget or as a -suggestions tab. You can choose one of the display by setting the SKRIBIT_TYPE -in your config. - - * SKRIBIT_WIDGET_ID : the identifier of your blog. - -All the customizations are done in the skribit web interface. - -To retrieve your identifier from the code snippet, you can use this python code:: - - import re - regex = re.compile('.*http://assets.skribit.com/javascripts/SkribitWidget.\ - js\?renderTo=writeSkribitHere&blog=(.*)&.*') - snippet = '''SNIPPET CONTENT''' - snippet = snippet.replace('\n', '') - identifier = regex.match(snippet).groups()[0] - -Suggestion tab --------------- - -The setting for suggestion tab's customizations are : - - * SKRIBIT_TAB_COLOR - * SKRIBIT_TAB_DISTANCE_HORIZ - * SKRIBIT_TAB_DISTANCE_VERT - * SKRIBIT_TAB_PLACEMENT - -The identifier is : - - * SKRIBIT_TAB_SITENAME : the identifier of your blog - -To retrieve your sitename from the code snippet, you can use this python code:: - - import re - regex = re.compile('.*http://skribit.com/lightbox/(.*)\',.*') - snippet = '''SNIPPET CONTENT''' - snippet = snippet.replace('\n', '') - identifier = regex.match(snippet).groups()[0] - -Skribit settings ----------------- - -================================================ ===================================================== -Setting name (default value) what does it do? -================================================ ===================================================== -`SKRIBIT_TYPE` The type of skribit widget (TAB or WIDGET). -`SKRIBIT_TAB_COLOR` Tab color (#XXXXXX, default #333333). -`SKRIBIT_TAB_HORIZ` Tab Distance from Left (% or distance, default Null). -`SKRIBIT_TAB_VERT` Tab Distance from Top (% or distance, default 20%). -`SKRIBIT_TAB_PLACEMENT` Tab placement (Top, Bottom, Left or Right, - default LEFT). -`SKRIBIT_TAB_SITENAME` Tab identifier (See Skribit part below). -`SKRIBIT_WIDGET_ID` Widget identifier (See Skribit part below). -================================================ ===================================================== - .. _pelican-themes: :doc:`pelican-themes` Example settings diff --git a/docs/themes.rst b/docs/themes.rst index 2ebfcf49..a20fd099 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -190,22 +190,6 @@ dates_page The current page of articles, ordered by date, page_name 'tag/`tag_name`'. Useful for pagination links. =================== =================================================== -Include skribit script -====================== - -In order to support skribit scripts in your themes, you must perform these -actions: - - * Copy `skribit_tab_script.html` and `skribit_widget_script.html` in your - templates directory. - * Add {% include 'skribit_tab_script.html' %} in your part in order to - support suggestions tab. - * Add {% include 'skribit_widget_script.html' %} where you want in order to - support sidebar widget. - -You can take a look at notmyidea default theme for working example. - - Inheritance =========== diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index ad0f636f..a34b5d5a 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -9,8 +9,6 @@ {% endif %} - {% include 'skribit_tab_script.html' %} - @@ -54,7 +52,6 @@
    {% endif %} - {% include 'skribit_widget_script.html' %} {% if SOCIAL %}
    diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index fd8545df..ad2a3b2e 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -11,7 +11,7 @@

    {{ article.title }}

    {{ article.summary }}
  2. From a39787c1a2c32dc936dba9ace73a0fcef0b73098 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Fri, 23 Dec 2011 22:01:32 +0000 Subject: [PATCH 131/219] Add settings to change the URL's and SAVE_AS paths Example usage: * ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/' * ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html' This removes CLEAN_URLS and ARTICLE_PERMALINK_STRUCTURE because these new settings can produce the same result. --- docs/settings.rst | 54 +++++++++++++++++++++++-------------- pelican/contents.py | 62 +++++++++++++++++++++++++++---------------- pelican/generators.py | 17 +----------- pelican/settings.py | 9 ++++++- 4 files changed, 82 insertions(+), 60 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 2d40d4ec..b93c8c11 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -24,12 +24,7 @@ Basic settings ================================================ ===================================================== Setting name (default value) What does it do? ================================================ ===================================================== -`ARTICLE_PERMALINK_STRUCTURE` (``''``) Empty by default. Enables some customization of URL - structure (see below for more detail). `AUTHOR` Default author (put your name) -`CLEAN_URLS` (``False``) If set to `True`, the URLs will not be suffixed by - `.html`, so you will have to setup URL rewriting on - your web server. `DATE_FORMATS` (``{}``) If you do manage multiple languages, you can set the date formatting here. See "Date format and locales" section below for details. @@ -79,16 +74,14 @@ Setting name (default value) What does it do? .. [#] Default is the system locale. -Article permalink structure ---------------------------- +URL Settings +------------ -This setting allows you to output your articles sorted by date, provided that -you specify a format as specified below. This format follows the Python -``datetime`` directives: - -* %Y: Year with century as a decimal number. -* %m: Month as a decimal number [01,12]. -* %d: Day of the month as a decimal number [01,31]. +You can customize the URL's and locations where files will be saved. The URL's and +SAVE_AS variables use python's format strings. These variables allow you to place +your articles in a location such as '{slug}/index.html' and link to then as +'{slug}' for clean urls. These settings give you the flexibility to place your +articles and pages anywhere you want. Note: If you specify a datetime directive, it will be substituted using the input files' date metadata attribute. If the date is not specified for a @@ -99,15 +92,36 @@ information. Also, you can use other file metadata attributes as well: -* category: '%(category)s' -* author: '%(author)s' -* tags: '%(tags)s' -* date: '%(date)s' +* slug +* date +* lang +* author +* category Example usage: -* '/%Y/%m/' will render something like '/2011/07/sample-post.html'. -* '/%Y/%(category)s/' will render something like '/2011/life/sample-post.html'. +* ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/' +* ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html' + +This would save your articles in something like '/posts/2011/Aug/07/sample-post/index.html', +and the URL to this would be '/posts/2011/Aug/07/sample-post/'. + +================================================ ===================================================== +Setting name (default value) what does it do? +================================================ ===================================================== +`ARTICLE_URL` ('{slug}.html') The URL to refer to an ARTICLE. +`ARTICLE_SAVE_AS` ('{slug}.html') The place where we will save an article. +`ARTICLE_LANG_URL` ('{slug}-{lang}.html') The URL to refer to an ARTICLE which doesn't use the + default language. +`ARTICLE_LANG_SAVE_AS` ('{slug}-{lang}.html' The place where we will save an article which + doesn't use the default language. +`PAGE_URL` ('pages/{slug}.html') The URL we will use to link to a page. +`PAGE_SAVE_AS` ('pages/{slug}.html') The location we will save the page. +`PAGE_LANG_URL` ('pages/{slug}-{lang}.html') The URL we will use to link to a page which doesn't + use the default language. +`PAGE_LANG_SAVE_AS` ('pages/{slug}-{lang}.html') The location we will save the page which doesn't + use the default language. +================================================ ===================================================== Timezone -------- diff --git a/pelican/contents.py b/pelican/contents.py index 8cd36186..bc42d41e 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -24,6 +24,7 @@ class Page(object): if not settings: settings = _DEFAULT_CONFIG + self.settings = settings self._content = content self.translations = [] @@ -55,29 +56,6 @@ class Page(object): if not hasattr(self, 'slug') and hasattr(self, 'title'): self.slug = slugify(self.title) - # create save_as from the slug (+lang) - if not hasattr(self, 'save_as') and hasattr(self, 'slug'): - if self.in_default_lang: - if settings.get('CLEAN_URLS', False): - self.save_as = '%s/index.html' % self.slug - else: - self.save_as = '%s.html' % self.slug - - clean_url = '%s/' % self.slug - else: - if settings.get('CLEAN_URLS', False): - self.save_as = '%s-%s/index.html' % (self.slug, self.lang) - else: - self.save_as = '%s-%s.html' % (self.slug, self.lang) - - clean_url = '%s-%s/' % (self.slug, self.lang) - - # change the save_as regarding the settings - if settings.get('CLEAN_URLS', False): - self.url = clean_url - elif hasattr(self, 'save_as'): - self.url = self.save_as - if filename: self.filename = filename @@ -115,6 +93,30 @@ class Page(object): if not hasattr(self, prop): raise NameError(prop) + @property + def url_format(self): + return { + 'slug': getattr(self, 'slug', ''), + 'lang': getattr(self, 'lang', 'en'), + 'date': getattr(self, 'date', datetime.now()), + 'author': self.author, + 'category': getattr(self, 'category', 'misc'), + } + + @property + def url(self): + if self.in_default_lang: + return self.settings.get('PAGE_URL', 'pages/{slug}.html').format(**self.url_format) + + return self.settings.get('PAGE_LANG_URL', 'pages/{slug}-{lang}.html').format(**self.url_format) + + @property + def save_as(self): + if self.in_default_lang: + return self.settings.get('PAGE_SAVE_AS', 'pages/{slug}.html').format(**self.url_format) + + return self.settings.get('PAGE_LANG_SAVE_AS', 'pages/{slug}-{lang}.html').format(**self.url_format) + @property def content(self): if hasattr(self, "_get_content"): @@ -138,6 +140,20 @@ class Page(object): class Article(Page): mandatory_properties = ('title', 'date', 'category') + @property + def url(self): + if self.in_default_lang: + return self.settings.get('ARTICLE_URL', '{slug}.html').format(**self.url_format) + + return self.settings.get('ARTICLE_LANG_URL', '{slug}-{lang}.html').format(**self.url_format) + + @property + def save_as(self): + if self.in_default_lang: + return self.settings.get('ARTICLE_SAVE_AS', '{slug}.html').format(**self.url_format) + + return self.settings.get('ARTICLE_LANG_SAVE_AS', '{slug}-{lang}.html').format(**self.url_format) + class Quote(Page): base_properties = ('author', 'date') diff --git a/pelican/generators.py b/pelican/generators.py index d9d584a4..7ad3d276 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -239,21 +239,6 @@ class ArticlesGenerator(Generator): if not is_valid_content(article, f): continue - add_to_url = u'' - if 'ARTICLE_PERMALINK_STRUCTURE' in self.settings: - article_permalink_structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] - article_permalink_structure = article_permalink_structure.lstrip('/').replace('%(', "%%(") - - # try to substitute any python datetime directive - add_to_url = article.date.strftime(article_permalink_structure) - # try to substitute any article metadata in rest file - add_to_url = add_to_url % article.__dict__ - add_to_url = [slugify(i) for i in add_to_url.split('/')] - add_to_url = os.path.join(*add_to_url) - - article.url = urlparse.urljoin(add_to_url, article.url) - article.save_as = urlparse.urljoin(add_to_url, article.save_as) - if article.status == "published": if hasattr(article, 'tags'): for tag in article.tags: @@ -348,7 +333,7 @@ class PagesGenerator(Generator): def generate_output(self, writer): for page in chain(self.translations, self.pages): - writer.write_file('pages/%s' % page.save_as, self.get_template('page'), + writer.write_file(page.save_as, self.get_template('page'), self.context, page=page, relative_urls = self.settings.get('RELATIVE_URLS')) diff --git a/pelican/settings.py b/pelican/settings.py index cf6b23e5..ec6ec483 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -26,7 +26,14 @@ _DEFAULT_CONFIG = {'PATH': None, 'REVERSE_ARCHIVE_ORDER': False, 'REVERSE_CATEGORY_ORDER': False, 'DELETE_OUTPUT_DIRECTORY': False, - 'CLEAN_URLS': False, # use /blah/ instead /blah.html in urls + 'ARTICLE_URL': '{slug}.html', + 'ARTICLE_SAVE_AS': '{slug}.html', + 'ARTICLE_LANG_URL': '{slug}-{lang}.html', + 'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html', + 'PAGE_URL': 'pages/{slug}.html', + 'PAGE_SAVE_AS': 'pages/{slug}.html', + 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', + 'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html', 'RELATIVE_URLS': True, 'DEFAULT_LANG': 'en', 'TAG_CLOUD_STEPS': 4, From 44cf2ad400629ea9d85dd598d837a35adcfaed85 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Fri, 23 Dec 2011 23:43:32 +0000 Subject: [PATCH 132/219] Support configurable URL's & SAVE_AS path for Author, Category and Tag --- docs/settings.rst | 6 +++++ pelican/contents.py | 29 +++++++++++++++------ pelican/generators.py | 8 +++--- pelican/readers.py | 60 +++++++++++++++++++++---------------------- 4 files changed, 60 insertions(+), 43 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index b93c8c11..69e2adc8 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -121,6 +121,12 @@ Setting name (default value) what does it do? use the default language. `PAGE_LANG_SAVE_AS` ('pages/{slug}-{lang}.html') The location we will save the page which doesn't use the default language. +`AUTHOR_URL` ('author/{name}.html') The URL to use for an author. +`AUTHOR_SAVE_AS` ('author/{name}.html') The location to save an author. +`CATEGORY_URL` ('category/{name}.html') The URL to use for a category. +`CATEGORY_SAVE_AS` ('category/{name}.html') The location to save a category. +`TAG_URL` ('tag/{name}.html') The URL to use for a tag. +`TAG_SAVE_AS` ('tag/{name}.html') The location to save the tag page. ================================================ ===================================================== Timezone diff --git a/pelican/contents.py b/pelican/contents.py index bc42d41e..316a0e56 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -38,9 +38,9 @@ class Page(object): # default author to the one in settings if not defined if not hasattr(self, 'author'): if 'AUTHOR' in settings: - self.author = Author(settings['AUTHOR']) + self.author = Author(settings['AUTHOR'], settings) else: - self.author = Author(getenv('USER', 'John Doe')) + self.author = Author(getenv('USER', 'John Doe'), settings) warning(u"Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author)) # manage languages @@ -159,8 +159,9 @@ class Quote(Page): base_properties = ('author', 'date') class URLWrapper(object): - def __init__(self, name): + def __init__(self, name, settings): self.name = unicode(name) + self.settings = settings def __hash__(self): return hash(self.name) @@ -181,20 +182,32 @@ class URLWrapper(object): class Category(URLWrapper): @property def url(self): - return 'category/%s.html' % self + return self.settings.get('CATEGORY_URL', 'category/{name}.html').format(name=self.name) + + @property + def save_as(self): + return self.settings.get('CATEGORY_SAVE_AS', 'category/{name}.html').format(name=self.name) class Tag(URLWrapper): - def __init__(self, name): - self.name = unicode.strip(name) + def __init__(self, name, *args, **kwargs): + super(Tag, self).__init__(unicode.strip(name), *args, **kwargs) @property def url(self): - return 'tag/%s.html' % self + return self.settings.get('TAG_URL', 'tag/{name}.html').format(name=self.name) + + @property + def save_as(self): + return self.settings.get('TAG_SAVE_AS', 'tag/{name}.html').format(name=self.name) class Author(URLWrapper): @property def url(self): - return 'author/%s.html' % self + return self.settings.get('AUTHOR_URL', 'author/{name}.html').format(name=self.name) + + @property + def save_as(self): + return self.settings.get('AUTHOR_SAVE_AS', 'author/{name}.html').format(name=self.name) def is_valid_content(content, f): try: diff --git a/pelican/generators.py b/pelican/generators.py index 7ad3d276..38bfb5b8 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -179,7 +179,7 @@ class ArticlesGenerator(Generator): for tag, articles in self.tags.items(): articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in self.dates if article in articles] - write(tag.url, tag_template, self.context, tag=tag, + write(tag.save_as, tag_template, self.context, tag=tag, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='tag/%s' % tag) @@ -187,7 +187,7 @@ class ArticlesGenerator(Generator): category_template = self.get_template('category') for cat, articles in self.categories: dates = [article for article in self.dates if article in articles] - write(cat.url, category_template, self.context, + write(cat.save_as, category_template, self.context, category=cat, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='category/%s' % cat) @@ -195,7 +195,7 @@ class ArticlesGenerator(Generator): author_template = self.get_template('author') for aut, articles in self.authors: dates = [article for article in self.dates if article in articles] - write(aut.url, author_template, self.context, + write(aut.save_as, author_template, self.context, author=aut, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='author/%s' % aut) @@ -228,7 +228,7 @@ class ArticlesGenerator(Generator): category = os.path.basename(os.path.dirname(f)).decode('utf-8') if category != '': - metadata['category'] = Category(category) + metadata['category'] = Category(category, self.settings) if 'date' not in metadata.keys()\ and self.settings['FALLBACK_ON_FS_DATE']: diff --git a/pelican/readers.py b/pelican/readers.py index e760b662..814f81d2 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -15,28 +15,30 @@ except ImportError: Markdown = False import re -from pelican.contents import Category, Tag, Author +from pelican.contents import Category, Tag, Author, URLWrapper from pelican.utils import get_date, open _METADATA_PROCESSORS = { - 'tags': lambda x: map(Tag, unicode(x).split(',')), - 'date': lambda x: get_date(x), - 'status': unicode.strip, + 'tags': lambda x, y: [Tag(tag, y) for tag in unicode(x).split(',')], + 'date': lambda x, y: get_date(x), + 'status': lambda x,y: unicode.strip(x), 'category': Category, 'author': Author, } -def _process_metadata(name, value): - if name.lower() in _METADATA_PROCESSORS: - return _METADATA_PROCESSORS[name.lower()](value) - return value - - class Reader(object): enabled = True extensions = None + def __init__(self, settings): + self.settings = settings + + def process_metadata(self, name, value): + if name.lower() in _METADATA_PROCESSORS: + return _METADATA_PROCESSORS[name.lower()](value, self.settings) + return value + class _FieldBodyTranslator(HTMLTranslator): def astext(self): @@ -54,29 +56,25 @@ def render_node_to_html(document, node): node.walkabout(visitor) return visitor.astext() -def get_metadata(document): - """Return the dict containing document metadata""" - output = {} - for docinfo in document.traverse(docutils.nodes.docinfo): - for element in docinfo.children: - if element.tagname == 'field': # custom fields (e.g. summary) - name_elem, body_elem = element.children - name = name_elem.astext() - value = render_node_to_html(document, body_elem) - else: # standard fields (e.g. address) - name = element.tagname - value = element.astext() - - output[name] = _process_metadata(name, value) - return output - - class RstReader(Reader): enabled = bool(docutils) extension = "rst" def _parse_metadata(self, document): - return get_metadata(document) + """Return the dict containing document metadata""" + output = {} + for docinfo in document.traverse(docutils.nodes.docinfo): + for element in docinfo.children: + if element.tagname == 'field': # custom fields (e.g. summary) + name_elem, body_elem = element.children + name = name_elem.astext() + value = render_node_to_html(document, body_elem) + else: # standard fields (e.g. address) + name = element.tagname + value = element.astext() + + output[name] = self.process_metadata(name, value) + return output def _get_publisher(self, filename): extra_params = {'initial_header_level': '2'} @@ -113,7 +111,7 @@ class MarkdownReader(Reader): metadata = {} for name, value in md.Meta.items(): name = name.lower() - metadata[name] = _process_metadata(name, value[0]) + metadata[name] = self.process_metadata(name, value[0]) return content, metadata @@ -129,7 +127,7 @@ class HtmlReader(Reader): key = i.split(':')[0][5:].strip() value = i.split(':')[-1][:-3].strip() name = key.lower() - metadata[name] = _process_metadata(name, value) + metadata[name] = self.process_metadata(name, value) return content, metadata @@ -143,7 +141,7 @@ def read_file(filename, fmt=None, settings=None): fmt = filename.split('.')[-1] if fmt not in _EXTENSIONS.keys(): raise TypeError('Pelican does not know how to parse %s' % filename) - reader = _EXTENSIONS[fmt]() + reader = _EXTENSIONS[fmt](settings) settings_key = '%s_EXTENSIONS' % fmt.upper() if settings and settings_key in settings: reader.extensions = settings[settings_key] From 9ba55c28b4cf22a74879f9a836af7e62fe3d9276 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Sat, 24 Dec 2011 00:37:18 +0000 Subject: [PATCH 133/219] Support CLEAN_URLS and ARTICLE_PERMALINK_STRUCTURE for backwards compatibility --- pelican/__init__.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pelican/__init__.py b/pelican/__init__.py index 710c9ff1..e7e90f56 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -1,5 +1,6 @@ import argparse import os, sys +import re import time from pelican.generators import (ArticlesGenerator, PagesGenerator, @@ -26,6 +27,42 @@ class Pelican(object): if self.path.endswith('/'): self.path = self.path[:-1] + if settings.get('CLEAN_URLS', False): + log.warning('Found deprecated `CLEAN_URLS` in settings. Modifing' + ' the following settings for the same behaviour.') + + settings['ARTICLE_URL'] = '{slug}/' + settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/' + settings['PAGE_URL'] = 'pages/{slug}/' + settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/' + + for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', + 'PAGE_LANG_URL'): + log.warning("%s = '%s'" % (setting, settings[setting])) + + if settings.get('ARTICLE_PERMALINK_STRUCTURE', False): + log.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' + ' settings. Modifing the following settings for' + ' the same behaviour.') + + structure = settings['ARTICLE_PERMALINK_STRUCTURE'] + + # Convert %(variable) into {variable}. + structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure) + + # Convert %x into {date:%x} for strftime + structure = re.sub('(%[A-z])', '{date:\g<1>}', structure) + + # Strip a / prefix + structure = re.sub('^/', '', structure) + + for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', + 'PAGE_LANG_URL', 'ARTICLE_SAVE_AS', + 'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS', + 'PAGE_LANG_SAVE_AS'): + settings[setting] = os.path.join(structure, settings[setting]) + log.warning("%s = '%s'" % (setting, settings[setting])) + # define the default settings self.settings = settings self.theme = theme or settings['THEME'] From c5816c9c5a242a3d5c3b26eb0b844fe2f79f52a2 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Thu, 1 Mar 2012 14:19:46 +0000 Subject: [PATCH 134/219] Make these patches compatible with upstream master --- pelican/contents.py | 2 +- pelican/generators.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 316a0e56..12da22ef 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -98,7 +98,7 @@ class Page(object): return { 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), - 'date': getattr(self, 'date', datetime.now()), + 'date': getattr(self, 'date', datetime.datetime.now()), 'author': self.author, 'category': getattr(self, 'category', 'misc'), } diff --git a/pelican/generators.py b/pelican/generators.py index 38bfb5b8..12bd20af 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -13,7 +13,7 @@ from operator import attrgetter, itemgetter from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader from jinja2.exceptions import TemplateNotFound -from pelican.contents import Article, Page, is_valid_content +from pelican.contents import Article, Page, Category, is_valid_content from pelican.log import * from pelican.readers import read_file from pelican.utils import copy, process_translations, open From 8bf0a22eb0a49cad133958cbd2145febb9e1228d Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Fri, 2 Mar 2012 16:32:05 +0100 Subject: [PATCH 135/219] fix encoding errors error was:codeEncodeError: 'ascii' codec can't encode character u'\xe9' [..] --- pelican/contents.py | 28 ++++++++++++++-------------- pelican/generators.py | 7 +++---- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 12da22ef..90bc189d 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -106,16 +106,16 @@ class Page(object): @property def url(self): if self.in_default_lang: - return self.settings.get('PAGE_URL', 'pages/{slug}.html').format(**self.url_format) + return self.settings.get('PAGE_URL', u'pages/{slug}.html').format(**self.url_format) - return self.settings.get('PAGE_LANG_URL', 'pages/{slug}-{lang}.html').format(**self.url_format) + return self.settings.get('PAGE_LANG_URL', u'pages/{slug}-{lang}.html').format(**self.url_format) @property def save_as(self): if self.in_default_lang: - return self.settings.get('PAGE_SAVE_AS', 'pages/{slug}.html').format(**self.url_format) + return self.settings.get('PAGE_SAVE_AS', u'pages/{slug}.html').format(**self.url_format) - return self.settings.get('PAGE_LANG_SAVE_AS', 'pages/{slug}-{lang}.html').format(**self.url_format) + return self.settings.get('PAGE_LANG_SAVE_AS', u'pages/{slug}-{lang}.html').format(**self.url_format) @property def content(self): @@ -143,16 +143,16 @@ class Article(Page): @property def url(self): if self.in_default_lang: - return self.settings.get('ARTICLE_URL', '{slug}.html').format(**self.url_format) + return self.settings.get('ARTICLE_URL', u'{slug}.html').format(**self.url_format) - return self.settings.get('ARTICLE_LANG_URL', '{slug}-{lang}.html').format(**self.url_format) + return self.settings.get('ARTICLE_LANG_URL', u'{slug}-{lang}.html').format(**self.url_format) @property def save_as(self): if self.in_default_lang: - return self.settings.get('ARTICLE_SAVE_AS', '{slug}.html').format(**self.url_format) + return self.settings.get('ARTICLE_SAVE_AS', u'{slug}.html').format(**self.url_format) - return self.settings.get('ARTICLE_LANG_SAVE_AS', '{slug}-{lang}.html').format(**self.url_format) + return self.settings.get('ARTICLE_LANG_SAVE_AS', u'{slug}-{lang}.html').format(**self.url_format) class Quote(Page): @@ -182,11 +182,11 @@ class URLWrapper(object): class Category(URLWrapper): @property def url(self): - return self.settings.get('CATEGORY_URL', 'category/{name}.html').format(name=self.name) + return self.settings.get('CATEGORY_URL', u'category/{name}.html').format(name=self.name) @property def save_as(self): - return self.settings.get('CATEGORY_SAVE_AS', 'category/{name}.html').format(name=self.name) + return self.settings.get('CATEGORY_SAVE_AS', u'category/{name}.html').format(name=self.name) class Tag(URLWrapper): def __init__(self, name, *args, **kwargs): @@ -194,20 +194,20 @@ class Tag(URLWrapper): @property def url(self): - return self.settings.get('TAG_URL', 'tag/{name}.html').format(name=self.name) + return self.settings.get('TAG_URL', u'tag/{name}.html').format(name=self.name) @property def save_as(self): - return self.settings.get('TAG_SAVE_AS', 'tag/{name}.html').format(name=self.name) + return self.settings.get('TAG_SAVE_AS', u'tag/{name}.html').format(name=self.name) class Author(URLWrapper): @property def url(self): - return self.settings.get('AUTHOR_URL', 'author/{name}.html').format(name=self.name) + return self.settings.get('AUTHOR_URL', u'author/{name}.html').format(name=self.name) @property def save_as(self): - return self.settings.get('AUTHOR_SAVE_AS', 'author/{name}.html').format(name=self.name) + return self.settings.get('AUTHOR_SAVE_AS', u'author/{name}.html').format(name=self.name) def is_valid_content(content, f): try: diff --git a/pelican/generators.py b/pelican/generators.py index 12bd20af..47ebb941 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -182,7 +182,7 @@ class ArticlesGenerator(Generator): write(tag.save_as, tag_template, self.context, tag=tag, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, - page_name='tag/%s' % tag) + page_name=u'tag/%s' % tag) category_template = self.get_template('category') for cat, articles in self.categories: @@ -190,7 +190,7 @@ class ArticlesGenerator(Generator): write(cat.save_as, category_template, self.context, category=cat, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, - page_name='category/%s' % cat) + page_name=u'category/%s' % cat) author_template = self.get_template('author') for aut, articles in self.authors: @@ -198,7 +198,7 @@ class ArticlesGenerator(Generator): write(aut.save_as, author_template, self.context, author=aut, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, - page_name='author/%s' % aut) + page_name=u'author/%s' % aut) for article in self.drafts: write('drafts/%s.html' % article.slug, article_template, self.context, @@ -212,7 +212,6 @@ class ArticlesGenerator(Generator): files = self.get_files(self.path, exclude=['pages',]) all_articles = [] for f in files: - try: content, metadata = read_file(f, settings=self.settings) except Exception, e: From bfd0e63e3d4b7a6b006024611e0451f4e39fbd7a Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Fri, 2 Mar 2012 23:19:03 +0100 Subject: [PATCH 136/219] fix pages urls --- pelican/themes/notmyidea/templates/base.html | 2 +- pelican/themes/notmyidea/templates/index.html | 2 +- pelican/themes/simple/templates/base.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index 4a4c70c6..12086dc7 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -31,7 +31,7 @@ {% endfor %} {% if DISPLAY_PAGES_ON_MENU %} {% for page in PAGES %} -
  3. {{ page.title }}
  4. +
  5. {{ page.title }}
  6. {% endfor %} {% endif %} {% for cat, null in categories %} diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html index 217bacf2..f81275ae 100644 --- a/pelican/themes/notmyidea/templates/index.html +++ b/pelican/themes/notmyidea/templates/index.html @@ -53,7 +53,7 @@

    Pages

    {% for page in PAGES %} -
  7. {{ page.title }}
  8. +
  9. {{ page.title }}
  10. {% endfor %}
    {% endif %} diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index a8fad2db..a1017219 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -17,7 +17,7 @@ {% endfor %} {% if DISPLAY_PAGES_ON_MENU %} {% for p in PAGES %} - {{ p.title }} + {{ p.title }} {% endfor %} {% else %} {% for cat, null in categories %} From c2b8caed3f75bb7f52065bf21226f8a41ac76519 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Mon, 5 Mar 2012 23:33:25 +0100 Subject: [PATCH 137/219] fix test test_article_with_metadata error was: ERROR: test_article_with_metadata (tests.test_readers.RstReaderTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/bruno/dev/pelican/tests/test_readers.py", line 22, in test_article_with_metadata reader = readers.RstReader() TypeError: __init__() takes exactly 2 arguments (1 given) --- tests/test_readers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_readers.py b/tests/test_readers.py index 1e920e09..120b3125 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -19,7 +19,7 @@ def _filename(*args): class RstReaderTest(unittest2.TestCase): def test_article_with_metadata(self): - reader = readers.RstReader() + reader = readers.RstReader({}) content, metadata = reader.read(_filename('article_with_metadata.rst')) expected = { 'category': 'yeah', From 8f8933d991fd5e9da1a22cc6e3d2beced83f6c7d Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Mon, 5 Mar 2012 23:48:18 +0100 Subject: [PATCH 138/219] fix test test_save_as ERROR: test_save_as (tests.test_contents.TestPage) If a lang is not the default lang, save_as should be set ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/bruno/dev/pelican/tests/test_contents.py", line 63, in test_save_as page.save_as = 'foo-bar.html' AttributeError: can't set attribute --- tests/test_contents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_contents.py b/tests/test_contents.py index 9772fac5..1abb125d 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -60,12 +60,12 @@ class TestPage(TestCase): """ # if a title is defined, save_as should be set page = Page(**self.page_kwargs) - page.save_as = 'foo-bar.html' + self.assertEqual(page.save_as, "pages/foo-bar.html") # if a language is defined, save_as should include it accordingly self.page_kwargs['metadata'].update({'lang': 'fr', }) page = Page(**self.page_kwargs) - self.assertEqual(page.save_as, "foo-bar-fr.html") + self.assertEqual(page.save_as, "pages/foo-bar-fr.html") def test_datetime(self): """If DATETIME is set to a tuple, it should be used to override LOCALE From 352d2047b433983231f1849ddfd81b46e609d940 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 6 Mar 2012 17:57:29 +0100 Subject: [PATCH 139/219] MOAR TYPOGRAPHY --- README.rst | 2 +- docs/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b521d430..2f66e54c 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ more information. Why the name "Pelican"? ------------------------ -Heh, you didn't notice? "Pelican" is an anagram for "Calepin" ;) +Heh, you didn't notice? "Pelican" is an anagram for « Calepin » ;) Source code ----------- diff --git a/docs/index.rst b/docs/index.rst index c6638997..1389f132 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,7 +29,7 @@ Pelican currently supports: Why the name "Pelican" ? ======================== -Heh, you didn't notice? "Pelican" is an anagram for "Calepin" ;) +Heh, you didn't notice? "Pelican" is an anagram for « Calepin » ;) Source code =========== From 1f32624e8bb33926d7d836ddf3b71772a2efb88e Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 10:14:47 +0000 Subject: [PATCH 140/219] use a try / except to check if argparse is a needed dependency --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9b299085..6c3a72a1 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,10 @@ import platform VERSION = "3.0" # find a better way to do so. requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz'] -if sys.version_info < (2,7): + +try: + import argparse +except ImportError: requires.append('argparse') scripts = ['bin/pelican', 'tools/pelican-themes', 'tools/pelican-import', 'tools/pelican-quickstart'] From 8009324a3be63e4c04e7d5dea0a50cb3a04bc68a Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 10:33:46 +0000 Subject: [PATCH 141/219] restructure the whole way scripts are created, using setuptools magic to take care of creating .exe wrappers for windows. To make this work needed to - rename modules with a "-" in it (not a valid name) - add an __init__.py to the tools directory - create an entry point dictionary which stores the right associations --- setup.py | 17 ++++++------- tools/__init__.py | 0 tools/pelican-import.bat | 1 - tools/pelican-quickstart.bat | 1 - tools/pelican-themes.bat | 1 - tools/{pelican-import => pelican_import.py} | 24 +++++++++---------- ...lican-quickstart => pelican_quickstart.py} | 0 tools/{pelican-themes => pelican_themes.py} | 0 8 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 tools/__init__.py delete mode 100755 tools/pelican-import.bat delete mode 100755 tools/pelican-quickstart.bat delete mode 100755 tools/pelican-themes.bat rename tools/{pelican-import => pelican_import.py} (98%) rename tools/{pelican-quickstart => pelican_quickstart.py} (100%) rename tools/{pelican-themes => pelican_themes.py} (100%) diff --git a/setup.py b/setup.py index 6c3a72a1..6556193d 100755 --- a/setup.py +++ b/setup.py @@ -12,13 +12,14 @@ try: except ImportError: requires.append('argparse') -scripts = ['bin/pelican', 'tools/pelican-themes', 'tools/pelican-import', 'tools/pelican-quickstart'] - -if sys.platform.startswith('win'): - scripts += [ - 'bin/pelican.bat', 'tools/pelican-themes.bat', - 'tools/pelican-import.bat', 'tools/pelican-quickstart.bat' - ] +entry_points = { + 'console_scripts': [ + 'pelican = pelican:main', + 'pelican-import = tools.pelican_import:main', + 'pelican-quickstart = tools.pelican_quickstart:main', + 'pelican-themes = tools.pelican_themes:main' + ] +} setup( name = "pelican", @@ -31,7 +32,7 @@ setup( packages = ['pelican'], include_package_data = True, install_requires = requires, - scripts = scripts, + entry_points = entry_points, classifiers = ['Development Status :: 5 - Production/Stable', 'Environment :: Console', 'License :: OSI Approved :: GNU Affero General Public License v3', diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/pelican-import.bat b/tools/pelican-import.bat deleted file mode 100755 index 674e1fd7..00000000 --- a/tools/pelican-import.bat +++ /dev/null @@ -1 +0,0 @@ -@python "%~dpn0" %* diff --git a/tools/pelican-quickstart.bat b/tools/pelican-quickstart.bat deleted file mode 100755 index 674e1fd7..00000000 --- a/tools/pelican-quickstart.bat +++ /dev/null @@ -1 +0,0 @@ -@python "%~dpn0" %* diff --git a/tools/pelican-themes.bat b/tools/pelican-themes.bat deleted file mode 100755 index 674e1fd7..00000000 --- a/tools/pelican-themes.bat +++ /dev/null @@ -1 +0,0 @@ -@python "%~dpn0" %* diff --git a/tools/pelican-import b/tools/pelican_import.py similarity index 98% rename from tools/pelican-import rename to tools/pelican_import.py index 3a931afd..261bff4c 100755 --- a/tools/pelican-import +++ b/tools/pelican_import.py @@ -231,18 +231,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False): fs.write(header + content) -def main(input_type, input, out_markup, output_path, dircat=False): - if input_type == 'wordpress': - fields = wp2fields(input) - elif input_type == 'dotclear': - fields = dc2fields(input) - elif input_type == 'feed': - fields = feed2fields(input) - - fields2pelican(fields, out_markup, output_path, dircat=dircat) - - -if __name__ == '__main__': +def main(): parser = argparse.ArgumentParser( description="Transform feed, Wordpress or Dotclear files to rst files." "Be sure to have pandoc installed") @@ -280,4 +269,13 @@ if __name__ == '__main__': error("Couldn't create the output folder: " + args.output) exit() - main(input_type, args.input, args.markup, args.output, dircat=args.dircat) + input_type, input, out_markup, output_path, dircat=False = input_type, args.input, args.markup, args.output, args.dircat + + if input_type == 'wordpress': + fields = wp2fields(input) + elif input_type == 'dotclear': + fields = dc2fields(input) + elif input_type == 'feed': + fields = feed2fields(input) + + fields2pelican(fields, out_markup, output_path, dircat=dircat) diff --git a/tools/pelican-quickstart b/tools/pelican_quickstart.py similarity index 100% rename from tools/pelican-quickstart rename to tools/pelican_quickstart.py diff --git a/tools/pelican-themes b/tools/pelican_themes.py similarity index 100% rename from tools/pelican-themes rename to tools/pelican_themes.py From 7c78f232b4b10a31607815469fb7cf276a807468 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 10:34:14 +0000 Subject: [PATCH 142/219] remove unused imports --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 6556193d..910499de 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python from setuptools import setup -import sys -import platform VERSION = "3.0" # find a better way to do so. From 8f7b08a01cd5a8ff679946996d35f285380ab93d Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 10:38:08 +0000 Subject: [PATCH 143/219] remove now useless if __name__ == '__main__' checks and clean up the old pelican script in bin --- bin/pelican | 3 --- bin/pelican.bat | 1 - pelican/__init__.py | 4 ---- tools/pelican_import.py | 1 + tools/pelican_quickstart.py | 3 --- tools/pelican_themes.py | 3 --- 6 files changed, 1 insertion(+), 14 deletions(-) delete mode 100755 bin/pelican delete mode 100755 bin/pelican.bat diff --git a/bin/pelican b/bin/pelican deleted file mode 100755 index 3fe2ee57..00000000 --- a/bin/pelican +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -from pelican import main -main() diff --git a/bin/pelican.bat b/bin/pelican.bat deleted file mode 100755 index 674e1fd7..00000000 --- a/bin/pelican.bat +++ /dev/null @@ -1 +0,0 @@ -@python "%~dpn0" %* diff --git a/pelican/__init__.py b/pelican/__init__.py index 710c9ff1..a0b5704c 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -158,7 +158,3 @@ def main(): raise else: sys.exit(getattr(e, 'exitcode', 1)) - - -if __name__ == '__main__': - main() diff --git a/tools/pelican_import.py b/tools/pelican_import.py index 261bff4c..e7f8e051 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -269,6 +269,7 @@ def main(): error("Couldn't create the output folder: " + args.output) exit() + # TODO: refactor this long assignment input_type, input, out_markup, output_path, dircat=False = input_type, args.input, args.markup, args.output, args.dircat if input_type == 'wordpress': diff --git a/tools/pelican_quickstart.py b/tools/pelican_quickstart.py index 4048c2bf..56c22f10 100755 --- a/tools/pelican_quickstart.py +++ b/tools/pelican_quickstart.py @@ -270,6 +270,3 @@ Please answer the following questions so this script can generate the files need print('Error: {0}'.format(e)) print('Done. Your new project is available at %s' % CONF['basedir']) - -if __name__ == '__main__': - main() diff --git a/tools/pelican_themes.py b/tools/pelican_themes.py index 78df4a48..3d35bb5d 100755 --- a/tools/pelican_themes.py +++ b/tools/pelican_themes.py @@ -212,6 +212,3 @@ def clean(v=False): c+=1 print("\nRemoved {0} broken links".format(c)) - -if __name__ == '__main__': - main() From 4eb20c5d82cf59ae60a765d5af4f723157fed2b6 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 12:00:32 +0000 Subject: [PATCH 144/219] global declaration is only needed if it's necessary to modify a module level variable, to declare it and use it read only it's not necessary --- pelican/log.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pelican/log.py b/pelican/log.py index 2790aed3..8ec44cad 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -4,7 +4,6 @@ from logging import CRITICAL, ERROR, WARN, INFO, DEBUG from logging import critical, error, info, warning, warn, debug from logging import Formatter, getLogger, StreamHandler -global ANSI ANSI = { 'gray' : lambda(text) : u'\033[1;30m' + unicode(text) + u'\033[1;m', 'red' : lambda(text) : u'\033[1;31m' + unicode(text) + u'\033[1;m', From 2e0e893a9b1119465c69c5204a03894ab955bcfb Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 12:14:13 +0000 Subject: [PATCH 145/219] fix three print statement --- tools/pelican_import.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index e7f8e051..b883f7fc 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -71,7 +71,7 @@ def dc2fields(file): else: posts.append(line) - print "%i posts read." % len(posts) + print("%i posts read." % len(posts)) for post in posts: fields = post.split('","') @@ -205,7 +205,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False): else: out_filename = os.path.join(output_path, filename+ext) - print out_filename + print(out_filename) if in_markup == "html": html_filename = os.path.join(output_path, filename+'.html') @@ -259,7 +259,7 @@ def main(): elif args.feed: input_type = 'feed' else: - print "you must provide either --wpfile, --dotclear or --feed options" + print("you must provide either --wpfile, --dotclear or --feed options") exit() if not os.path.exists(args.output): From 20af8fd378d693c5c0cf59a6c12fad0800945ece Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 7 Mar 2012 12:17:39 +0000 Subject: [PATCH 146/219] refactoring of the ANSI dictionary, removing all the repeated code --- pelican/log.py | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/pelican/log.py b/pelican/log.py index 8ec44cad..1cb76e16 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -4,26 +4,36 @@ from logging import CRITICAL, ERROR, WARN, INFO, DEBUG from logging import critical, error, info, warning, warn, debug from logging import Formatter, getLogger, StreamHandler -ANSI = { - 'gray' : lambda(text) : u'\033[1;30m' + unicode(text) + u'\033[1;m', - 'red' : lambda(text) : u'\033[1;31m' + unicode(text) + u'\033[1;m', - 'green' : lambda(text) : u'\033[1;32m' + unicode(text) + u'\033[1;m', - 'yellow' : lambda(text) : u'\033[1;33m' + unicode(text) + u'\033[1;m', - 'blue' : lambda(text) : u'\033[1;34m' + unicode(text) + u'\033[1;m', - 'magenta' : lambda(text) : u'\033[1;35m' + unicode(text) + u'\033[1;m', - 'cyan' : lambda(text) : u'\033[1;36m' + unicode(text) + u'\033[1;m', - 'white' : lambda(text) : u'\033[1;37m' + unicode(text) + u'\033[1;m', - 'bgred' : lambda(text) : u'\033[1;41m' + unicode(text) + u'\033[1;m', - 'bggreen' : lambda(text) : u'\033[1;42m' + unicode(text) + u'\033[1;m', - 'bgbrown' : lambda(text) : u'\033[1;43m' + unicode(text) + u'\033[1;m', - 'bgblue' : lambda(text) : u'\033[1;44m' + unicode(text) + u'\033[1;m', - 'bgmagenta' : lambda(text) : u'\033[1;45m' + unicode(text) + u'\033[1;m', - 'bgcyan' : lambda(text) : u'\033[1;46m' + unicode(text) + u'\033[1;m', - 'bggray' : lambda(text) : u'\033[1;47m' + unicode(text) + u'\033[1;m', - 'bgyellow' : lambda(text) : u'\033[1;43m' + unicode(text) + u'\033[1;m', - 'bggrey' : lambda(text) : u'\033[1;100m' + unicode(text) + u'\033[1;m' + +RESET_TERM = u'\033[1;m' + + +def term_color(code): + return lambda text: code + unicode(text) + RESET_TERM + + +COLOR_CODES = { + 'gray': u'\033[1;30m', + 'red': u'\033[1;31m', + 'green': u'\033[1;32m', + 'yellow': u'\033[1;33m', + 'blue': u'\033[1;34m', + 'magenta': u'\033[1;35m', + 'cyan': u'\033[1;36m', + 'white': u'\033[1;37m', + 'bgred': u'\033[1;41m', + 'bggreen': u'\033[1;42m', + 'bgbrown': u'\033[1;43m', + 'bgblue': u'\033[1;44m', + 'bgmagenta': u'\033[1;45m', + 'bgcyan': u'\033[1;46m', + 'bggray': u'\033[1;47m', + 'bgyellow': u'\033[1;43m', + 'bggrey': u'\033[1;100m', } +ANSI = dict((col, term_color(code)) for col, code in COLOR_CODES.items()) + class ANSIFormatter(Formatter): """ From dd07ddb0b018c94e0ca1c04a9be3cbefdf42b028 Mon Sep 17 00:00:00 2001 From: Meir Kriheli Date: Fri, 9 Mar 2012 02:03:15 +0200 Subject: [PATCH 147/219] Make sure test passes for dates formatted as utf-8 --- tests/test_contents.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_contents.py b/tests/test_contents.py index 9772fac5..e4c98741 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -82,7 +82,8 @@ class TestPage(TestCase): page_kwargs['metadata']['date'] = dt page = Page( **page_kwargs) - self.assertEqual(page.locale_date, dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])) + self.assertEqual(page.locale_date, + unicode(dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']), 'utf-8')) page_kwargs['settings'] = {x:_DEFAULT_CONFIG[x] for x in _DEFAULT_CONFIG} From 7240460688a9dcf438cd8f9c8edb931700b2375f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 15:33:34 +0100 Subject: [PATCH 148/219] Update the CHANGELOG for 2.8 --- CHANGELOG | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 57056826..3d34ad20 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,21 @@ -3.0 +2.8 * dotclear importer -* Markdown extensions -* Theme extensions -* Plugins support +* Allow the usage of markdown extensions +* Themes are now easily extensible +* Don't output pagination information if there is only one page. +* Add a page per author, with all their articles +* Improved the test suite +* Made the themes more easy to extend +* Removed Skribit support +* Added a "pelican-quickstart" script +* Fixed timezone-related issues +* Add some scripts for windows support +* Date can be specified in seconds +* Never fail when generating posts (skip and continue) +* Allow the use of future dates +* Support having different timezones per languages. +* Enhanced the documentation 2.7 From 0298d412dc595436df995eed540ea462b7df8161 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 15:36:29 +0100 Subject: [PATCH 149/219] updated CHANGELOG --- CHANGELOG | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3d34ad20..e68c6f0d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +X.X + +* Refactored the way URL are handled. +* Improved the english documentation +* Fixed packaging using setuptools entrypoints + 2.8 * dotclear importer From df25dec30a3aee09bc1b75f35419886e5ea77bfd Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 16:17:09 +0100 Subject: [PATCH 150/219] Use the with statement when opening files. --- pelican/generators.py | 3 ++- pelican/readers.py | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 47ebb941..9d5607e2 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -375,7 +375,8 @@ class PdfGenerator(Generator): filename = obj.slug + ".pdf" output_pdf=os.path.join(output_path, filename) # print "Generating pdf for", obj.filename, " in ", output_pdf - self.pdfcreator.createPdf(text=open(obj.filename), output=output_pdf) + with open(obj.filename) as f: + self.pdfcreator.createPdf(text=f, output=output_pdf) info(u' [ok] writing %s' % output_pdf) def generate_context(self): diff --git a/pelican/readers.py b/pelican/readers.py index 814f81d2..c4c12280 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -121,20 +121,20 @@ class HtmlReader(Reader): def read(self, filename): """Parse content and metadata of (x)HTML files""" - content = open(filename) - metadata = {'title':'unnamed'} - for i in self._re.findall(content): - key = i.split(':')[0][5:].strip() - value = i.split(':')[-1][:-3].strip() - name = key.lower() - metadata[name] = self.process_metadata(name, value) - - return content, metadata + with open(filename) as content: + metadata = {'title': 'unnamed'} + for i in self._re.findall(content): + key = i.split(':')[0][5:].strip() + value = i.split(':')[-1][:-3].strip() + name = key.lower() + metadata[name] = self.process_metadata(name, value) + return content, metadata _EXTENSIONS = dict((cls.extension, cls) for cls in Reader.__subclasses__()) + def read_file(filename, fmt=None, settings=None): """Return a reader object using the given format.""" if not fmt: From 6cde7fd27a6faf062e52bca9e3e2645e487191b3 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 16:21:38 +0100 Subject: [PATCH 151/219] PEP8-ify. Wrap to 80 chars, sanitize imports. --- pelican/__init__.py | 44 ++++++++++++---------- pelican/contents.py | 26 ++++++++----- pelican/generators.py | 85 +++++++++++++++++++++++-------------------- pelican/paginator.py | 3 +- pelican/readers.py | 18 +++++---- pelican/settings.py | 25 +++++++------ pelican/utils.py | 20 ++++++---- pelican/writers.py | 31 +++++++++------- 8 files changed, 142 insertions(+), 110 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index bbd0b679..8de68d69 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -1,5 +1,6 @@ import argparse -import os, sys +import os +import sys import re import time @@ -69,7 +70,8 @@ class Pelican(object): output_path = output_path or settings['OUTPUT_PATH'] self.output_path = os.path.realpath(output_path) self.markup = markup or settings['MARKUP'] - self.delete_outputdir = delete_outputdir or settings['DELETE_OUTPUT_DIRECTORY'] + self.delete_outputdir = delete_outputdir \ + or settings['DELETE_OUTPUT_DIRECTORY'] # find the theme in pelican.theme if the given one does not exists if not os.path.exists(self.theme): @@ -112,7 +114,6 @@ class Pelican(object): if hasattr(p, 'generate_output'): p.generate_output(writer) - def get_generator_classes(self): generators = [ArticlesGenerator, PagesGenerator, StaticGenerator] if self.settings['PDF_GENERATOR']: @@ -123,7 +124,6 @@ class Pelican(object): return Writer(self.output_path, settings=self.settings) - def main(): parser = argparse.ArgumentParser(description="""A tool to generate a static blog, with restructured text input files.""") @@ -134,32 +134,38 @@ def main(): 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.') + 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=None, dest='markup', help='the list of markup language to use (rst or md). Please indicate ' 'them separated by commas') parser.add_argument('-s', '--settings', dest='settings', default='', help='the settings of the application. Default to False.') - parser.add_argument('-d', '--delete-output-directory', dest='delete_outputdir', + parser.add_argument('-d', '--delete-output-directory', + dest='delete_outputdir', action='store_true', help='Delete the output directory.') - parser.add_argument('-v', '--verbose', action='store_const', const=log.INFO, dest='verbosity', - help='Show all messages') - parser.add_argument('-q', '--quiet', action='store_const', const=log.CRITICAL, dest='verbosity', - help='Show only critical errors') - parser.add_argument('-D', '--debug', action='store_const', const=log.DEBUG, dest='verbosity', - help='Show all message, including debug messages') + parser.add_argument('-v', '--verbose', action='store_const', + const=log.INFO, dest='verbosity', + help='Show all messages') + parser.add_argument('-q', '--quiet', action='store_const', + const=log.CRITICAL, dest='verbosity', + help='Show only critical errors') + parser.add_argument('-D', '--debug', action='store_const', + const=log.DEBUG, dest='verbosity', + help='Show all message, including debug messages') parser.add_argument('--version', action='version', version=__version__, help='Print the pelican version and exit') - parser.add_argument('-r', '--autoreload', dest='autoreload', action='store_true', - help="Relaunch pelican each time a modification occurs on the content" - "files") + parser.add_argument('-r', '--autoreload', dest='autoreload', + action='store_true', + help="Relaunch pelican each time a modification occurs" + " on the content files") args = parser.parse_args() log.init(args.verbosity) - # Split the markup languages only if some have been given. Otherwise, populate - # the variable with None. - markup = [a.strip().lower() for a in args.markup.split(',')] if args.markup else None + # Split the markup languages only if some have been given. Otherwise, + # populate the variable with None. + markup = [a.strip().lower() for a in args.markup.split(',')]\ + if args.markup else None settings = read_settings(args.settings) diff --git a/pelican/contents.py b/pelican/contents.py index 90bc189d..b408ff58 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -import datetime +from datetime import datetime from os import getenv from sys import platform, stdin import locale -from pelican.log import * +from pelican.log import warning, error from pelican.settings import _DEFAULT_CONFIG from pelican.utils import slugify, truncate_html_words + class Page(object): """Represents a page Given a content, and metadata, create an adequate object. @@ -41,7 +42,8 @@ class Page(object): self.author = Author(settings['AUTHOR'], settings) else: self.author = Author(getenv('USER', 'John Doe'), settings) - warning(u"Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author)) + warning(u"Author of `{0}' unknow, assuming that his name is " + "`{1}'".format(filename or self.title, self.author)) # manage languages self.in_default_lang = True @@ -71,16 +73,19 @@ class Page(object): self.date_format = self.date_format[1] if hasattr(self, 'date'): + encoded_date = self.date.strftime( + self.date_format.encode('ascii', 'xmlcharrefreplace')) + if platform == 'win32': - self.locale_date = self.date.strftime(self.date_format.encode('ascii','xmlcharrefreplace')).decode(stdin.encoding) + self.locale_date = encoded_date.decode(stdin.encoding) else: - self.locale_date = self.date.strftime(self.date_format.encode('ascii','xmlcharrefreplace')).decode('utf') + self.locale_date = encoded_date.decode('utf') # manage status if not hasattr(self, 'status'): self.status = settings['DEFAULT_STATUS'] if not settings['WITH_FUTURE_DATES']: - if hasattr(self, 'date') and self.date > datetime.datetime.now(): + if hasattr(self, 'date') and self.date > datetime.now(): self.status = 'draft' # set summary @@ -98,7 +103,7 @@ class Page(object): return { 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), - 'date': getattr(self, 'date', datetime.datetime.now()), + 'date': getattr(self, 'date', datetime.now()), 'author': self.author, 'category': getattr(self, 'category', 'misc'), } @@ -133,8 +138,8 @@ class Page(object): """Dummy function""" pass - summary = property(_get_summary, _set_summary, - "Summary of the article. Based on the content. Can't be set") + summary = property(_get_summary, _set_summary, "Summary of the article." + "Based on the content. Can't be set") class Article(Page): @@ -214,5 +219,6 @@ def is_valid_content(content, f): content.check_properties() return True except NameError, e: - error(u"Skipping %s: impossible to find informations about '%s'" % (f, e)) + error(u"Skipping %s: impossible to find informations about '%s'"\ + % (f, e)) return False diff --git a/pelican/generators.py b/pelican/generators.py index 9d5607e2..ee95545e 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -3,7 +3,6 @@ import os import datetime import math import random -import urlparse from collections import defaultdict from functools import partial @@ -14,10 +13,9 @@ from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader from jinja2.exceptions import TemplateNotFound from pelican.contents import Article, Page, Category, is_valid_content -from pelican.log import * +from pelican.log import warning, error, debug, info from pelican.readers import read_file from pelican.utils import copy, process_translations, open -from pelican.utils import slugify class Generator(object): @@ -33,17 +31,23 @@ class Generator(object): # templates cache self._templates = {} - self._templates_path = os.path.expanduser(os.path.join(self.theme, 'templates')) - simple_loader = FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), "themes", "simple", "templates")) + self._templates_path = os.path.expanduser( + os.path.join(self.theme, 'templates')) + + theme_path = os.path.join(os.path.dirname(os.path.abspath(__file__))) + + simple_loader = FileSystemLoader(theme_path, + "themes", "simple", "templates") self._env = Environment( loader=ChoiceLoader([ FileSystemLoader(self._templates_path), - simple_loader, # implicit inheritance - PrefixLoader({'!simple' : simple_loader}) # explicit inheritance + simple_loader, # implicit inheritance + PrefixLoader({'!simple': simple_loader}) # explicit one ]), extensions=self.settings.get('JINJA_EXTENSIONS', []), ) - debug('self._env.list_templates(): {0}'.format(self._env.list_templates())) + + debug('template list: {0}'.format(self._env.list_templates())) # get custom Jinja filters from user settings custom_filters = self.settings.get('JINJA_FILTERS', {}) @@ -58,8 +62,8 @@ class Generator(object): try: self._templates[name] = self._env.get_template(name + '.html') except TemplateNotFound: - raise Exception('[templates] unable to load %s.html from %s' % ( - name, self._templates_path)) + raise Exception('[templates] unable to load %s.html from %s' \ + % (name, self._templates_path)) return self._templates[name] def get_files(self, path, exclude=[], extensions=None): @@ -75,7 +79,7 @@ class Generator(object): try: iter = os.walk(path, followlinks=True) - except TypeError: # python 2.5 does not support followlinks + except TypeError: # python 2.5 does not support followlinks iter = os.walk(path) for root, dirs, temp_files in iter: @@ -102,7 +106,7 @@ class ArticlesGenerator(Generator): def __init__(self, *args, **kwargs): """initialize properties""" - self.articles = [] # only articles in default language + self.articles = [] # only articles in default language self.translations = [] self.dates = {} self.tags = defaultdict(list) @@ -138,7 +142,8 @@ class ArticlesGenerator(Generator): if 'TAG_FEED_RSS' in self.settings: writer.write_feed(arts, self.context, - self.settings['TAG_FEED_RSS'] % tag, feed_type='rss') + self.settings['TAG_FEED_RSS'] % tag, + feed_type='rss') translations_feeds = defaultdict(list) for article in chain(self.articles, self.translations): @@ -149,14 +154,11 @@ class ArticlesGenerator(Generator): writer.write_feed(items, self.context, self.settings['TRANSLATION_FEED'] % lang) - def generate_pages(self, writer): """Generate the pages on the disk""" - write = partial( - writer.write_file, - relative_urls = self.settings.get('RELATIVE_URLS') - ) + write = partial(writer.write_file, + relative_urls=self.settings.get('RELATIVE_URLS')) # to minimize the number of relative path stuff modification # in writer, articles pass first @@ -171,8 +173,10 @@ class ArticlesGenerator(Generator): paginated = {} if template in PAGINATED_TEMPLATES: paginated = {'articles': self.articles, 'dates': self.dates} - write('%s.html' % template, self.get_template(template), self.context, - blog=True, paginated=paginated, page_name=template) + + write('%s.html' % template, self.get_template(template), + self.context, blog=True, paginated=paginated, + page_name=template) # and subfolders after that tag_template = self.get_template('tag') @@ -201,15 +205,14 @@ class ArticlesGenerator(Generator): page_name=u'author/%s' % aut) for article in self.drafts: - write('drafts/%s.html' % article.slug, article_template, self.context, - article=article, category=article.category) - + write('drafts/%s.html' % article.slug, article_template, + 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',]) + files = self.get_files(self.path, exclude=['pages', ]) all_articles = [] for f in files: try: @@ -224,14 +227,16 @@ class ArticlesGenerator(Generator): if os.path.dirname(f) == self.path: category = self.settings['DEFAULT_CATEGORY'] else: - category = os.path.basename(os.path.dirname(f)).decode('utf-8') + category = os.path.basename(os.path.dirname(f))\ + .decode('utf-8') if category != '': metadata['category'] = Category(category, self.settings) if 'date' not in metadata.keys()\ and self.settings['FALLBACK_ON_FS_DATE']: - metadata['date'] = datetime.datetime.fromtimestamp(os.stat(f).st_ctime) + metadata['date'] = datetime.datetime.fromtimestamp( + os.stat(f).st_ctime) article = Article(content, metadata, settings=self.settings, filename=f) @@ -265,7 +270,7 @@ class ArticlesGenerator(Generator): for tag in getattr(article, 'tags', []): tag_cloud[tag] += 1 - tag_cloud = sorted(tag_cloud.items(), key = itemgetter(1), reverse = True) + tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True) tag_cloud = tag_cloud[:self.settings.get('TAG_CLOUD_MAX_ITEMS')] tags = map(itemgetter(1), tag_cloud) @@ -277,9 +282,8 @@ class ArticlesGenerator(Generator): self.tag_cloud = [ ( tag, - int( - math.floor(steps - (steps - 1) * math.log(count) / (math.log(max_count)or 1)) - ) + int(math.floor(steps - (steps - 1) * math.log(count) + / (math.log(max_count)or 1))) ) for tag, count in tag_cloud ] @@ -290,14 +294,13 @@ class ArticlesGenerator(Generator): # order the categories per name self.categories = list(self.categories.items()) - self.categories.sort(reverse=self.settings.get('REVERSE_CATEGORY_ORDER')) + self.categories.sort(reverse=self.settings['REVERSE_CATEGORY_ORDER']) self.authors = list(self.authors.items()) self.authors.sort() - self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud', 'authors')) - - + self._update_context(('articles', 'dates', 'tags', 'categories', + 'tag_cloud', 'authors')) def generate_output(self, writer): self.generate_feeds(writer) @@ -334,7 +337,7 @@ class PagesGenerator(Generator): for page in chain(self.translations, self.pages): writer.write_file(page.save_as, self.get_template('page'), self.context, page=page, - relative_urls = self.settings.get('RELATIVE_URLS')) + relative_urls=self.settings.get('RELATIVE_URLS')) class StaticGenerator(Generator): @@ -345,8 +348,8 @@ class StaticGenerator(Generator): final_path=None): """Copy all the paths from source to destination""" for path in paths: - copy(path, source, os.path.join(output_path, destination), final_path, - overwrite=True) + copy(path, source, os.path.join(output_path, destination), + final_path, overwrite=True) def generate_output(self, writer): self._copy_paths(self.settings['STATIC_PATHS'], self.path, @@ -356,7 +359,8 @@ class StaticGenerator(Generator): # copy all the files needed for source, destination in self.settings['FILES_TO_COPY']: - copy(source, self.path, self.output_path, destination, overwrite=True) + copy(source, self.path, self.output_path, destination, + overwrite=True) class PdfGenerator(Generator): @@ -365,7 +369,8 @@ class PdfGenerator(Generator): def __init__(self, *args, **kwargs): try: from rst2pdf.createpdf import RstToPdf - self.pdfcreator = RstToPdf(breakside=0, stylesheets=['twelvepoint']) + self.pdfcreator = RstToPdf(breakside=0, + stylesheets=['twelvepoint']) except ImportError: raise Exception("unable to find rst2pdf") super(PdfGenerator, self).__init__(*args, **kwargs) @@ -373,7 +378,7 @@ class PdfGenerator(Generator): def _create_pdf(self, obj, output_path): if obj.filename.endswith(".rst"): filename = obj.slug + ".pdf" - output_pdf=os.path.join(output_path, filename) + output_pdf = os.path.join(output_path, filename) # print "Generating pdf for", obj.filename, " in ", output_pdf with open(obj.filename) as f: self.pdfcreator.createPdf(text=f, output=output_pdf) diff --git a/pelican/paginator.py b/pelican/paginator.py index 89e081ca..fe871491 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -1,6 +1,7 @@ # From django.core.paginator from math import ceil + class Paginator(object): def __init__(self, object_list, per_page, orphans=0): self.object_list = object_list @@ -39,6 +40,7 @@ class Paginator(object): return range(1, self.num_pages + 1) page_range = property(_get_page_range) + class Page(object): def __init__(self, object_list, number, paginator): self.object_list = object_list @@ -82,4 +84,3 @@ class Page(object): if self.number == self.paginator.num_pages: return self.paginator.count return self.number * self.paginator.per_page - diff --git a/pelican/readers.py b/pelican/readers.py index c4c12280..5bbbfb30 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -6,27 +6,28 @@ try: from docutils.writers.html4css1 import HTMLTranslator # import the directives to have pygments support - from pelican import rstdirectives + from pelican import rstdirectives # NOQA except ImportError: core = False try: from markdown import Markdown except ImportError: - Markdown = False + Markdown = False # NOQA import re -from pelican.contents import Category, Tag, Author, URLWrapper +from pelican.contents import Category, Tag, Author from pelican.utils import get_date, open _METADATA_PROCESSORS = { 'tags': lambda x, y: [Tag(tag, y) for tag in unicode(x).split(',')], 'date': lambda x, y: get_date(x), - 'status': lambda x,y: unicode.strip(x), + 'status': lambda x, y: unicode.strip(x), 'category': Category, 'author': Author, } + class Reader(object): enabled = True extensions = None @@ -39,6 +40,7 @@ class Reader(object): return _METADATA_PROCESSORS[name.lower()](value, self.settings) return value + class _FieldBodyTranslator(HTMLTranslator): def astext(self): @@ -56,6 +58,7 @@ def render_node_to_html(document, node): node.walkabout(visitor) return visitor.astext() + class RstReader(Reader): enabled = bool(docutils) extension = "rst" @@ -65,11 +68,11 @@ class RstReader(Reader): output = {} for docinfo in document.traverse(docutils.nodes.docinfo): for element in docinfo.children: - if element.tagname == 'field': # custom fields (e.g. summary) + if element.tagname == 'field': # custom fields (e.g. summary) name_elem, body_elem = element.children name = name_elem.astext() value = render_node_to_html(document, body_elem) - else: # standard fields (e.g. address) + else: # standard fields (e.g. address) name = element.tagname value = element.astext() @@ -78,7 +81,8 @@ class RstReader(Reader): def _get_publisher(self, filename): extra_params = {'initial_header_level': '2'} - pub = docutils.core.Publisher(destination_class=docutils.io.StringOutput) + pub = docutils.core.Publisher( + destination_class=docutils.io.StringOutput) pub.set_components('standalone', 'restructuredtext', 'html') pub.process_programmatic_settings(None, extra_params, None) pub.set_source(source_path=filename) diff --git a/pelican/settings.py b/pelican/settings.py index ec6ec483..fcabd518 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import os +from os.path import isabs import locale from pelican import log @@ -10,8 +11,8 @@ _DEFAULT_CONFIG = {'PATH': None, 'THEME': DEFAULT_THEME, 'OUTPUT_PATH': 'output/', 'MARKUP': ('rst', 'md'), - 'STATIC_PATHS': ['images',], - 'THEME_STATIC_PATHS': ['static',], + 'STATIC_PATHS': ['images', ], + 'THEME_STATIC_PATHS': ['static', ], 'FEED': 'feeds/all.atom.xml', 'CATEGORY_FEED': 'feeds/%s.atom.xml', 'TRANSLATION_FEED': 'feeds/all-%s.atom.xml', @@ -44,7 +45,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'DEFAULT_DATE_FORMAT': '%a %d %B %Y', 'DATE_FORMATS': {}, 'JINJA_EXTENSIONS': [], - 'LOCALE': '', # default to user locale + 'LOCALE': '', # default to user locale 'DEFAULT_PAGINATION': False, 'DEFAULT_ORPHANS': 0, 'DEFAULT_METADATA': (), @@ -53,6 +54,7 @@ _DEFAULT_CONFIG = {'PATH': None, 'ARTICLE_PERMALINK_STRUCTURE': '' } + def read_settings(filename): """Load a Python file into a dictionary. """ @@ -67,9 +69,10 @@ def read_settings(filename): # Make the paths relative to the settings file for path in ['PATH', 'OUTPUT_PATH']: if path in context: - if context[path] is not None and not os.path.isabs(context[path]): - # FIXME: - context[path] = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(filename), context[path]))) + if context[path] is not None and not isabs(context[path]): + context[path] = os.path.abspath(os.path.normpath( + os.path.join(os.path.dirname(filename), context[path])) + ) # if locales is not a list, make it one locales = context['LOCALE'] @@ -84,17 +87,17 @@ def read_settings(filename): for locale_ in locales: try: locale.setlocale(locale.LC_ALL, locale_) - break # break if it is successfull + break # break if it is successfull except locale.Error: pass else: log.warn("LOCALE option doesn't contain a correct value") if not 'TIMEZONE' in context: - log.warn("No timezone information specified in the settings. Assuming your "\ - "timezone is UTC for feed generation. "\ - "Check http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "\ - "for more information") + log.warn("No timezone information specified in the settings. Assuming" + " your timezone is UTC for feed generation. Check " + "http://docs.notmyidea.org/alexis/pelican/settings.html#timezone " + "for more information") # set the locale return context diff --git a/pelican/utils.py b/pelican/utils.py index c2daae03..93541cc0 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -19,7 +19,7 @@ def get_date(string): string = re.sub(' +', ' ', string) formats = ['%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M', '%Y-%m-%d', '%Y/%m/%d', - '%d-%m-%Y', '%Y-%d-%m', # Weird ones + '%d-%m-%Y', '%Y-%d-%m', # Weird ones '%d/%m/%Y', '%d.%m.%Y', '%d.%m.%Y %H:%M', '%Y-%m-%d %H:%M:%S'] for date_format in formats: @@ -48,6 +48,7 @@ def slugify(value): value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) return re.sub('[-\s]+', '-', value) + def copy(path, source, destination, destination_path=None, overwrite=False): """Copy path from origin to destination. @@ -57,8 +58,8 @@ def copy(path, source, destination, destination_path=None, overwrite=False): :param source: the source dir :param destination: the destination dir :param destination_path: the destination path (optional) - :param overwrite: wether to overwrite the destination if already exists or not - + :param overwrite: wether to overwrite the destination if already exists or + not """ if not destination_path: destination_path = path @@ -109,7 +110,8 @@ def truncate_html_words(s, num, end_text='...'): length = int(num) if length <= 0: return u'' - html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input') + html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', + 'hr', 'input') # Set up regular expressions re_words = re.compile(r'&.*?;|<.*?>|(\w[\w-]*)', re.U) @@ -147,8 +149,9 @@ def truncate_html_words(s, num, end_text='...'): except ValueError: pass else: - # SGML: An end tag closes, back to the matching start tag, all unclosed intervening start tags with omitted end tags - open_tags = open_tags[i+1:] + # SGML: An end tag closes, back to the matching start tag, + # all unclosed intervening start tags with omitted end tags + open_tags = open_tags[i + 1:] else: # Add it to the start of the open tags list open_tags.insert(0, tagname) @@ -195,7 +198,7 @@ def process_translations(content_list): default_lang_items = items[:1] if not slug: - warning('empty slug for %r' %( default_lang_items[0].filename,)) + warning('empty slug for %r' % (default_lang_items[0].filename,)) index.extend(default_lang_items) translations.extend(filter( lambda x: x not in default_lang_items, @@ -233,7 +236,8 @@ def files_changed(path, extensions): def set_date_tzinfo(d, tz_name=None): """ Date without tzinfo shoudbe utc. - This function set the right tz to date that aren't utc and don't have tzinfo + This function set the right tz to date that aren't utc and don't have + tzinfo. """ if tz_name is not None: tz = pytz.timezone(tz_name) diff --git a/pelican/writers.py b/pelican/writers.py index 814de40c..b27443be 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -8,7 +8,7 @@ import re from feedgenerator import Atom1Feed, Rss201rev2Feed from pelican.paginator import Paginator -from pelican.log import * +from pelican.log import info from pelican.utils import get_relative_path, set_date_tzinfo @@ -28,7 +28,6 @@ class Writer(object): description=context.get('SITESUBTITLE', '')) return feed - def _add_item_to_the_feed(self, feed, item): feed.add_item( @@ -44,8 +43,8 @@ class Writer(object): 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. + Return the feed. If no output_path or filename is specified, just + return the feed object. :param elements: the articles to put on the feed. :param context: the context to get the feed metadata. @@ -56,7 +55,7 @@ class Writer(object): locale.setlocale(locale.LC_ALL, 'C') try: self.site_url = context.get('SITEURL', get_relative_path(filename)) - self.feed_url= '%s/%s' % (self.site_url, filename) + self.feed_url = '%s/%s' % (self.site_url, filename) feed = self._create_new_feed(feed_type, context) @@ -132,7 +131,7 @@ class Writer(object): self.settings.get('DEFAULT_PAGINATION'), self.settings.get('DEFAULT_ORPHANS')) else: - paginators[key] = Paginator(object_list, len(object_list), 0) + paginators[key] = Paginator(object_list, len(object_list)) # generated pages, and write for page_num in range(paginators.values()[0].num_pages): @@ -140,9 +139,10 @@ class Writer(object): paginated_name = name for key in paginators.iterkeys(): paginator = paginators[key] - page = paginator.page(page_num+1) - paginated_localcontext.update({'%s_paginator' % key: paginator, - '%s_page' % key: page}) + page = paginator.page(page_num + 1) + paginated_localcontext.update( + {'%s_paginator' % key: paginator, + '%s_page' % key: page}) if page_num > 0: ext = '.' + paginated_name.rsplit('.')[-1] paginated_name = paginated_name.replace(ext, @@ -160,8 +160,8 @@ class Writer(object): relative paths. :param name: name of the file to output. - :param context: dict that will be passed to the templates, which need to - be updated. + :param context: dict that will be passed to the templates, which need + to be updated. """ def _update_content(name, input): """Change all the relatives paths of the input content to relatives @@ -184,9 +184,12 @@ class Writer(object): def replacer(m): relative_path = m.group('path') - dest_path = os.path.normpath( os.sep.join( (get_relative_path(name), - "static", relative_path) ) ) - return m.group('markup') + m.group('quote') + dest_path + m.group('quote') + dest_path = os.path.normpath( + os.sep.join((get_relative_path(name), "static", + relative_path))) + + return m.group('markup') + m.group('quote') + dest_path \ + + m.group('quote') return hrefs.sub(replacer, content) From 8a442e726a7e0fe1f52e1542a9eb6ab1e4b5942e Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 16:22:28 +0100 Subject: [PATCH 152/219] Don't specify the default when accessing settings. There is no need to do this, since all the default values are already provided in the default settings dict (in settings.py) --- pelican/contents.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index b408ff58..99740168 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -111,16 +111,14 @@ class Page(object): @property def url(self): if self.in_default_lang: - return self.settings.get('PAGE_URL', u'pages/{slug}.html').format(**self.url_format) - - return self.settings.get('PAGE_LANG_URL', u'pages/{slug}-{lang}.html').format(**self.url_format) + return self.settings['PAGE_URL'].format(**self.url_format) + return self.settings['PAGE_LANG_URL'].format(**self.url_format) @property def save_as(self): if self.in_default_lang: - return self.settings.get('PAGE_SAVE_AS', u'pages/{slug}.html').format(**self.url_format) - - return self.settings.get('PAGE_LANG_SAVE_AS', u'pages/{slug}-{lang}.html').format(**self.url_format) + return self.settings['PAGE_SAVE_AS'].format(**self.url_format) + return self.settings['PAGE_LANG_SAVE_AS'].format(**self.url_format) @property def content(self): @@ -148,21 +146,20 @@ class Article(Page): @property def url(self): if self.in_default_lang: - return self.settings.get('ARTICLE_URL', u'{slug}.html').format(**self.url_format) - - return self.settings.get('ARTICLE_LANG_URL', u'{slug}-{lang}.html').format(**self.url_format) + return self.settings['ARTICLE_URL'].format(**self.url_format) + return self.settings['ARTICLE_LANG_URL'].format(**self.url_format) @property def save_as(self): if self.in_default_lang: - return self.settings.get('ARTICLE_SAVE_AS', u'{slug}.html').format(**self.url_format) - - return self.settings.get('ARTICLE_LANG_SAVE_AS', u'{slug}-{lang}.html').format(**self.url_format) + return self.settings['ARTICLE_SAVE_AS'].format(**self.url_format) + return self.settings['ARTICLE_LANG_SAVE_AS'].format(**self.url_format) class Quote(Page): base_properties = ('author', 'date') + class URLWrapper(object): def __init__(self, name, settings): self.name = unicode(name) @@ -184,14 +181,16 @@ class URLWrapper(object): def url(self): return '%s.html' % self.name + class Category(URLWrapper): @property def url(self): - return self.settings.get('CATEGORY_URL', u'category/{name}.html').format(name=self.name) + return self.settings['CATEGORY_URL'].format(name=self.name) @property def save_as(self): - return self.settings.get('CATEGORY_SAVE_AS', u'category/{name}.html').format(name=self.name) + return self.settings['CATEGORY_SAVE_AS'].format(name=self.name) + class Tag(URLWrapper): def __init__(self, name, *args, **kwargs): @@ -199,20 +198,22 @@ class Tag(URLWrapper): @property def url(self): - return self.settings.get('TAG_URL', u'tag/{name}.html').format(name=self.name) + return self.settings['TAG_URL'].format(name=self.name) @property def save_as(self): - return self.settings.get('TAG_SAVE_AS', u'tag/{name}.html').format(name=self.name) + return self.settings['TAG_SAVE_AS'].format(name=self.name) + class Author(URLWrapper): @property def url(self): - return self.settings.get('AUTHOR_URL', u'author/{name}.html').format(name=self.name) + return self.settings['AUTHOR_URL'].format(name=self.name) @property def save_as(self): - return self.settings.get('AUTHOR_SAVE_AS', u'author/{name}.html').format(name=self.name) + return self.settings['AUTHOR_SAVE_AS'].format(name=self.name) + def is_valid_content(content, f): try: From 542b8b87058b73d25a713ffa62acb69afc97aff0 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 9 Mar 2012 16:27:23 +0100 Subject: [PATCH 153/219] add a 'coding standards' section in the contributors' guide --- docs/contribute.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/contribute.rst b/docs/contribute.rst index 84b99293..fcf8d5c0 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -38,3 +38,12 @@ The tests live in "pelican/tests" and you can run them using the "discover" feature of unittest2:: $ unit2 discover + +Coding standards +================ + +Try to respect what is described in the PEP8 +(http://www.python.org/dev/peps/pep-0008/) when providing patches. This can be +eased by the pep8 tool (http://pypi.python.org/pypi/pep8) or by Flake8, which +will give you some other cool hints about what's good or wrong +(http://pypi.python.org/pypi/flake8/) From 5e26062fd037f267206cbba40c06e271786131ff Mon Sep 17 00:00:00 2001 From: saghul Date: Sat, 10 Mar 2012 11:32:22 +0100 Subject: [PATCH 154/219] Fix for issue #233 --- pelican/generators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index ee95545e..5def01ab 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -36,8 +36,8 @@ class Generator(object): theme_path = os.path.join(os.path.dirname(os.path.abspath(__file__))) - simple_loader = FileSystemLoader(theme_path, - "themes", "simple", "templates") + simple_loader = FileSystemLoader(os.path.join(theme_path, + "themes", "simple", "templates")) self._env = Environment( loader=ChoiceLoader([ FileSystemLoader(self._templates_path), From a7ea166fd256b5a25a2942195a08437cf62d4ffe Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 10 Mar 2012 12:21:54 +0100 Subject: [PATCH 155/219] fix #233 --- pelican/generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/generators.py b/pelican/generators.py index 5def01ab..6ba12cf4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -34,7 +34,7 @@ class Generator(object): self._templates_path = os.path.expanduser( os.path.join(self.theme, 'templates')) - theme_path = os.path.join(os.path.dirname(os.path.abspath(__file__))) + theme_path = os.path.dirname(os.path.abspath(__file__)) simple_loader = FileSystemLoader(os.path.join(theme_path, "themes", "simple", "templates")) From fd1fbca520f06c10b8100c1272976cfe03c70f56 Mon Sep 17 00:00:00 2001 From: m-r-r Date: Sat, 10 Mar 2012 12:25:05 +0100 Subject: [PATCH 156/219] Removed small errors in pelican-quickstart --- tools/pelican_quickstart.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/pelican_quickstart.py b/tools/pelican_quickstart.py index 56c22f10..04fc20be 100755 --- a/tools/pelican_quickstart.py +++ b/tools/pelican_quickstart.py @@ -40,7 +40,7 @@ html: clean $$(OUTPUTDIR)/index.html \t@echo 'Done' $$(OUTPUTDIR)/%.html: -\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) +\t$$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS) clean: \trm -fr $$(OUTPUTDIR) @@ -94,7 +94,7 @@ DEFAULT_PAGINATION = $default_pagination CONF = { 'pelican' : 'pelican', - 'pelicanopts' : None, + 'pelicanopts' : '', 'basedir': '.', 'ftp_host': 'localhost', 'ftp_user': 'anonymous', @@ -103,7 +103,7 @@ CONF = { 'ssh_user': 'root', 'ssh_target_dir': '/var/www', 'dropbox_dir' : '~/Dropbox/Public/', - 'default_pagination' : 7, + 'default_pagination' : 10, 'lang': 'en' } From 8499ce3340d993fcc61d4d8268df739b9169ea20 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 10 Mar 2012 13:23:50 +0100 Subject: [PATCH 157/219] add some url and saveas settings that weren't present in the default settings dict --- pelican/settings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pelican/settings.py b/pelican/settings.py index fcabd518..6ed76f46 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -35,6 +35,12 @@ _DEFAULT_CONFIG = {'PATH': None, 'PAGE_SAVE_AS': 'pages/{slug}.html', 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', 'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html', + 'CATEGORY_URL': 'category/{name}.html', + 'CATEGORY_SAVE_AS': 'category/{name}.html', + 'TAG_URL': 'tag/{name}.html', + 'TAG_SAVE_AS': 'tag/{name}.html', + 'AUTHOR_URL': u'author/{name}.html', + 'AUTHOR_SAVE_AS': u'author/{name}.html', 'RELATIVE_URLS': True, 'DEFAULT_LANG': 'en', 'TAG_CLOUD_STEPS': 4, From 1c2f631723e12b414a5f2c50ac461fa1e81d0e2a Mon Sep 17 00:00:00 2001 From: draftcode Date: Sat, 10 Mar 2012 20:04:09 +0900 Subject: [PATCH 158/219] Avoid #226. --- tests/test_contents.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/test_contents.py b/tests/test_contents.py index b44d151f..e058e721 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import with_statement try: - from unittest2 import TestCase + from unittest2 import TestCase, skip except ImportError, e: - from unittest import TestCase + from unittest import TestCase, skip from pelican.contents import Page from pelican.settings import _DEFAULT_CONFIG @@ -94,6 +94,18 @@ class TestPage(TestCase): locale = 'ja_JP.utf8' page_kwargs['settings']['DATE_FORMATS'] = {'jp':(locale,'%Y-%m-%d(%a)')} page_kwargs['metadata']['lang'] = 'jp' - page = Page( **page_kwargs) - self.assertEqual(page.locale_date, u'2015-09-13(\u65e5)') - # above is unicode in Japanese: 2015-09-13() + + import locale as locale_module + try: + page = Page( **page_kwargs) + self.assertEqual(page.locale_date, u'2015-09-13(\u65e5)') + # above is unicode in Japanese: 2015-09-13() + except locale_module.Error: + # The constructor of ``Page`` will try to set the locale to + # ``ja_JP.utf8``. But this attempt will failed when there is no + # such locale in the system. You can see which locales there are + # in your system with ``locale -a`` command. + # + # Until we find some other method to test this functionality, we + # will simply skip this test. + skip("There is no locale %s in this system." % locale) From 1194764ed1ddcce797fb1895668ccc5e97137c93 Mon Sep 17 00:00:00 2001 From: draftcode Date: Sat, 10 Mar 2012 21:18:01 +0900 Subject: [PATCH 159/219] Do not create feeds when their filenames are set to None. --- docs/settings.rst | 4 +++- pelican/generators.py | 47 ++++++++++++++++++++++------------------ tests/test_generators.py | 36 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 tests/test_generators.py diff --git a/docs/settings.rst b/docs/settings.rst index 69e2adc8..b08b5bcb 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -199,7 +199,6 @@ Pelican generates category feeds as well as feeds for all your articles. It does not generate feeds for tags by default, but it is possible to do so using the ``TAG_FEED`` and ``TAG_FEED_RSS`` settings: - ================================================ ===================================================== Setting name (default value) What does it do? ================================================ ===================================================== @@ -214,6 +213,9 @@ Setting name (default value) What does it do? quantity is unrestricted by default. ================================================ ===================================================== +If you don't want to generate some of these feeds, set ``None`` to the +variables above. + .. [2] %s is the name of the category. Pagination diff --git a/pelican/generators.py b/pelican/generators.py index 6ba12cf4..ccfdb39f 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -118,41 +118,46 @@ class ArticlesGenerator(Generator): 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: + if self.settings.get('FEED'): writer.write_feed(self.articles, self.context, - self.settings['FEED_RSS'], feed_type='rss') + self.settings['FEED']) + + if self.settings.get('FEED_RSS'): + writer.write_feed(self.articles, self.context, + self.settings['FEED_RSS'], feed_type='rss') for cat, arts in self.categories: 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: + if self.settings.get('CATEGORY_FEED'): writer.write_feed(arts, self.context, - self.settings['CATEGORY_FEED_RSS'] % cat, - feed_type='rss') + self.settings['CATEGORY_FEED'] % cat) - if 'TAG_FEED' in self.settings: + if self.settings.get('CATEGORY_FEED_RSS'): + writer.write_feed(arts, self.context, + self.settings['CATEGORY_FEED_RSS'] % cat, + feed_type='rss') + + if self.settings.get('TAG_FEED') or self.settings.get('TAG_FEED_RSS'): 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 self.settings.get('TAG_FEED'): + writer.write_feed(arts, self.context, + self.settings['TAG_FEED'] % tag) - if 'TAG_FEED_RSS' in self.settings: + if self.settings.get('TAG_FEED_RSS'): writer.write_feed(arts, self.context, self.settings['TAG_FEED_RSS'] % tag, feed_type='rss') - translations_feeds = defaultdict(list) - for article in chain(self.articles, self.translations): - translations_feeds[article.lang].append(article) + if self.settings.get('TRANSLATION_FEED'): + translations_feeds = defaultdict(list) + for article in chain(self.articles, self.translations): + translations_feeds[article.lang].append(article) - for lang, items in translations_feeds.items(): - items.sort(key=attrgetter('date'), reverse=True) - writer.write_feed(items, self.context, - self.settings['TRANSLATION_FEED'] % lang) + for lang, items in translations_feeds.items(): + items.sort(key=attrgetter('date'), reverse=True) + writer.write_feed(items, self.context, + self.settings['TRANSLATION_FEED'] % lang) def generate_pages(self, writer): """Generate the pages on the disk""" diff --git a/tests/test_generators.py b/tests/test_generators.py new file mode 100644 index 00000000..94088c34 --- /dev/null +++ b/tests/test_generators.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import with_statement +try: + from unittest2 import TestCase +except ImportError, e: + from unittest import TestCase + +from pelican.generators import ArticlesGenerator +from pelican.settings import _DEFAULT_CONFIG + +class TestArticlesGenerator(TestCase): + + def test_generate_feeds(self): + + class FakeWriter(object): + def __init__(self): + self.called = False + + def write_feed(self, *args, **kwargs): + self.called = True + + generator = ArticlesGenerator(None, {'FEED': _DEFAULT_CONFIG['FEED']}, + None, _DEFAULT_CONFIG['THEME'], None, + None) + writer = FakeWriter() + generator.generate_feeds(writer) + assert writer.called, ("The feed should be written, " + "if settings['FEED'] is specified.") + + generator = ArticlesGenerator(None, {'FEED': None}, None, + _DEFAULT_CONFIG['THEME'], None, None) + writer = FakeWriter() + generator.generate_feeds(writer) + assert not writer.called, ("If settings['FEED'] is None, " + "the feed should not be generated.") + From 3bdc769134a57d6ce62f2d80b095061d91bacb2f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 01:14:22 +0100 Subject: [PATCH 160/219] Factorize some code about URL wrapping. --- pelican/contents.py | 69 +++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 99740168..900061ad 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -2,6 +2,7 @@ from datetime import datetime from os import getenv from sys import platform, stdin +import functools import locale from pelican.log import warning, error @@ -108,17 +109,13 @@ class Page(object): 'category': getattr(self, 'category', 'misc'), } - @property - def url(self): - if self.in_default_lang: - return self.settings['PAGE_URL'].format(**self.url_format) - return self.settings['PAGE_LANG_URL'].format(**self.url_format) + def _expand_settings(self, key): + fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper() + return self.settings[fq_key].format(**self.url_format) - @property - def save_as(self): - if self.in_default_lang: - return self.settings['PAGE_SAVE_AS'].format(**self.url_format) - return self.settings['PAGE_LANG_SAVE_AS'].format(**self.url_format) + def get_url_setting(self, key): + key = key if self.in_default_lang else 'lang_%s' % key + return self._expand_settings(key) @property def content(self): @@ -139,22 +136,13 @@ class Page(object): summary = property(_get_summary, _set_summary, "Summary of the article." "Based on the content. Can't be set") + url = property(functools.partial(get_url_setting, key='url')) + save_as = property(functools.partial(get_url_setting, key='save_as')) + class Article(Page): mandatory_properties = ('title', 'date', 'category') - @property - def url(self): - if self.in_default_lang: - return self.settings['ARTICLE_URL'].format(**self.url_format) - return self.settings['ARTICLE_LANG_URL'].format(**self.url_format) - - @property - def save_as(self): - if self.in_default_lang: - return self.settings['ARTICLE_SAVE_AS'].format(**self.url_format) - return self.settings['ARTICLE_LANG_SAVE_AS'].format(**self.url_format) - class Quote(Page): base_properties = ('author', 'date') @@ -163,8 +151,12 @@ class Quote(Page): class URLWrapper(object): def __init__(self, name, settings): self.name = unicode(name) + self.slug = slugify(self.name) self.settings = settings + def as_dict(self): + return self.__dict__ + def __hash__(self): return hash(self.name) @@ -177,42 +169,25 @@ class URLWrapper(object): def __unicode__(self): return self.name - @property - def url(self): - return '%s.html' % self.name + def _from_settings(self, key): + setting = "%s_%s" % (self.__class__.__name__.upper(), key) + return self.settings[setting].format(**self.as_dict()) + + url = property(functools.partial(_from_settings, key='URL')) + save_as = property(functools.partial(_from_settings, key='SAVE_AS')) class Category(URLWrapper): - @property - def url(self): - return self.settings['CATEGORY_URL'].format(name=self.name) - - @property - def save_as(self): - return self.settings['CATEGORY_SAVE_AS'].format(name=self.name) + pass class Tag(URLWrapper): def __init__(self, name, *args, **kwargs): super(Tag, self).__init__(unicode.strip(name), *args, **kwargs) - @property - def url(self): - return self.settings['TAG_URL'].format(name=self.name) - - @property - def save_as(self): - return self.settings['TAG_SAVE_AS'].format(name=self.name) - class Author(URLWrapper): - @property - def url(self): - return self.settings['AUTHOR_URL'].format(name=self.name) - - @property - def save_as(self): - return self.settings['AUTHOR_SAVE_AS'].format(name=self.name) + pass def is_valid_content(content, f): From 1ec2779f5e4470c6ed19b56d16185c6174ab520c Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 01:16:02 +0100 Subject: [PATCH 161/219] Make the readers tests a bit more verbose. --- tests/test_readers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_readers.py b/tests/test_readers.py index 120b3125..2d023462 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -1,8 +1,8 @@ # coding: utf-8 try: - import unittest2 + import unittest2 as unittest except ImportError, e: - import unittest as unittest2 + import unittest import datetime import os @@ -12,11 +12,12 @@ from pelican import readers CUR_DIR = os.path.dirname(__file__) CONTENT_PATH = os.path.join(CUR_DIR, 'content') + def _filename(*args): return os.path.join(CONTENT_PATH, *args) -class RstReaderTest(unittest2.TestCase): +class RstReaderTest(unittest.TestCase): def test_article_with_metadata(self): reader = readers.RstReader({}) @@ -29,4 +30,6 @@ class RstReaderTest(unittest2.TestCase): 'date': datetime.datetime(2010, 12, 2, 10, 14), 'tags': ['foo', 'bar', 'foobar'], } - self.assertDictEqual(metadata, expected) + + for key, value in expected.items(): + self.assertEquals(value, metadata[key], key) From 1c219d14bb36ea4f12dda3f987a7c843ba2cec40 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 01:16:32 +0100 Subject: [PATCH 162/219] Use the slug as default URL for tags and authors. --- pelican/settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pelican/settings.py b/pelican/settings.py index 6ed76f46..d62acf42 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -37,10 +37,10 @@ _DEFAULT_CONFIG = {'PATH': None, 'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html', 'CATEGORY_URL': 'category/{name}.html', 'CATEGORY_SAVE_AS': 'category/{name}.html', - 'TAG_URL': 'tag/{name}.html', - 'TAG_SAVE_AS': 'tag/{name}.html', - 'AUTHOR_URL': u'author/{name}.html', - 'AUTHOR_SAVE_AS': u'author/{name}.html', + 'TAG_URL': 'tag/{slug}.html', + 'TAG_SAVE_AS': 'tag/{slug}.html', + 'AUTHOR_URL': u'author/{slug}.html', + 'AUTHOR_SAVE_AS': u'author/{slug}.html', 'RELATIVE_URLS': True, 'DEFAULT_LANG': 'en', 'TAG_CLOUD_STEPS': 4, From 0ca9997e107f67fc4e8eebd0511809e254b11f58 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 6 Mar 2012 00:29:56 +0100 Subject: [PATCH 163/219] paths for finding articles and pages are now parametrable --- pelican/generators.py | 10 ++++++---- pelican/settings.py | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 6ba12cf4..ac2cd865 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -211,10 +211,10 @@ class ArticlesGenerator(Generator): def generate_context(self): """change the context""" - # return the list of files to use - files = self.get_files(self.path, exclude=['pages', ]) all_articles = [] - for f in files: + for f in self.get_files( + os.path.join(self.path, self.settings['ARTICLE_DIR']), + exclude=self.settings['ARTICLE_EXCLUDES']): try: content, metadata = read_file(f, settings=self.settings) except Exception, e: @@ -316,7 +316,9 @@ class PagesGenerator(Generator): def generate_context(self): all_pages = [] - for f in self.get_files(os.sep.join((self.path, 'pages'))): + for f in self.get_files( + os.path.join(self.path, self.settings['PAGE_DIR']), + exclude=self.settings['PAGE_EXCLUDES']): try: content, metadata = read_file(f) except Exception, e: diff --git a/pelican/settings.py b/pelican/settings.py index 6ed76f46..1b31582f 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -8,6 +8,10 @@ from pelican import log DEFAULT_THEME = os.sep.join([os.path.dirname(os.path.abspath(__file__)), "themes/notmyidea"]) _DEFAULT_CONFIG = {'PATH': None, + 'ARTICLE_DIR': '', + 'ARTICLE_EXCLUDES': ('pages',), + 'PAGE_DIR': 'pages', + 'PAGE_EXCLUDES': (), 'THEME': DEFAULT_THEME, 'OUTPUT_PATH': 'output/', 'MARKUP': ('rst', 'md'), From aef7418bdf2094b8e6edc5b2e6300bf8a60bb851 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 6 Mar 2012 00:45:22 +0100 Subject: [PATCH 164/219] add docs for new page/article paths settings --- docs/settings.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index 69e2adc8..31183be3 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -52,6 +52,10 @@ Setting name (default value) What does it do? supported extensions. `OUTPUT_PATH` (``'output/'``) Where to output the generated files. `PATH` (``None``) Path to look at for input files. +`PAGE_DIR' (``'pages'``) Directory to look at for pages. +`PAGE_EXCLUDES' (``()``) A list of directories to exclude when looking for pages. +`ARTICLE_DIR' (``''``) Directory to look at for articles. +`ARTICLE_EXCLUDES': (``('pages',)``) A list of directories to exclude when looking for articles. `PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions of your documents. You will need to install `rst2pdf`. From d6be2fb44cfb39afc41bf4a3ce1dd842d549da06 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 01:59:04 +0100 Subject: [PATCH 165/219] Put deprecation code in a separate place --- pelican/__init__.py | 83 +++++++++++++++++++++++++-------------------- pelican/settings.py | 2 +- 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 8de68d69..dcdbdcb6 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -6,7 +6,7 @@ import time from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, PdfGenerator) -from pelican.settings import read_settings +from pelican.settings import read_settings, _DEFAULT_CONFIG from pelican.utils import clean_output_dir, files_changed from pelican.writers import Writer from pelican import log @@ -20,6 +20,9 @@ class Pelican(object): """Read the settings, and performs some checks on the environment before doing anything else. """ + if settings is None: + settings = _DEFAULT_CONFIG + self.path = path or settings['PATH'] if not self.path: raise Exception('you need to specify a path containing the content' @@ -28,44 +31,11 @@ class Pelican(object): if self.path.endswith('/'): self.path = self.path[:-1] - if settings.get('CLEAN_URLS', False): - log.warning('Found deprecated `CLEAN_URLS` in settings. Modifing' - ' the following settings for the same behaviour.') - - settings['ARTICLE_URL'] = '{slug}/' - settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/' - settings['PAGE_URL'] = 'pages/{slug}/' - settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/' - - for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', - 'PAGE_LANG_URL'): - log.warning("%s = '%s'" % (setting, settings[setting])) - - if settings.get('ARTICLE_PERMALINK_STRUCTURE', False): - log.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' - ' settings. Modifing the following settings for' - ' the same behaviour.') - - structure = settings['ARTICLE_PERMALINK_STRUCTURE'] - - # Convert %(variable) into {variable}. - structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure) - - # Convert %x into {date:%x} for strftime - structure = re.sub('(%[A-z])', '{date:\g<1>}', structure) - - # Strip a / prefix - structure = re.sub('^/', '', structure) - - for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', - 'PAGE_LANG_URL', 'ARTICLE_SAVE_AS', - 'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS', - 'PAGE_LANG_SAVE_AS'): - settings[setting] = os.path.join(structure, settings[setting]) - log.warning("%s = '%s'" % (setting, settings[setting])) - # define the default settings self.settings = settings + + self._handle_deprecation() + self.theme = theme or settings['THEME'] output_path = output_path or settings['OUTPUT_PATH'] self.output_path = os.path.realpath(output_path) @@ -82,6 +52,45 @@ class Pelican(object): else: raise Exception("Impossible to find the theme %s" % theme) + def _handle_deprecation(self): + + if self.settings.get('CLEAN_URLS', False): + log.warning('Found deprecated `CLEAN_URLS` in settings. Modifing' + ' the following settings for the same behaviour.') + + self.settings['ARTICLE_URL'] = '{slug}/' + self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/' + self.settings['PAGE_URL'] = 'pages/{slug}/' + self.settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/' + + for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', + 'PAGE_LANG_URL'): + log.warning("%s = '%s'" % (setting, self.settings[setting])) + + if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False): + log.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' + ' settings. Modifing the following settings for' + ' the same behaviour.') + + structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] + + # Convert %(variable) into {variable}. + structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure) + + # Convert %x into {date:%x} for strftime + structure = re.sub('(%[A-z])', '{date:\g<1>}', structure) + + # Strip a / prefix + structure = re.sub('^/', '', structure) + + for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', + 'PAGE_LANG_URL', 'ARTICLE_SAVE_AS', + 'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS', + 'PAGE_LANG_SAVE_AS'): + self.settings[setting] = os.path.join(structure, + self.settings[setting]) + log.warning("%s = '%s'" % (setting, self.settings[setting])) + def run(self): """Run the generators and return""" diff --git a/pelican/settings.py b/pelican/settings.py index d62acf42..bfc8e940 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -61,7 +61,7 @@ _DEFAULT_CONFIG = {'PATH': None, } -def read_settings(filename): +def read_settings(filename=None): """Load a Python file into a dictionary. """ context = _DEFAULT_CONFIG.copy() From fbf89687cc05c0656e7efc69befde6c790ec66b4 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 01:59:58 +0100 Subject: [PATCH 166/219] start functional testing --- tests/support.py | 18 ++++++++++++++++++ tests/test_pelican.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/support.py create mode 100644 tests/test_pelican.py diff --git a/tests/support.py b/tests/support.py new file mode 100644 index 00000000..0cd757a6 --- /dev/null +++ b/tests/support.py @@ -0,0 +1,18 @@ +from contextlib import contextmanager + +from tempfile import mkdtemp +from shutil import rmtree + + +@contextmanager +def temporary_folder(): + """creates a temporary folder, return it and delete it afterwards. + + This allows to do something like this in tests: + + >>> with temporary_folder() as d: + # do whatever you want + """ + tempdir = mkdtemp() + yield tempdir + rmtree(tempdir) diff --git a/tests/test_pelican.py b/tests/test_pelican.py new file mode 100644 index 00000000..dce4fadc --- /dev/null +++ b/tests/test_pelican.py @@ -0,0 +1,31 @@ +import unittest +import os + +from support import temporary_folder + +from pelican import Pelican +from pelican.settings import read_settings + +SAMPLES_PATH = os.path.abspath(os.sep.join( + (os.path.dirname(os.path.abspath(__file__)), "..", "samples"))) + +INPUT_PATH = os.path.join(SAMPLES_PATH, "content") +SAMPLE_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf.py") + + +class TestPelican(unittest.TestCase): + # general functional testing for pelican. Basically, this test case tries + # to run pelican in different situations and see how it behaves + + def test_basic_generation_works(self): + # when running pelican without settings, it should pick up the default + # ones and generate the output without raising any exception / issuing + # any warning. + with temporary_folder() as temp_path: + pelican = Pelican(path=INPUT_PATH, output_path=temp_path) + pelican.run() + + # the same thing with a specified set of settins should work + with temporary_folder() as temp_path: + pelican = Pelican(path=INPUT_PATH, output_path=temp_path, + settings=read_settings(SAMPLE_CONFIG)) From d43bd1dcb80801dfabfba661afa81a790816ee28 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 02:48:36 +0100 Subject: [PATCH 167/219] Add a way to use Typogrify to enhance the generated HTML. --- docs/settings.rst | 5 +++++ pelican/readers.py | 14 +++++++++++++- pelican/settings.py | 3 ++- tests/content/article.rst | 4 ++++ tests/test_readers.py | 17 +++++++++++++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/content/article.rst diff --git a/docs/settings.rst b/docs/settings.rst index 69e2adc8..6780c6ae 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -69,6 +69,11 @@ Setting name (default value) What does it do? `TIMEZONE` The timezone used in the date information, to generate Atom and RSS feeds. See the "timezone" section below for more info. +`TYPOGRIFY` (``False``) If set to true, some + additional transformations will be done on the + generated HTML, using the `Typogrify + `_ + library ================================================ ===================================================== .. [#] Default is the system locale. diff --git a/pelican/readers.py b/pelican/readers.py index 5bbbfb30..a581e458 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -143,12 +143,24 @@ def read_file(filename, fmt=None, settings=None): """Return a reader object using the given format.""" if not fmt: fmt = filename.split('.')[-1] + if fmt not in _EXTENSIONS.keys(): raise TypeError('Pelican does not know how to parse %s' % filename) + reader = _EXTENSIONS[fmt](settings) settings_key = '%s_EXTENSIONS' % fmt.upper() + if settings and settings_key in settings: reader.extensions = settings[settings_key] + if not reader.enabled: raise ValueError("Missing dependencies for %s" % fmt) - return reader.read(filename) + + content, metadata = reader.read(filename) + + # eventually filter the content with typogrify if asked so + if settings and settings['TYPOGRIFY']: + from typogrify import Typogrify + content = Typogrify.typogrify(content) + + return content, metadata diff --git a/pelican/settings.py b/pelican/settings.py index bfc8e940..8cb06e90 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -57,7 +57,8 @@ _DEFAULT_CONFIG = {'PATH': None, 'DEFAULT_METADATA': (), 'FILES_TO_COPY': (), 'DEFAULT_STATUS': 'published', - 'ARTICLE_PERMALINK_STRUCTURE': '' + 'ARTICLE_PERMALINK_STRUCTURE': '', + 'TYPOGRIFY': False, } diff --git a/tests/content/article.rst b/tests/content/article.rst new file mode 100644 index 00000000..1707ab03 --- /dev/null +++ b/tests/content/article.rst @@ -0,0 +1,4 @@ +Article title +############# + +This is some content. With some stuff to "typogrify". diff --git a/tests/test_readers.py b/tests/test_readers.py index 2d023462..d4f0aecf 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -33,3 +33,20 @@ class RstReaderTest(unittest.TestCase): for key, value in expected.items(): self.assertEquals(value, metadata[key], key) + + def test_typogrify(self): + # if nothing is specified in the settings, the content should be + # unmodified + content, _ = readers.read_file(_filename('article.rst')) + expected = "

    This is some content. With some stuff to "\ + ""typogrify".

    \n" + + self.assertEqual(content, expected) + + # otherwise, typogrify should be applied + content, _ = readers.read_file(_filename('article.rst'), + settings={'TYPOGRIFY': True}) + expected = "

    This is some content. With some stuff to "\ + "“typogrify”.

    \n" + + self.assertEqual(content, expected) From 6a4f4a55b421f71acfb915d3e3b3027b5934322f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 02:52:40 +0100 Subject: [PATCH 168/219] updated the changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index e68c6f0d..ef9bc070 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ X.X * Refactored the way URL are handled. * Improved the english documentation * Fixed packaging using setuptools entrypoints +* Added typogrify support 2.8 From cfd050b0f29928dd31e4a994c58566f49e23d2c2 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 03:02:12 +0100 Subject: [PATCH 169/219] Add a CSS file for typogrify on the notmyidea theme --- pelican/themes/notmyidea/static/css/main.css | 1 + pelican/themes/notmyidea/static/css/typogrify.css | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 pelican/themes/notmyidea/static/css/typogrify.css diff --git a/pelican/themes/notmyidea/static/css/main.css b/pelican/themes/notmyidea/static/css/main.css index b3677771..7534790f 100644 --- a/pelican/themes/notmyidea/static/css/main.css +++ b/pelican/themes/notmyidea/static/css/main.css @@ -10,6 +10,7 @@ /* Imports */ @import url("reset.css"); @import url("pygment.css"); +@import url("typogrify.css"); @import url(http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin); /***** Global *****/ diff --git a/pelican/themes/notmyidea/static/css/typogrify.css b/pelican/themes/notmyidea/static/css/typogrify.css new file mode 100644 index 00000000..c9b34dc8 --- /dev/null +++ b/pelican/themes/notmyidea/static/css/typogrify.css @@ -0,0 +1,3 @@ +.caps {font-size:.92em;} +.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;} +.dquo {margin-left:-.38em;} From 3c983d62c9ef426e9d08e9a7f3f05ae9090cb304 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 11:25:30 +0100 Subject: [PATCH 170/219] change the tests to use the mock library instead of a custom mocking system --- CHANGELOG | 1 + tests/test_generators.py | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ef9bc070..46aa68a5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ X.X * Improved the english documentation * Fixed packaging using setuptools entrypoints * Added typogrify support +* Added a way to disable feed generation 2.8 diff --git a/tests/test_generators.py b/tests/test_generators.py index 94088c34..20929622 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -1,36 +1,28 @@ # -*- coding: utf-8 -*- -from __future__ import with_statement try: - from unittest2 import TestCase + import unittest2 as unittest except ImportError, e: - from unittest import TestCase + import unittest # NOQA from pelican.generators import ArticlesGenerator from pelican.settings import _DEFAULT_CONFIG -class TestArticlesGenerator(TestCase): +from mock import MagicMock + + +class TestArticlesGenerator(unittest.TestCase): def test_generate_feeds(self): - class FakeWriter(object): - def __init__(self): - self.called = False - - def write_feed(self, *args, **kwargs): - self.called = True - generator = ArticlesGenerator(None, {'FEED': _DEFAULT_CONFIG['FEED']}, None, _DEFAULT_CONFIG['THEME'], None, None) - writer = FakeWriter() + writer = MagicMock() generator.generate_feeds(writer) - assert writer.called, ("The feed should be written, " - "if settings['FEED'] is specified.") + writer.write_feed.assert_called_with([], None, 'feeds/all.atom.xml') generator = ArticlesGenerator(None, {'FEED': None}, None, _DEFAULT_CONFIG['THEME'], None, None) - writer = FakeWriter() + writer = MagicMock() generator.generate_feeds(writer) - assert not writer.called, ("If settings['FEED'] is None, " - "the feed should not be generated.") - + self.assertFalse(writer.write_feed.called) From c393b011c43fb359844484b5e7dcf971e00e7226 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 15:50:53 +0100 Subject: [PATCH 171/219] cleaning --- pelican/utils.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index 93541cc0..eead1ac9 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -169,13 +169,11 @@ def truncate_html_words(s, num, end_text='...'): def process_translations(content_list): - """ Finds all translation and returns - tuple with two lists (index, translations). - Index list includes items in default language - or items which have no variant in default language. + """ Finds all translation and returns tuple with two lists (index, + translations). Index list includes items in default language or items + which have no variant in default language. - Also, for each content_list item, it - sets attribute 'translations' + Also, for each content_list item, it sets attribute 'translations' """ content_list.sort(key=attrgetter('slug')) grouped_by_slugs = groupby(content_list, attrgetter('slug')) @@ -185,10 +183,7 @@ def process_translations(content_list): for slug, items in grouped_by_slugs: items = list(items) # find items with default language - default_lang_items = filter( - attrgetter('in_default_lang'), - items - ) + default_lang_items = filter(attrgetter('in_default_lang'), items) len_ = len(default_lang_items) if len_ > 1: warning(u'there are %s variants of "%s"' % (len_, slug)) From 32355f546373a1dcc4faa4d426f71c4552642956 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 15:51:48 +0100 Subject: [PATCH 172/219] add some more tests for the utils module --- tests/support.py | 10 +++++- tests/test_utils.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/test_utils.py diff --git a/tests/support.py b/tests/support.py index 0cd757a6..5829fe78 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,8 +1,9 @@ from contextlib import contextmanager - from tempfile import mkdtemp from shutil import rmtree +from pelican.contents import Article + @contextmanager def temporary_folder(): @@ -16,3 +17,10 @@ def temporary_folder(): tempdir = mkdtemp() yield tempdir rmtree(tempdir) + + +def get_article(title, slug, content, lang, extra_metadata=None): + metadata = {'slug': slug, 'title': title, 'lang': lang} + if extra_metadata is not None: + metadata.update(extra_metadata) + return Article(content, metadata=metadata) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..9654825e --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +try: + import unittest2 as unittest +except ImportError: + import unittest # NOQA +import datetime + +from pelican import utils +from pelican.contents import Article + +from support import get_article + + +class TestUtils(unittest.TestCase): + + def test_get_date(self): + # valid ones + date = datetime.datetime(year=2012, month=11, day=22) + date_hour = datetime.datetime(year=2012, month=11, day=22, hour=22, + minute=11) + date_hour_sec = datetime.datetime(year=2012, month=11, day=22, hour=22, + minute=11, second=10) + dates = {'2012-11-22': date, + '2012/11/22': date, + '2012-11-22 22:11': date_hour, + '2012/11/22 22:11': date_hour, + '22-11-2012': date, + '22/11/2012': date, + '22.11.2012': date, + '2012-22-11': date, + '22.11.2012 22:11': date_hour, + '2012-11-22 22:11:10': date_hour_sec} + + for value, expected in dates.items(): + self.assertEquals(utils.get_date(value), expected, value) + + # invalid ones + invalid_dates = ('2010-110-12', 'yay') + for item in invalid_dates: + self.assertRaises(ValueError, utils.get_date, item) + + def test_slugify(self): + + samples = (('this is a test', 'this-is-a-test'), + ('this is a test', 'this-is-a-test'), + (u'this → is ← a ↑ test', 'this-is-a-test'), + ('this--is---a test', 'this-is-a-test')) + + for value, expected in samples: + self.assertEquals(utils.slugify(value), expected) + + def test_get_relative_path(self): + + samples = (('/test/test', '../../.'), + ('/test/test/', '../../../.'), + ('/', '../.')) + + for value, expected in samples: + self.assertEquals(utils.get_relative_path(value), expected) + + def test_process_translations(self): + # create a bunch of articles + fr_article1 = get_article(lang='fr', slug='yay', title='Un titre', + content='en français') + en_article1 = get_article(lang='en', slug='yay', title='A title', + content='in english') + + articles = [fr_article1, en_article1] + + index, trans = utils.process_translations(articles) + + self.assertIn(en_article1, index) + self.assertIn(fr_article1, trans) + self.assertNotIn(en_article1, trans) + self.assertNotIn(fr_article1, index) From 48b318d29e8ba9d84897997ee5d1982b574afb6a Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 17:05:46 +0100 Subject: [PATCH 173/219] skip typogrify if not installed --- tests/test_readers.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/test_readers.py b/tests/test_readers.py index d4f0aecf..c0b8cc41 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -26,7 +26,8 @@ class RstReaderTest(unittest.TestCase): 'category': 'yeah', 'author': u'Alexis Métaireau', 'title': 'This is a super article !', - 'summary': 'Multi-line metadata should be supported\nas well as inline markup.', + 'summary': 'Multi-line metadata should be supported\nas well as'\ + ' inline markup.', 'date': datetime.datetime(2010, 12, 2, 10, 14), 'tags': ['foo', 'bar', 'foobar'], } @@ -43,10 +44,13 @@ class RstReaderTest(unittest.TestCase): self.assertEqual(content, expected) - # otherwise, typogrify should be applied - content, _ = readers.read_file(_filename('article.rst'), - settings={'TYPOGRIFY': True}) - expected = "

    This is some content. With some stuff to "\ - "“typogrify”.

    \n" + try: + # otherwise, typogrify should be applied + content, _ = readers.read_file(_filename('article.rst'), + settings={'TYPOGRIFY': True}) + expected = "

    This is some content. With some stuff to "\ + "“typogrify”.

    \n" - self.assertEqual(content, expected) + self.assertEqual(content, expected) + except ImportError: + return unittest.skip('need the typogrify distribution') From 912b1dbc1a25a9a5079b21649a8f0aa8b3db3b68 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 17:05:59 +0100 Subject: [PATCH 174/219] test travis-ci --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..bb1f5af1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: python +python: + - "2.6" + - "2.7" +install: + - pip install nose --use-mirrors + - pip install . --use-mirrors +script: nosetests -s tests From d42b6d9ad7682c55e5beae9d90f9f6607cfc9b65 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 17:20:04 +0100 Subject: [PATCH 175/219] fix nose --- tox.ini | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 462abf09..e1ca32f2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,13 @@ [tox] -envlist = py25,py26,py27 +envlist = py26,py27 [testenv] -commands=py.test +commands = nosetests -s tests deps = + nose Jinja2 Pygments docutils feedgenerator unittest2 - pytest + mock From 2827a6df47e2c9a08f286183dcb3b5323c98b1b7 Mon Sep 17 00:00:00 2001 From: draftcode Date: Mon, 12 Mar 2012 01:22:54 +0900 Subject: [PATCH 176/219] Fixed some typos. --- pelican/__init__.py | 20 ++++++++++---------- pelican/contents.py | 2 +- pelican/utils.py | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index dcdbdcb6..0b53dbcc 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -25,7 +25,7 @@ class Pelican(object): self.path = path or settings['PATH'] if not self.path: - raise Exception('you need to specify a path containing the content' + raise Exception('You need to specify a path containing the content' ' (see pelican --help for more information)') if self.path.endswith('/'): @@ -138,7 +138,7 @@ def main(): static blog, with restructured text input files.""") parser.add_argument(dest='path', nargs='?', - help='Path where to find the content files') + 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.') @@ -146,28 +146,28 @@ def main(): 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=None, dest='markup', - help='the list of markup language to use (rst or md). Please indicate ' - 'them separated by commas') + help='The list of markup language to use (rst or md). Please indicate ' + 'them separated by commas.') parser.add_argument('-s', '--settings', dest='settings', default='', - help='the settings of the application. Default to False.') + help='The settings of the application. Default to False.') parser.add_argument('-d', '--delete-output-directory', dest='delete_outputdir', action='store_true', help='Delete the output directory.') parser.add_argument('-v', '--verbose', action='store_const', const=log.INFO, dest='verbosity', - help='Show all messages') + help='Show all messages.') parser.add_argument('-q', '--quiet', action='store_const', const=log.CRITICAL, dest='verbosity', - help='Show only critical errors') + help='Show only critical errors.') parser.add_argument('-D', '--debug', action='store_const', const=log.DEBUG, dest='verbosity', - help='Show all message, including debug messages') + help='Show all message, including debug messages.') parser.add_argument('--version', action='version', version=__version__, - help='Print the pelican version and exit') + help='Print the pelican version and exit.') parser.add_argument('-r', '--autoreload', dest='autoreload', action='store_true', help="Relaunch pelican each time a modification occurs" - " on the content files") + " on the content files.") args = parser.parse_args() log.init(args.verbosity) diff --git a/pelican/contents.py b/pelican/contents.py index 900061ad..4f424461 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -43,7 +43,7 @@ class Page(object): self.author = Author(settings['AUTHOR'], settings) else: self.author = Author(getenv('USER', 'John Doe'), settings) - warning(u"Author of `{0}' unknow, assuming that his name is " + warning(u"Author of `{0}' unknown, assuming that his name is " "`{1}'".format(filename or self.title, self.author)) # manage languages diff --git a/pelican/utils.py b/pelican/utils.py index eead1ac9..1b84f108 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -14,7 +14,7 @@ from pelican.log import warning, info def get_date(string): """Return a datetime object from a string. - If no format matches the given date, raise a ValuEerror + If no format matches the given date, raise a ValueError. """ string = re.sub(' +', ' ', string) formats = ['%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M', @@ -58,8 +58,8 @@ def copy(path, source, destination, destination_path=None, overwrite=False): :param source: the source dir :param destination: the destination dir :param destination_path: the destination path (optional) - :param overwrite: wether to overwrite the destination if already exists or - not + :param overwrite: whether to overwrite the destination if already exists + or not """ if not destination_path: destination_path = path From 3cb18303f6203f84b32e2f4c8bdf48430d7311ff Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 17:26:59 +0100 Subject: [PATCH 177/219] fix python 2.6 support --- tests/test_contents.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/test_contents.py b/tests/test_contents.py index e058e721..ed9885b6 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -3,18 +3,19 @@ from __future__ import with_statement try: from unittest2 import TestCase, skip except ImportError, e: - from unittest import TestCase, skip + from unittest import TestCase, skip # NOQA from pelican.contents import Page from pelican.settings import _DEFAULT_CONFIG + class TestPage(TestCase): def setUp(self): super(TestPage, self).setUp() self.page_kwargs = { 'content': 'content', - 'metadata':{ + 'metadata': { 'title': 'foo bar', 'author': 'Blogger', }, @@ -72,32 +73,38 @@ class TestPage(TestCase): """ from datetime import datetime from sys import platform - dt = datetime(2015,9,13) + dt = datetime(2015, 9, 13) # make a deep copy of page_kawgs - page_kwargs = {key:self.page_kwargs[key] for key in self.page_kwargs} + page_kwargs = dict([(key, self.page_kwargs[key]) for key in + self.page_kwargs]) for key in page_kwargs: - if not isinstance(page_kwargs[key], dict): break - page_kwargs[key] = {subkey:page_kwargs[key][subkey] for subkey in page_kwargs[key]} + if not isinstance(page_kwargs[key], dict): + break + page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey]) + for subkey in page_kwargs[key]]) # set its date to dt page_kwargs['metadata']['date'] = dt - page = Page( **page_kwargs) + page = Page(**page_kwargs) self.assertEqual(page.locale_date, - unicode(dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']), 'utf-8')) + unicode(dt.strftime(_DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']), + 'utf-8')) + page_kwargs['settings'] = dict([(x, _DEFAULT_CONFIG[x]) for x in + _DEFAULT_CONFIG]) - page_kwargs['settings'] = {x:_DEFAULT_CONFIG[x] for x in _DEFAULT_CONFIG} # I doubt this can work on all platforms ... if platform == "win32": locale = 'jpn' else: locale = 'ja_JP.utf8' - page_kwargs['settings']['DATE_FORMATS'] = {'jp':(locale,'%Y-%m-%d(%a)')} + page_kwargs['settings']['DATE_FORMATS'] = {'jp': (locale, + '%Y-%m-%d(%a)')} page_kwargs['metadata']['lang'] = 'jp' import locale as locale_module try: - page = Page( **page_kwargs) + page = Page(**page_kwargs) self.assertEqual(page.locale_date, u'2015-09-13(\u65e5)') # above is unicode in Japanese: 2015-09-13() except locale_module.Error: From 08b40c7967cdf5ef0780f1f2b4d41d29b31d6578 Mon Sep 17 00:00:00 2001 From: draftcode Date: Mon, 12 Mar 2012 01:33:30 +0900 Subject: [PATCH 178/219] Make names of metadata lower. --- pelican/readers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index a581e458..632c72b9 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -36,8 +36,8 @@ class Reader(object): self.settings = settings def process_metadata(self, name, value): - if name.lower() in _METADATA_PROCESSORS: - return _METADATA_PROCESSORS[name.lower()](value, self.settings) + if name in _METADATA_PROCESSORS: + return _METADATA_PROCESSORS[name](value, self.settings) return value @@ -75,6 +75,7 @@ class RstReader(Reader): else: # standard fields (e.g. address) name = element.tagname value = element.astext() + name = name.lower() output[name] = self.process_metadata(name, value) return output From c05b743fa64696625762a09fe2aabb05195a1dfd Mon Sep 17 00:00:00 2001 From: draftcode Date: Mon, 12 Mar 2012 01:40:27 +0900 Subject: [PATCH 179/219] Add mock to dev_requirements. --- dev_requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev_requirements.txt b/dev_requirements.txt index 198880ec..c7f53682 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -4,3 +4,4 @@ docutils feedgenerator unittest2 pytz +mock From 9cc7efbe12fea30018eafd0a92c7186304ec8600 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 18:02:57 +0100 Subject: [PATCH 180/219] add requirements to travis-ci --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bb1f5af1..8f5dc3a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,6 @@ python: - "2.6" - "2.7" install: - - pip install nose --use-mirrors + - pip install nose unittest2 mock --use-mirrors - pip install . --use-mirrors script: nosetests -s tests From e95b26bf204d684882b711ad6cc817fba16dda7a Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 11 Mar 2012 18:07:08 +0100 Subject: [PATCH 181/219] Add travis-ci build-image support on the README --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 2f66e54c..5012bb9c 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ Pelican ####### +.. image:: https://secure.travis-ci.org/ametaireau/pelican.png?branch=master + Pelican is a simple weblog generator, written in `Python `_. * Write your weblog entries directly with your editor of choice (vim!) From a4ce10d88e76e60a5da6579d3d04d79ba0d02b7a Mon Sep 17 00:00:00 2001 From: draftcode Date: Mon, 12 Mar 2012 02:24:26 +0900 Subject: [PATCH 182/219] Add a test for readers. --- tests/content/article_with_uppercase_metadata.rst | 6 ++++++ tests/test_readers.py | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/content/article_with_uppercase_metadata.rst diff --git a/tests/content/article_with_uppercase_metadata.rst b/tests/content/article_with_uppercase_metadata.rst new file mode 100644 index 00000000..e26cdd13 --- /dev/null +++ b/tests/content/article_with_uppercase_metadata.rst @@ -0,0 +1,6 @@ + +This is a super article ! +######################### + +:Category: Yeah + diff --git a/tests/test_readers.py b/tests/test_readers.py index d4f0aecf..4472d7fe 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -34,6 +34,14 @@ class RstReaderTest(unittest.TestCase): for key, value in expected.items(): self.assertEquals(value, metadata[key], key) + def test_article_metadata_key_lowercase(self): + """Keys of metadata should be lowercase.""" + reader = readers.RstReader({}) + content, metadata = reader.read(_filename('article_with_uppercase_metadata.rst')) + + self.assertIn('category', metadata, "Key should be lowercase.") + self.assertEquals('Yeah', metadata.get('category'), "Value keeps cases.") + def test_typogrify(self): # if nothing is specified in the settings, the content should be # unmodified From 89a481f8e77a96970e03dc0576cb4156fb53c4a1 Mon Sep 17 00:00:00 2001 From: Thanos Lefteris Date: Sun, 11 Mar 2012 21:00:38 +0200 Subject: [PATCH 183/219] Match closing tag --- pelican/themes/simple/templates/archives.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/simple/templates/archives.html b/pelican/themes/simple/templates/archives.html index e6364efa..1ea9b58f 100644 --- a/pelican/themes/simple/templates/archives.html +++ b/pelican/themes/simple/templates/archives.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block content %} -

    Archives for {{ SITENAME }}

    +

    Archives for {{ SITENAME }}

    {% for article in dates %} From acad3e864d6fc729ce08407e619b61da7846f792 Mon Sep 17 00:00:00 2001 From: Thanos Lefteris Date: Sun, 11 Mar 2012 21:08:18 +0200 Subject: [PATCH 184/219] Double-quote HTML attribute --- pelican/themes/notmyidea/templates/archives.html | 2 +- pelican/themes/simple/templates/archives.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/themes/notmyidea/templates/archives.html b/pelican/themes/notmyidea/templates/archives.html index 5ba2c817..f7f1c400 100644 --- a/pelican/themes/notmyidea/templates/archives.html +++ b/pelican/themes/notmyidea/templates/archives.html @@ -6,7 +6,7 @@
    {% for article in dates %}
    {{ article.locale_date }}
    -
    {{ article.title }}
    +
    {{ article.title }}
    {% endfor %}
    diff --git a/pelican/themes/simple/templates/archives.html b/pelican/themes/simple/templates/archives.html index e6364efa..c1a9087e 100644 --- a/pelican/themes/simple/templates/archives.html +++ b/pelican/themes/simple/templates/archives.html @@ -5,7 +5,7 @@
    {% for article in dates %}
    {{ article.locale_date }}
    -
    {{ article.title }}
    +
    {{ article.title }}
    {% endfor %}
    {% endblock %} From e097175a77d94d41d8979c3fb4718b44c0e067b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 13 Mar 2012 02:14:38 +0100 Subject: [PATCH 185/219] Import not used of BeautifulSoup --- tools/pelican_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index b883f7fc..c0d8bf1c 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -38,7 +38,7 @@ def wp2fields(xml): def dc2fields(file): """Opens a Dotclear export file, and yield pelican fields""" - from BeautifulSoup import BeautifulStoneSoup, BeautifulSoup + from BeautifulSoup import BeautifulStoneSoup in_cat = False in_post = False From c4f96b108f54a1968334767c93eb9d59dcb7f5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 13 Mar 2012 02:16:11 +0100 Subject: [PATCH 186/219] Don't set unused fields of Dotclear post --- tools/pelican_import.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index c0d8bf1c..14f06138 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -85,10 +85,10 @@ def dc2fields(file): post_creadt = fields[6] # post_upddt = fields[7] # post_password = fields[8] - post_type = fields[9] + # post_type = fields[9] post_format = fields[10] - post_url = fields[11] - post_lang = fields[12] + # post_url = fields[11] + # post_lang = fields[12] post_title = fields[13] post_excerpt = fields[14] post_excerpt_xhtml = fields[15] From fec605b5775f0f9dba6e78a99f99dddcf09b8b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 13 Mar 2012 02:17:06 +0100 Subject: [PATCH 187/219] Fix way to handle OSError (error doesn't exist) A better way would to use sys.stderr.write or PY3 print(file=sys.stderr) --- tools/pelican_import.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index 14f06138..12c3597f 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -2,6 +2,7 @@ import argparse import os +import sys import time from codecs import open @@ -259,14 +260,14 @@ def main(): elif args.feed: input_type = 'feed' else: - print("you must provide either --wpfile, --dotclear or --feed options") + print("You must provide either --wpfile, --dotclear or --feed options") exit() if not os.path.exists(args.output): try: os.mkdir(args.output) except OSError: - error("Couldn't create the output folder: " + args.output) + print("Unable to create the output folder: " + args.output) exit() # TODO: refactor this long assignment From d7f0b1637e1f6ed4c0ea54f3e314981c72581292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 13 Mar 2012 02:20:33 +0100 Subject: [PATCH 188/219] Cleanup awful line with TODO in pelican-import --- tools/pelican_import.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index 12c3597f..8a425776 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -270,14 +270,11 @@ def main(): print("Unable to create the output folder: " + args.output) exit() - # TODO: refactor this long assignment - input_type, input, out_markup, output_path, dircat=False = input_type, args.input, args.markup, args.output, args.dircat - if input_type == 'wordpress': - fields = wp2fields(input) + fields = wp2fields(args.input) elif input_type == 'dotclear': - fields = dc2fields(input) + fields = dc2fields(args.input) elif input_type == 'feed': - fields = feed2fields(input) + fields = feed2fields(args.input) - fields2pelican(fields, out_markup, output_path, dircat=dircat) + fields2pelican(fields, args.markup, args.output, dircat=args.dircat or False) From 2f79d5f0526bb2fa6efd4755349d2673298ba608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Raimbault?= Date: Tue, 13 Mar 2012 16:52:28 +0100 Subject: [PATCH 189/219] Improve error handling when pandoc is missing --- tools/pelican_import.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/pelican_import.py b/tools/pelican_import.py index 8a425776..b89740df 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -2,6 +2,7 @@ import argparse import os +import subprocess import sys import time @@ -217,7 +218,20 @@ def fields2pelican(fields, out_markup, output_path, dircat=False): content = content.replace("\n", "
    \n") fp.write(content) - os.system('pandoc --normalize --reference-links --from=html --to=%s -o "%s" "%s"' % (out_markup, out_filename, html_filename)) + cmd = 'pandoc --normalize --reference-links --from=html --to={0} -o "{1}" "{2}"'.format( + out_markup, out_filename, html_filename) + + try: + rc = subprocess.call(cmd, shell=True) + if rc < 0: + print("Child was terminated by signal %d" % -rc) + exit() + elif rc > 0: + print("Please, check your Pandoc installation.") + exit() + except OSError, e: + print("Pandoc execution failed: %s" % e) + exit() os.remove(html_filename) From ff2426c4ad9e24c52d9a1d02f9c841d4a6438d00 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 13 Mar 2012 17:10:20 +0100 Subject: [PATCH 190/219] Fix for #245: return the summary of an article based on the :summary: metadata if it is set, else troncate the content. --- pelican/contents.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 4f424461..d8ccf9f3 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -89,9 +89,9 @@ class Page(object): if hasattr(self, 'date') and self.date > datetime.now(): self.status = 'draft' - # set summary - if not hasattr(self, 'summary'): - self.summary = truncate_html_words(self.content, 50) + # store the :summary: metadata if it is set + if 'summary' in metadata: + self._summary = metadata['summary'] def check_properties(self): """test that each mandatory property is set.""" @@ -126,8 +126,12 @@ class Page(object): return content def _get_summary(self): - """Returns the summary of an article, based on to the content""" - return truncate_html_words(self.content, 50) + """Returns the summary of an article, based on the :summary: metadata + if it is set, else troncate the content.""" + if hasattr(self, '_summary'): + return self._summary + else: + return truncate_html_words(self.content, 50) def _set_summary(self, summary): """Dummy function""" From c4418555223315455be8178c86aab1a980335a8a Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 13 Mar 2012 17:15:25 +0100 Subject: [PATCH 191/219] add a test for the summary metadata --- tests/test_contents.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/test_contents.py b/tests/test_contents.py index ed9885b6..8e1407dc 100644 --- a/tests/test_contents.py +++ b/tests/test_contents.py @@ -8,14 +8,20 @@ except ImportError, e: from pelican.contents import Page from pelican.settings import _DEFAULT_CONFIG +from jinja2.utils import generate_lorem_ipsum + +# generate one paragraph, enclosed with

    +TEST_CONTENT = str(generate_lorem_ipsum(n=1)) +TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) class TestPage(TestCase): def setUp(self): super(TestPage, self).setUp() self.page_kwargs = { - 'content': 'content', + 'content': TEST_CONTENT, 'metadata': { + 'summary': TEST_SUMMARY, 'title': 'foo bar', 'author': 'Blogger', }, @@ -27,11 +33,11 @@ class TestPage(TestCase): """ metadata = {'foo': 'bar', 'foobar': 'baz', 'title': 'foobar', } - page = Page('content', metadata=metadata) + page = Page(TEST_CONTENT, metadata=metadata) for key, value in metadata.items(): self.assertTrue(hasattr(page, key)) self.assertEqual(value, getattr(page, key)) - self.assertEqual(page.content, 'content') + self.assertEqual(page.content, TEST_CONTENT) def test_mandatory_properties(self): """If the title is not set, must throw an exception.""" @@ -39,6 +45,11 @@ class TestPage(TestCase): page = Page(**self.page_kwargs) page.check_properties() + def test_summary_from_metadata(self): + """If a :summary: metadata is given, it should be used.""" + page = Page(**self.page_kwargs) + self.assertEqual(page.summary, TEST_SUMMARY) + def test_slug(self): """If a title is given, it should be used to generate the slug.""" page = Page(**self.page_kwargs) From 4f95b9f05c9cf2d5f48786fb6831318e37a1f762 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 14 Mar 2012 09:38:36 +0100 Subject: [PATCH 192/219] remove useless .keys() for key in dict expressions --- pelican/generators.py | 5 ++--- pelican/readers.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 2987dcfe..71208430 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -227,7 +227,7 @@ class ArticlesGenerator(Generator): continue # if no category is set, use the name of the path as a category - if 'category' not in metadata.keys(): + if 'category' not in metadata: if os.path.dirname(f) == self.path: category = self.settings['DEFAULT_CATEGORY'] @@ -238,8 +238,7 @@ class ArticlesGenerator(Generator): if category != '': metadata['category'] = Category(category, self.settings) - if 'date' not in metadata.keys()\ - and self.settings['FALLBACK_ON_FS_DATE']: + if 'date' not in metadata and self.settings['FALLBACK_ON_FS_DATE']: metadata['date'] = datetime.datetime.fromtimestamp( os.stat(f).st_ctime) diff --git a/pelican/readers.py b/pelican/readers.py index 632c72b9..d4e13b4d 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -145,7 +145,7 @@ def read_file(filename, fmt=None, settings=None): if not fmt: fmt = filename.split('.')[-1] - if fmt not in _EXTENSIONS.keys(): + if fmt not in _EXTENSIONS: raise TypeError('Pelican does not know how to parse %s' % filename) reader = _EXTENSIONS[fmt](settings) From 39bdbcfd86bc0f3025ec82b51d86892796356c10 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 14 Mar 2012 12:36:55 +0100 Subject: [PATCH 193/219] files_changed: cleanup and add a test --- pelican/utils.py | 3 --- tests/test_utils.py | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index 1b84f108..7ffd9eb9 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -210,9 +210,6 @@ LAST_MTIME = 0 def files_changed(path, extensions): """Return True if the files have changed since the last check""" - def with_extension(f): - return any(f.endswith(ext) for ext in extensions) - def file_times(path): """Return the last time files have been modified""" for root, dirs, files in os.walk(path): diff --git a/tests/test_utils.py b/tests/test_utils.py index 9654825e..40f710d9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,11 +3,12 @@ try: import unittest2 as unittest except ImportError: import unittest # NOQA + +import os import datetime +import time from pelican import utils -from pelican.contents import Article - from support import get_article @@ -73,3 +74,20 @@ class TestUtils(unittest.TestCase): self.assertIn(fr_article1, trans) self.assertNotIn(en_article1, trans) self.assertNotIn(fr_article1, index) + + def test_files_changed(self): + "Test if file changes are correctly detected" + + path = os.path.join(os.path.dirname(__file__), 'content') + filename = os.path.join(path, 'article_with_metadata.rst') + changed = utils.files_changed(path, 'rst') + self.assertEquals(changed, True) + + changed = utils.files_changed(path, 'rst') + self.assertEquals(changed, False) + + t = time.time() + os.utime(filename, (t, t)) + changed = utils.files_changed(path, 'rst') + self.assertEquals(changed, True) + self.assertAlmostEqual(utils.LAST_MTIME, t, places=2) From 4bac1ee7451cda501bb7826f808f11ff12574cf4 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 14 Mar 2012 19:54:28 +0000 Subject: [PATCH 194/219] - remove default=None with argparse is redundant since it's the default value already. - use the argparse.ArgumentDefaultsHelpFormatter as the formatter_class, to print out automatically the default values --- pelican/__init__.py | 9 +++++---- tools/pelican_import.py | 3 ++- tools/pelican_quickstart.py | 10 ++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 0b53dbcc..5ffcc3b5 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -135,7 +135,8 @@ class Pelican(object): def main(): parser = argparse.ArgumentParser(description="""A tool to generate a - static blog, with restructured text input files.""") + static blog, with restructured text input files.""", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument(dest='path', nargs='?', help='Path where to find the content files.') @@ -145,11 +146,11 @@ 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=None, dest='markup', + parser.add_argument('-m', '--markup', dest='markup', help='The list of markup language to use (rst or md). Please indicate ' 'them separated by commas.') - parser.add_argument('-s', '--settings', dest='settings', default='', - help='The settings of the application. Default to False.') + parser.add_argument('-s', '--settings', dest='settings', + help='The settings of the application.') parser.add_argument('-d', '--delete-output-directory', dest='delete_outputdir', action='store_true', help='Delete the output directory.') diff --git a/tools/pelican_import.py b/tools/pelican_import.py index b883f7fc..bfa0f623 100755 --- a/tools/pelican_import.py +++ b/tools/pelican_import.py @@ -234,7 +234,8 @@ def fields2pelican(fields, out_markup, output_path, dircat=False): def main(): parser = argparse.ArgumentParser( description="Transform feed, Wordpress or Dotclear files to rst files." - "Be sure to have pandoc installed") + "Be sure to have pandoc installed", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument(dest='input', help='The input file to read') parser.add_argument('--wpfile', action='store_true', dest='wpfile', diff --git a/tools/pelican_quickstart.py b/tools/pelican_quickstart.py index 04fc20be..4427be65 100755 --- a/tools/pelican_quickstart.py +++ b/tools/pelican_quickstart.py @@ -193,14 +193,16 @@ def ask(question, answer=str, default=None, l=None): def main(): - parser = argparse.ArgumentParser(description="A kickstarter for pelican") + parser = argparse.ArgumentParser( + description="A kickstarter for pelican", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-p', '--path', default=".", help="The path to generate the blog into") - parser.add_argument('-t', '--title', default=None, metavar="title", + parser.add_argument('-t', '--title', metavar="title", help='Set the title of the website') - parser.add_argument('-a', '--author', default=None, metavar="author", + parser.add_argument('-a', '--author', metavar="author", help='Set the author name of the website') - parser.add_argument('-l', '--lang', default=None, metavar="lang", + parser.add_argument('-l', '--lang', metavar="lang", help='Set the default lang of the website') args = parser.parse_args() From ec31832c5cf5f1bdfcf42a6719648c7c5efdffb2 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 14 Mar 2012 20:04:58 +0000 Subject: [PATCH 195/219] - reorganize imports and remove unused import - remove trailing spaces at the end of the generated files --- tools/pelican_quickstart.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/pelican_quickstart.py b/tools/pelican_quickstart.py index 04fc20be..f2ec9e88 100755 --- a/tools/pelican_quickstart.py +++ b/tools/pelican_quickstart.py @@ -1,10 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -import os, sys, argparse, string +import os +import string +import argparse + from pelican import __version__ -TEMPLATES={ +TEMPLATES = { 'Makefile' : ''' PELICAN=$pelican PELICANOPTS=$pelicanopts @@ -60,7 +63,7 @@ github: $$(OUTPUTDIR)/index.html \tgit push origin gh-pages .PHONY: html help clean ftp_upload ssh_upload dropbox_upload github - ''', +''', 'pelican.conf.py': '''#!/usr/bin/env python # -*- coding: utf-8 -*- # @@ -87,9 +90,7 @@ SOCIAL = ( ) DEFAULT_PAGINATION = $default_pagination - - - ''' +''' } CONF = { From d01606e86d126e46d63a16a72b5f10bbe8162d01 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Wed, 14 Mar 2012 20:06:13 +0000 Subject: [PATCH 196/219] remove unused class _dict --- tools/pelican_quickstart.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tools/pelican_quickstart.py b/tools/pelican_quickstart.py index f2ec9e88..2358161e 100755 --- a/tools/pelican_quickstart.py +++ b/tools/pelican_quickstart.py @@ -109,17 +109,6 @@ CONF = { } -class _dict(dict): - def __init__(self, *args, **kwargs): - dict.__init__(self, *args, **kwargs) - - def __getitem__(self, i): - return dict.get(self,i,None) - - def has_key(k): - return True - - def ask(question, answer=str, default=None, l=None): if answer == str: r = '' From 10fc8a733d7e8f8c99f31101067c77a8ae7c7c6c Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Thu, 15 Mar 2012 00:17:01 +0000 Subject: [PATCH 197/219] fix warnings and errors from settings.rst --- docs/settings.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index 9eb46439..c9b58d04 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -160,6 +160,8 @@ maintain multiple languages with different date formats, you can set this dict using language name (``lang`` in your posts) as key. Regarding available format codes, see `strftime document of python`_ : +.. parsed-literal:: + DATE_FORMAT = { 'en': '%a, %d %b %Y', 'jp': '%Y-%m-%d(%a)', @@ -167,6 +169,8 @@ codes, see `strftime document of python`_ : You can set locale to further control date format: +.. parsed-literal:: + LOCALE = ('usa', 'jpn', # On Windows 'en_US', 'ja_JP' # On Unix/Linux ) @@ -175,6 +179,7 @@ Also, it is possible to set different locale settings for each language. If you put (locale, format) tuples in the dict, this will override the LOCALE setting above: +.. parsed-literal:: # On Unix/Linux DATE_FORMAT = { 'en': ('en_US','%a, %d %b %Y'), From 7f8f6e8a7e0a4d9b8c313f22e65eb1b4ea055513 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Thu, 15 Mar 2012 00:17:50 +0000 Subject: [PATCH 198/219] the sys.path should not contain the _themes directory, which is not a python package --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 4c4530e2..b4a58992 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,6 @@ release = version # -- Options for HTML output --------------------------------------------------- -sys.path.append(os.path.abspath('_themes')) html_theme_path = ['_themes'] html_theme = 'pelican' From 020c1400e22ec1b3f45e7bf5c4ec404223b83a6a Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Thu, 15 Mar 2012 00:23:07 +0000 Subject: [PATCH 199/219] add the pelican path to the sys.path, to be able to use autodoc --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index b4a58992..6c4e1ce5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import sys, os +sys.path.append(os.path.abspath('..')) + # -- General configuration ----------------------------------------------------- templates_path = ['_templates'] extensions = ['sphinx.ext.autodoc',] From 99852ebda8e581a9ecf2710761bb35dbcef89734 Mon Sep 17 00:00:00 2001 From: Dana Woodman Date: Thu, 15 Mar 2012 11:59:58 -0700 Subject: [PATCH 200/219] `MD_EXTENSIONS` should be a list not a tuple. --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index c9b58d04..b98b649e 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -46,7 +46,7 @@ Setting name (default value) What does it do? `MARKUP` (``('rst', 'md')``) A list of available markup languages you want to use. For the moment, the only available values are `rst` and `md`. -`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the Markdown processor +`MD_EXTENSIONS` (``['codehilite','extra']``) A list of the extensions that the Markdown processor will use. Refer to the extensions chapter in the Python-Markdown documentation for a complete list of supported extensions. From 2ff3db8f517772c6b9c57fb35a8572827551694b Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Thu, 15 Mar 2012 19:37:49 +0000 Subject: [PATCH 201/219] fix the reset term code to make it work also on OSX --- pelican/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/log.py b/pelican/log.py index 1cb76e16..027743d0 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -5,7 +5,7 @@ from logging import critical, error, info, warning, warn, debug from logging import Formatter, getLogger, StreamHandler -RESET_TERM = u'\033[1;m' +RESET_TERM = u'\033[0;m' def term_color(code): From 74c2449d8f0fe4e6ae8c237835b438f87dfc9aad Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Fri, 16 Mar 2012 14:27:26 +0000 Subject: [PATCH 202/219] add version information in __init__.py and import them from setup.py and conf.py --- docs/conf.py | 6 ++++-- pelican/__init__.py | 4 +++- setup.py | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6c4e1ce5..ac2d67ee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,6 +3,8 @@ import sys, os sys.path.append(os.path.abspath('..')) +from pelican import __version__, __major__ + # -- General configuration ----------------------------------------------------- templates_path = ['_templates'] extensions = ['sphinx.ext.autodoc',] @@ -11,8 +13,8 @@ master_doc = 'index' project = u'Pelican' copyright = u'2010, Alexis Metaireau and contributors' exclude_patterns = ['_build'] -version = "2" -release = version +version = __version__ +release = __major__ # -- Options for HTML output --------------------------------------------------- diff --git a/pelican/__init__.py b/pelican/__init__.py index 5ffcc3b5..780938a7 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -11,7 +11,9 @@ from pelican.utils import clean_output_dir, files_changed from pelican.writers import Writer from pelican import log -__version__ = "3.0" +__major__ = 3 +__minor__ = 0 +__version__ = "{0}.{1}".format(__major__, __minor__) class Pelican(object): diff --git a/setup.py b/setup.py index 910499de..d26ad5f4 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from setuptools import setup -VERSION = "3.0" # find a better way to do so. +from pelican import __version__ requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz'] @@ -21,7 +21,7 @@ entry_points = { setup( name = "pelican", - version = VERSION, + version = __version__, url = 'http://pelican.notmyidea.org/', author = 'Alexis Metaireau', author_email = 'alexis@notmyidea.org', From df8b71f8110566351db3fdc358fcf8ff65d6bdcf Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Fri, 16 Mar 2012 14:27:51 +0000 Subject: [PATCH 203/219] remove unused *bat include --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index fc46d905..a092ecd0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,3 @@ include *.rst global-include *.py recursive-include pelican *.html *.css *png include LICENSE -global-include *.bat From 26078ecc175764c138b8415c70ba85aecbb81565 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Fri, 16 Mar 2012 14:50:26 +0000 Subject: [PATCH 204/219] remove unused colors and refactor more how the escaping is done --- pelican/log.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/pelican/log.py b/pelican/log.py index 027743d0..c5e9b688 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -8,31 +8,25 @@ from logging import Formatter, getLogger, StreamHandler RESET_TERM = u'\033[0;m' -def term_color(code): - return lambda text: code + unicode(text) + RESET_TERM +def start_color(index): + return u'\033[1;{0}m'.format(index) + + +def term_color(color): + code = COLOR_CODES[color] + return lambda text: start_color(code) + unicode(text) + RESET_TERM COLOR_CODES = { - 'gray': u'\033[1;30m', - 'red': u'\033[1;31m', - 'green': u'\033[1;32m', - 'yellow': u'\033[1;33m', - 'blue': u'\033[1;34m', - 'magenta': u'\033[1;35m', - 'cyan': u'\033[1;36m', - 'white': u'\033[1;37m', - 'bgred': u'\033[1;41m', - 'bggreen': u'\033[1;42m', - 'bgbrown': u'\033[1;43m', - 'bgblue': u'\033[1;44m', - 'bgmagenta': u'\033[1;45m', - 'bgcyan': u'\033[1;46m', - 'bggray': u'\033[1;47m', - 'bgyellow': u'\033[1;43m', - 'bggrey': u'\033[1;100m', + 'red': 31, + 'yellow': 33, + 'cyan': 36, + 'white': 37, + 'bgred': 41, + 'bggrey': 100, } -ANSI = dict((col, term_color(code)) for col, code in COLOR_CODES.items()) +ANSI = dict((col, term_color(col)) for col in COLOR_CODES) class ANSIFormatter(Formatter): From 173133dbf34b322511feb36663e189709e505949 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Fri, 16 Mar 2012 14:53:28 +0000 Subject: [PATCH 205/219] remove couple of extra spaces --- pelican/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/log.py b/pelican/log.py index c5e9b688..8811b372 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -1,6 +1,6 @@ import os import sys -from logging import CRITICAL, ERROR, WARN, INFO, DEBUG +from logging import CRITICAL, ERROR, WARN, INFO, DEBUG from logging import critical, error, info, warning, warn, debug from logging import Formatter, getLogger, StreamHandler @@ -74,7 +74,7 @@ class DummyFormatter(object): and not sys.platform.startswith('win'): return ANSIFormatter(*args, **kwargs) else: - return TextFormatter( *args, **kwargs) + return TextFormatter(*args, **kwargs) def init(level=None, logger=getLogger(), handler=StreamHandler()): From 4e4af9d011625f79c04a6ab1d55de892dbaf5149 Mon Sep 17 00:00:00 2001 From: Andrea Crotti Date: Fri, 16 Mar 2012 18:53:08 +0000 Subject: [PATCH 206/219] move tools in pelican.tools and add the pelican.tools package to setup.py --- {tools => pelican/tools}/__init__.py | 0 {tools => pelican/tools}/pelican_import.py | 0 {tools => pelican/tools}/pelican_quickstart.py | 0 {tools => pelican/tools}/pelican_themes.py | 0 setup.py | 8 ++++---- 5 files changed, 4 insertions(+), 4 deletions(-) rename {tools => pelican/tools}/__init__.py (100%) rename {tools => pelican/tools}/pelican_import.py (100%) rename {tools => pelican/tools}/pelican_quickstart.py (100%) rename {tools => pelican/tools}/pelican_themes.py (100%) diff --git a/tools/__init__.py b/pelican/tools/__init__.py similarity index 100% rename from tools/__init__.py rename to pelican/tools/__init__.py diff --git a/tools/pelican_import.py b/pelican/tools/pelican_import.py similarity index 100% rename from tools/pelican_import.py rename to pelican/tools/pelican_import.py diff --git a/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py similarity index 100% rename from tools/pelican_quickstart.py rename to pelican/tools/pelican_quickstart.py diff --git a/tools/pelican_themes.py b/pelican/tools/pelican_themes.py similarity index 100% rename from tools/pelican_themes.py rename to pelican/tools/pelican_themes.py diff --git a/setup.py b/setup.py index 910499de..a8e0f57d 100755 --- a/setup.py +++ b/setup.py @@ -13,9 +13,9 @@ except ImportError: entry_points = { 'console_scripts': [ 'pelican = pelican:main', - 'pelican-import = tools.pelican_import:main', - 'pelican-quickstart = tools.pelican_quickstart:main', - 'pelican-themes = tools.pelican_themes:main' + 'pelican-import = pelican.tools.pelican_import:main', + 'pelican-quickstart = pelican.tools.pelican_quickstart:main', + 'pelican-themes = pelican.tools.pelican_themes:main' ] } @@ -27,7 +27,7 @@ setup( author_email = 'alexis@notmyidea.org', description = "A tool to generate a static blog from reStructuredText or Markdown input files.", long_description=open('README.rst').read(), - packages = ['pelican'], + packages = ['pelican', 'pelican.tools'], include_package_data = True, install_requires = requires, entry_points = entry_points, From c75ab513daa0c4b78d2f0ebfd23077fcc6b158e2 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 16 Mar 2012 19:59:03 +0100 Subject: [PATCH 207/219] use 'summary' rather than ':summary:' in comments --- pelican/contents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index d8ccf9f3..2a7dc22a 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -89,7 +89,7 @@ class Page(object): if hasattr(self, 'date') and self.date > datetime.now(): self.status = 'draft' - # store the :summary: metadata if it is set + # store the summary metadata if it is set if 'summary' in metadata: self._summary = metadata['summary'] @@ -126,7 +126,7 @@ class Page(object): return content def _get_summary(self): - """Returns the summary of an article, based on the :summary: metadata + """Returns the summary of an article, based on the summary metadata if it is set, else troncate the content.""" if hasattr(self, '_summary'): return self._summary From 3dcedb60d83e80ecfd16d6440f21f7cfe110bcef Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 16 Mar 2012 20:56:00 +0100 Subject: [PATCH 208/219] add irc support to travis-ci --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8f5dc3a3..89a2ebde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,5 @@ install: - pip install nose unittest2 mock --use-mirrors - pip install . --use-mirrors script: nosetests -s tests +notifications: + irc: "irc.freenode.org#pelican" From 8b846b9fe2eaff627b411ef6af1f9ba1bae2a5b8 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 16 Mar 2012 21:12:48 +0100 Subject: [PATCH 209/219] restore back to hardcoded version in setup.py due to import hell --- setup.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 1f75131b..67bbae80 100755 --- a/setup.py +++ b/setup.py @@ -1,8 +1,6 @@ #!/usr/bin/env python from setuptools import setup -from pelican import __version__ - requires = ['feedgenerator', 'jinja2', 'pygments', 'docutils', 'pytz'] try: @@ -16,12 +14,12 @@ entry_points = { 'pelican-import = pelican.tools.pelican_import:main', 'pelican-quickstart = pelican.tools.pelican_quickstart:main', 'pelican-themes = pelican.tools.pelican_themes:main' - ] + ] } setup( name = "pelican", - version = __version__, + version = "3.0", url = 'http://pelican.notmyidea.org/', author = 'Alexis Metaireau', author_email = 'alexis@notmyidea.org', From 4819a830037ec56466a4d0fb35f6441db663774e Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 16 Mar 2012 21:13:24 +0100 Subject: [PATCH 210/219] Fix #171. Handle unicode filenames --- pelican/contents.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/contents.py b/pelican/contents.py index 2a7dc22a..3386dba9 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -42,9 +42,10 @@ class Page(object): if 'AUTHOR' in settings: self.author = Author(settings['AUTHOR'], settings) else: + title = filename.decode('utf-8') if filename else self.title self.author = Author(getenv('USER', 'John Doe'), settings) warning(u"Author of `{0}' unknown, assuming that his name is " - "`{1}'".format(filename or self.title, self.author)) + "`{1}'".format(title, self.author)) # manage languages self.in_default_lang = True From cbc609c8f00277efbbb2e17becbc36054526d48f Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Fri, 16 Mar 2012 21:23:34 +0100 Subject: [PATCH 211/219] only notify build is okay if it was failing previously --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 89a2ebde..823c1172 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,7 @@ install: - pip install . --use-mirrors script: nosetests -s tests notifications: - irc: "irc.freenode.org#pelican" + irc: + channels: + - "irc.freenode.org#pelican" + on_success: change From a9f5fdc47b5c42dee41e8ffebd773bcc9c2276ab Mon Sep 17 00:00:00 2001 From: Martin Brochhaus Date: Sun, 18 Mar 2012 14:57:53 +0800 Subject: [PATCH 212/219] Always using normal capitalization in headlines. --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index b98b649e..b1c35122 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -83,7 +83,7 @@ Setting name (default value) What does it do? .. [#] Default is the system locale. -URL Settings +URL settings ------------ You can customize the URL's and locations where files will be saved. The URL's and From 9c4b40fd35833a6f68b04316ca5ce8f77f19c325 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 18 Mar 2012 15:12:06 +0100 Subject: [PATCH 213/219] Keep raw metadata text (but for summary) --- pelican/readers.py | 5 ++++- tests/content/article_with_metadata.rst | 1 + tests/test_readers.py | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pelican/readers.py b/pelican/readers.py index d4e13b4d..2e269647 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -71,7 +71,10 @@ class RstReader(Reader): if element.tagname == 'field': # custom fields (e.g. summary) name_elem, body_elem = element.children name = name_elem.astext() - value = render_node_to_html(document, body_elem) + if name == 'summary': + value = render_node_to_html(document, body_elem) + else: + value = body_elem.astext() else: # standard fields (e.g. address) name = element.tagname value = element.astext() diff --git a/tests/content/article_with_metadata.rst b/tests/content/article_with_metadata.rst index 3410885e..d4bac1c0 100644 --- a/tests/content/article_with_metadata.rst +++ b/tests/content/article_with_metadata.rst @@ -9,3 +9,4 @@ This is a super article ! :summary: Multi-line metadata should be supported as well as **inline markup**. +:custom_field: http://notmyidea.org diff --git a/tests/test_readers.py b/tests/test_readers.py index 058469d4..4c04a212 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -30,6 +30,7 @@ class RstReaderTest(unittest.TestCase): ' inline markup.', 'date': datetime.datetime(2010, 12, 2, 10, 14), 'tags': ['foo', 'bar', 'foobar'], + 'custom_field': 'http://notmyidea.org', } for key, value in expected.items(): From 229ebbbcbf5f6ccb1b3c193fb992ab0539c4ac1f Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 20 Mar 2012 01:26:26 +0100 Subject: [PATCH 214/219] plugins branch is planned to be merged for 3.0 --- docs/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 3bf7c532..f788b32a 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -3,7 +3,7 @@ Plugins ####### -Since version 2.8, pelican manages plugins. Plugins are a way to add feature to +Since version 3.0, pelican manages plugins. Plugins are a way to add feature to pelican without having to directly hack pelican code. Pelican is shipped with a set of core plugins, but you can easily implement From 67f7fcba264eaa122d86286636531247935eca71 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 20 Mar 2012 01:39:52 +0100 Subject: [PATCH 215/219] remove duplicated pelican-themes entry from the toctree --- docs/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 202bdbd9..7dbac168 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -60,7 +60,6 @@ A French version of the documentation is available at :doc:`fr/index`. getting_started settings themes - pelican-themes plugins internals pelican-themes From 7cd4d28bb2e57e0154a3cb8b1546b669abdf715f Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 20 Mar 2012 23:31:04 +0100 Subject: [PATCH 216/219] better plugins doc --- docs/plugins.rst | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index f788b32a..db5a4bfc 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -3,17 +3,18 @@ Plugins ####### -Since version 3.0, pelican manages plugins. Plugins are a way to add feature to -pelican without having to directly hack pelican code. +Since version 3.0, pelican manages plugins. Plugins are a way to add features +to pelican without having to directly hack pelican code. Pelican is shipped with a set of core plugins, but you can easily implement -your own (and this page describes how) +your own (and this page describes how). How to use plugins? ==================== -To load plugins, you have to specify a them in your settings file. You have two -ways to do so: by specifying strings with the path to the callables: :: +To load plugins, you have to specify them in your settings file. You have two +ways to do so. +Either by specifying strings with the path to the callables:: PLUGINS = ['pelican.plugins.gravatar',] @@ -76,28 +77,18 @@ Translation ----------- Github Activity -_______________ +--------------- -This plugins introduces a new depencency, you have to install feedparser -if you want to use it, these are some ways to do it:: +This plugin makes use of the ``feedparser`` library that you'll need to +install. - apt-get install python-feedparser # on debian based distributions like ubuntu - sudo easy_install feedparser - sudo pip install feedparser - -To enable it set in your pelican config file the GITHUB_ACTIVITY_FEED -parameter pointing to your github activity feed. - -for example my personal activity feed is:: - - https://github.com/kpanic.atom - -and the config line could be:: +Set the GITHUB_ACTIVITY_FEED parameter to your github activity feed. +For example, my setting would look like:: GITHUB_ACTIVITY_FEED = 'https://github.com/kpanic.atom' -in your template just write a for in jinja2 syntax against the -github_activity variable, like for example:: +On the templates side, you just have to iterate over the ``github_activity`` +variable, as in the example:: {% if GITHUB_ACTIVITY_FEED %}