mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge branch 'urls' of https://github.com/kylef/pelican
This commit is contained in:
commit
a7cea63db6
17 changed files with 229 additions and 114 deletions
|
|
@ -24,12 +24,7 @@ Basic settings
|
||||||
================================================ =====================================================
|
================================================ =====================================================
|
||||||
Setting name (default value) What does it do?
|
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)
|
`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
|
`DATE_FORMATS` (``{}``) If you do manage multiple languages, you can
|
||||||
set the date formatting here. See "Date format and locales"
|
set the date formatting here. See "Date format and locales"
|
||||||
section below for details.
|
section below for details.
|
||||||
|
|
@ -79,16 +74,14 @@ Setting name (default value) What does it do?
|
||||||
.. [#] Default is the system locale.
|
.. [#] Default is the system locale.
|
||||||
|
|
||||||
|
|
||||||
Article permalink structure
|
URL Settings
|
||||||
---------------------------
|
------------
|
||||||
|
|
||||||
This setting allows you to output your articles sorted by date, provided that
|
You can customize the URL's and locations where files will be saved. The URL's and
|
||||||
you specify a format as specified below. This format follows the Python
|
SAVE_AS variables use python's format strings. These variables allow you to place
|
||||||
``datetime`` directives:
|
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
|
||||||
* %Y: Year with century as a decimal number.
|
articles and pages anywhere you want.
|
||||||
* %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
|
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
|
input files' date metadata attribute. If the date is not specified for a
|
||||||
|
|
@ -99,15 +92,42 @@ information.
|
||||||
|
|
||||||
Also, you can use other file metadata attributes as well:
|
Also, you can use other file metadata attributes as well:
|
||||||
|
|
||||||
* category: '%(category)s'
|
* slug
|
||||||
* author: '%(author)s'
|
* date
|
||||||
* tags: '%(tags)s'
|
* lang
|
||||||
* date: '%(date)s'
|
* author
|
||||||
|
* category
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
* '/%Y/%m/' will render something like '/2011/07/sample-post.html'.
|
* ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'
|
||||||
* '/%Y/%(category)s/' will render something like '/2011/life/sample-post.html'.
|
* 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.
|
||||||
|
`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
|
Timezone
|
||||||
--------
|
--------
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os, sys
|
import os, sys
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||||
|
|
@ -26,6 +27,42 @@ class Pelican(object):
|
||||||
if self.path.endswith('/'):
|
if self.path.endswith('/'):
|
||||||
self.path = self.path[:-1]
|
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
|
# define the default settings
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.theme = theme or settings['THEME']
|
self.theme = theme or settings['THEME']
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class Page(object):
|
||||||
if not settings:
|
if not settings:
|
||||||
settings = _DEFAULT_CONFIG
|
settings = _DEFAULT_CONFIG
|
||||||
|
|
||||||
|
self.settings = settings
|
||||||
self._content = content
|
self._content = content
|
||||||
self.translations = []
|
self.translations = []
|
||||||
|
|
||||||
|
|
@ -37,9 +38,9 @@ class Page(object):
|
||||||
# default author to the one in settings if not defined
|
# default author to the one in settings if not defined
|
||||||
if not hasattr(self, 'author'):
|
if not hasattr(self, 'author'):
|
||||||
if 'AUTHOR' in settings:
|
if 'AUTHOR' in settings:
|
||||||
self.author = settings['AUTHOR']
|
self.author = Author(settings['AUTHOR'], settings)
|
||||||
else:
|
else:
|
||||||
self.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))
|
warning(u"Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author))
|
||||||
|
|
||||||
# manage languages
|
# manage languages
|
||||||
|
|
@ -55,29 +56,6 @@ class Page(object):
|
||||||
if not hasattr(self, 'slug') and hasattr(self, 'title'):
|
if not hasattr(self, 'slug') and hasattr(self, 'title'):
|
||||||
self.slug = slugify(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:
|
if filename:
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
|
@ -115,6 +93,30 @@ class Page(object):
|
||||||
if not hasattr(self, prop):
|
if not hasattr(self, prop):
|
||||||
raise NameError(prop)
|
raise NameError(prop)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_format(self):
|
||||||
|
return {
|
||||||
|
'slug': getattr(self, 'slug', ''),
|
||||||
|
'lang': getattr(self, 'lang', 'en'),
|
||||||
|
'date': getattr(self, 'date', datetime.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', u'pages/{slug}.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', 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)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def content(self):
|
def content(self):
|
||||||
if hasattr(self, "_get_content"):
|
if hasattr(self, "_get_content"):
|
||||||
|
|
@ -138,10 +140,74 @@ class Page(object):
|
||||||
class Article(Page):
|
class Article(Page):
|
||||||
mandatory_properties = ('title', 'date', 'category')
|
mandatory_properties = ('title', 'date', 'category')
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
|
||||||
class Quote(Page):
|
class Quote(Page):
|
||||||
base_properties = ('author', 'date')
|
base_properties = ('author', 'date')
|
||||||
|
|
||||||
|
class URLWrapper(object):
|
||||||
|
def __init__(self, name, settings):
|
||||||
|
self.name = unicode(name)
|
||||||
|
self.settings = settings
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.name == unicode(other)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.name)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
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)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def save_as(self):
|
||||||
|
return self.settings.get('CATEGORY_SAVE_AS', u'category/{name}.html').format(name=self.name)
|
||||||
|
|
||||||
|
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.get('TAG_URL', u'tag/{name}.html').format(name=self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def save_as(self):
|
||||||
|
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', u'author/{name}.html').format(name=self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def save_as(self):
|
||||||
|
return self.settings.get('AUTHOR_SAVE_AS', u'author/{name}.html').format(name=self.name)
|
||||||
|
|
||||||
def is_valid_content(content, f):
|
def is_valid_content(content, f):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from operator import attrgetter, itemgetter
|
||||||
from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader
|
from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader
|
||||||
from jinja2.exceptions import TemplateNotFound
|
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.log import *
|
||||||
from pelican.readers import read_file
|
from pelican.readers import read_file
|
||||||
from pelican.utils import copy, process_translations, open
|
from pelican.utils import copy, process_translations, open
|
||||||
|
|
@ -179,26 +179,26 @@ class ArticlesGenerator(Generator):
|
||||||
for tag, articles in self.tags.items():
|
for tag, articles in self.tags.items():
|
||||||
articles.sort(key=attrgetter('date'), reverse=True)
|
articles.sort(key=attrgetter('date'), reverse=True)
|
||||||
dates = [article for article in self.dates if article in articles]
|
dates = [article for article in self.dates if article in articles]
|
||||||
write('tag/%s.html' % tag, tag_template, self.context, tag=tag,
|
write(tag.save_as, tag_template, self.context, tag=tag,
|
||||||
articles=articles, dates=dates,
|
articles=articles, dates=dates,
|
||||||
paginated={'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')
|
category_template = self.get_template('category')
|
||||||
for cat, articles in self.categories:
|
for cat, articles in self.categories:
|
||||||
dates = [article for article in self.dates if article in articles]
|
dates = [article for article in self.dates if article in articles]
|
||||||
write('category/%s.html' % cat, category_template, self.context,
|
write(cat.save_as, category_template, self.context,
|
||||||
category=cat, articles=articles, dates=dates,
|
category=cat, articles=articles, dates=dates,
|
||||||
paginated={'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')
|
author_template = self.get_template('author')
|
||||||
for aut, articles in self.authors:
|
for aut, articles in self.authors:
|
||||||
dates = [article for article in self.dates if article in articles]
|
dates = [article for article in self.dates if article in articles]
|
||||||
write('author/%s.html' % aut, author_template, self.context,
|
write(aut.save_as, author_template, self.context,
|
||||||
author=aut, articles=articles, dates=dates,
|
author=aut, articles=articles, dates=dates,
|
||||||
paginated={'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:
|
for article in self.drafts:
|
||||||
write('drafts/%s.html' % article.slug, article_template, self.context,
|
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',])
|
files = self.get_files(self.path, exclude=['pages',])
|
||||||
all_articles = []
|
all_articles = []
|
||||||
for f in files:
|
for f in files:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
content, metadata = read_file(f, settings=self.settings)
|
content, metadata = read_file(f, settings=self.settings)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|
@ -228,7 +227,7 @@ class ArticlesGenerator(Generator):
|
||||||
category = os.path.basename(os.path.dirname(f)).decode('utf-8')
|
category = os.path.basename(os.path.dirname(f)).decode('utf-8')
|
||||||
|
|
||||||
if category != '':
|
if category != '':
|
||||||
metadata['category'] = unicode(category)
|
metadata['category'] = Category(category, self.settings)
|
||||||
|
|
||||||
if 'date' not in metadata.keys()\
|
if 'date' not in metadata.keys()\
|
||||||
and self.settings['FALLBACK_ON_FS_DATE']:
|
and self.settings['FALLBACK_ON_FS_DATE']:
|
||||||
|
|
@ -239,21 +238,6 @@ class ArticlesGenerator(Generator):
|
||||||
if not is_valid_content(article, f):
|
if not is_valid_content(article, f):
|
||||||
continue
|
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 article.status == "published":
|
||||||
if hasattr(article, 'tags'):
|
if hasattr(article, 'tags'):
|
||||||
for tag in article.tags:
|
for tag in article.tags:
|
||||||
|
|
@ -348,7 +332,7 @@ class PagesGenerator(Generator):
|
||||||
|
|
||||||
def generate_output(self, writer):
|
def generate_output(self, writer):
|
||||||
for page in chain(self.translations, self.pages):
|
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,
|
self.context, page=page,
|
||||||
relative_urls = self.settings.get('RELATIVE_URLS'))
|
relative_urls = self.settings.get('RELATIVE_URLS'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,25 +15,30 @@ except ImportError:
|
||||||
Markdown = False
|
Markdown = False
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from pelican.contents import Category, Tag, Author, URLWrapper
|
||||||
from pelican.utils import get_date, open
|
from pelican.utils import get_date, open
|
||||||
|
|
||||||
|
|
||||||
_METADATA_PROCESSORS = {
|
_METADATA_PROCESSORS = {
|
||||||
'tags': lambda x: map(unicode.strip, unicode(x).split(',')),
|
'tags': lambda x, y: [Tag(tag, y) for tag in unicode(x).split(',')],
|
||||||
'date': lambda x: get_date(x),
|
'date': lambda x, y: get_date(x),
|
||||||
'status': unicode.strip,
|
'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):
|
class Reader(object):
|
||||||
enabled = True
|
enabled = True
|
||||||
extensions = None
|
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):
|
class _FieldBodyTranslator(HTMLTranslator):
|
||||||
|
|
||||||
def astext(self):
|
def astext(self):
|
||||||
|
|
@ -51,29 +56,25 @@ def render_node_to_html(document, node):
|
||||||
node.walkabout(visitor)
|
node.walkabout(visitor)
|
||||||
return visitor.astext()
|
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):
|
class RstReader(Reader):
|
||||||
enabled = bool(docutils)
|
enabled = bool(docutils)
|
||||||
extension = "rst"
|
extension = "rst"
|
||||||
|
|
||||||
def _parse_metadata(self, document):
|
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):
|
def _get_publisher(self, filename):
|
||||||
extra_params = {'initial_header_level': '2'}
|
extra_params = {'initial_header_level': '2'}
|
||||||
|
|
@ -110,7 +111,7 @@ class MarkdownReader(Reader):
|
||||||
metadata = {}
|
metadata = {}
|
||||||
for name, value in md.Meta.items():
|
for name, value in md.Meta.items():
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
metadata[name] = _process_metadata(name, value[0])
|
metadata[name] = self.process_metadata(name, value[0])
|
||||||
return content, metadata
|
return content, metadata
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,7 +127,7 @@ class HtmlReader(Reader):
|
||||||
key = i.split(':')[0][5:].strip()
|
key = i.split(':')[0][5:].strip()
|
||||||
value = i.split(':')[-1][:-3].strip()
|
value = i.split(':')[-1][:-3].strip()
|
||||||
name = key.lower()
|
name = key.lower()
|
||||||
metadata[name] = _process_metadata(name, value)
|
metadata[name] = self.process_metadata(name, value)
|
||||||
|
|
||||||
return content, metadata
|
return content, metadata
|
||||||
|
|
||||||
|
|
@ -140,7 +141,7 @@ def read_file(filename, fmt=None, settings=None):
|
||||||
fmt = filename.split('.')[-1]
|
fmt = filename.split('.')[-1]
|
||||||
if fmt not in _EXTENSIONS.keys():
|
if fmt not in _EXTENSIONS.keys():
|
||||||
raise TypeError('Pelican does not know how to parse %s' % filename)
|
raise TypeError('Pelican does not know how to parse %s' % filename)
|
||||||
reader = _EXTENSIONS[fmt]()
|
reader = _EXTENSIONS[fmt](settings)
|
||||||
settings_key = '%s_EXTENSIONS' % fmt.upper()
|
settings_key = '%s_EXTENSIONS' % fmt.upper()
|
||||||
if settings and settings_key in settings:
|
if settings and settings_key in settings:
|
||||||
reader.extensions = settings[settings_key]
|
reader.extensions = settings[settings_key]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,14 @@ _DEFAULT_CONFIG = {'PATH': None,
|
||||||
'REVERSE_ARCHIVE_ORDER': False,
|
'REVERSE_ARCHIVE_ORDER': False,
|
||||||
'REVERSE_CATEGORY_ORDER': False,
|
'REVERSE_CATEGORY_ORDER': False,
|
||||||
'DELETE_OUTPUT_DIRECTORY': 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,
|
'RELATIVE_URLS': True,
|
||||||
'DEFAULT_LANG': 'en',
|
'DEFAULT_LANG': 'en',
|
||||||
'TAG_CLOUD_STEPS': 4,
|
'TAG_CLOUD_STEPS': 4,
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
{% if article.author %}
|
{% if article.author %}
|
||||||
<address class="vcard author">
|
<address class="vcard author">
|
||||||
By <a class="url fn" href="{{ SITEURL }}/author/{{ article.author }}.html">{{ article.author }}</a>
|
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||||
</address>
|
</address>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>In <a href="{{ SITEURL }}/category/{{ article.category }}.html">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
|
<p>In <a href="{{ SITEURL }}/{{ article.category.url }}">{{ article.category }}</a>. {% if PDF_PROCESSOR %}<a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a>{% endif %}</p>
|
||||||
{% include 'taglist.html' %}
|
{% include 'taglist.html' %}
|
||||||
{% include 'translations.html' %}
|
{% include 'translations.html' %}
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if DISPLAY_PAGES_ON_MENU %}
|
{% if DISPLAY_PAGES_ON_MENU %}
|
||||||
{% for page in PAGES %}
|
{% for page in PAGES %}
|
||||||
<li><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></li>
|
<li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for cat, null in categories %}
|
{% for cat, null in categories %}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for category, articles in categories %}
|
{% for category, articles in categories %}
|
||||||
<li>{{ category }}</li>
|
<li><a href="{{ category.url }}">{{ category }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<section id="content" class="body">
|
<section id="content" class="body">
|
||||||
<h2>Pages</h2>
|
<h2>Pages</h2>
|
||||||
{% for page in PAGES %}
|
{% for page in PAGES %}
|
||||||
<li><a href="{{ SITEURL }}/pages/{{ page.url }}">{{ page.title }}</a></li>
|
<li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
{% if article.tags %}<p>tags: {% for tag in article.tags %}<a href="{{ SITEURL }}/tag/{{ tag }}.html">{{ tag }}</a>{% endfor %}</p>{% endif %}
|
{% if article.tags %}<p>tags: {% for tag in article.tags %}<a href="{{ SITEURL }}/{{ tag.url }}">{{ tag }}</a>{% endfor %}</p>{% endif %}
|
||||||
{% if PDF_PROCESSOR %}<p><a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a></p>{% endif %}
|
{% if PDF_PROCESSOR %}<p><a href="{{ SITEURL }}/pdf/{{ article.slug }}.pdf">get the pdf</a></p>{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</abbr>
|
</abbr>
|
||||||
{% if article.author %}
|
{% if article.author %}
|
||||||
<address class="vcard author">
|
<address class="vcard author">
|
||||||
By <a class="url fn" href="#">{{ article.author }}</a>
|
By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a>
|
||||||
</address>
|
</address>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if DISPLAY_PAGES_ON_MENU %}
|
{% if DISPLAY_PAGES_ON_MENU %}
|
||||||
{% for p in PAGES %}
|
{% for p in PAGES %}
|
||||||
<li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/pages/{{ p.url }}">{{ p.title }}</a></li>
|
<li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% for cat, null in categories %}
|
{% for cat, null in categories %}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for category, articles in categories %}
|
{% for category, articles in categories %}
|
||||||
<li>{{ category }}</li>
|
<li><a href="{{ SITEURL }}/{{ category.url }}">{{ category }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<header> <h2 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
|
<header> <h2 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title}}">{{ article.title }}</a></h2> </header>
|
||||||
<footer class="post-info">
|
<footer class="post-info">
|
||||||
<abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.locale_date }} </abbr>
|
<abbr class="published" title="{{ article.date.isoformat() }}"> {{ article.locale_date }} </abbr>
|
||||||
{% if article.author %}<address class="vcard author">By <a class="url fn" href="{{ SITEURL }}/author/{{ article.author }}.html">{{ article.author }}</a></address>{% endif %}
|
{% if article.author %}<address class="vcard author">By <a class="url fn" href="{{ SITEURL }}/{{ article.author.url }}">{{ article.author }}</a></address>{% endif %}
|
||||||
</footer><!-- /.post-info -->
|
</footer><!-- /.post-info -->
|
||||||
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
|
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
|
||||||
</article></li>
|
</article></li>
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,12 @@ class TestPage(TestCase):
|
||||||
"""
|
"""
|
||||||
# if a title is defined, save_as should be set
|
# if a title is defined, save_as should be set
|
||||||
page = Page(**self.page_kwargs)
|
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
|
# if a language is defined, save_as should include it accordingly
|
||||||
self.page_kwargs['metadata'].update({'lang': 'fr', })
|
self.page_kwargs['metadata'].update({'lang': 'fr', })
|
||||||
page = Page(**self.page_kwargs)
|
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):
|
def test_datetime(self):
|
||||||
"""If DATETIME is set to a tuple, it should be used to override LOCALE
|
"""If DATETIME is set to a tuple, it should be used to override LOCALE
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ def _filename(*args):
|
||||||
class RstReaderTest(unittest2.TestCase):
|
class RstReaderTest(unittest2.TestCase):
|
||||||
|
|
||||||
def test_article_with_metadata(self):
|
def test_article_with_metadata(self):
|
||||||
reader = readers.RstReader()
|
reader = readers.RstReader({})
|
||||||
content, metadata = reader.read(_filename('article_with_metadata.rst'))
|
content, metadata = reader.read(_filename('article_with_metadata.rst'))
|
||||||
expected = {
|
expected = {
|
||||||
'category': 'yeah',
|
'category': 'yeah',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue