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

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

View file

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

View file

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

View file

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

85
pelican/paginator.py Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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