1
0
Fork 0
forked from github/pelican
pelican-theme/pelican/generators.py

238 lines
8.4 KiB
Python
Raw Normal View History

from operator import attrgetter
from datetime import datetime
import os
from jinja2 import Environment, FileSystemLoader
from jinja2.exceptions import TemplateNotFound
from pelican.utils import update_dict, copytree
from pelican.contents import Article, Page, is_valid_content
from pelican.readers import read_file
_TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories',
2010-10-30 20:17:23 +01:00
'archives', 'page')
_DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives')
class Generator(object):
"""Baseclass generator"""
def __init__(self, *args, **kwargs):
for idx, item in enumerate(('context', 'settings', 'path', 'theme',
'output_path', 'markup')):
setattr(self, item, args[idx])
for arg, value in kwargs.items():
setattr(self, arg, value)
def get_templates(self):
2010-11-20 02:25:42 +00:00
"""Return the templates to use.
Use self.theme to get the templates to use, and return a list of
templates ready to use with Jinja2.
"""
path = os.path.expanduser(os.path.join(self.theme, 'templates'))
env = Environment(loader=FileSystemLoader(path))
templates = {}
for template in _TEMPLATES:
try:
templates[template] = env.get_template('%s.html' % template)
except TemplateNotFound:
2010-11-20 02:25:42 +00:00
raise Exception('[templates] unable to load %s.html from %s' % (
template, path))
return templates
2010-11-20 02:25:42 +00:00
def get_files(self, path, exclude=[], extensions=None):
"""Return a list of files to use, based on rules
2010-10-30 20:17:23 +01:00
:param path: the path to search the file on
:param exclude: the list of path to exclude
"""
2010-11-20 02:25:42 +00:00
if not extensions:
extensions = self.markup
2010-10-30 20:17:23 +01:00
files = []
for root, dirs, temp_files in os.walk(path, followlinks=True):
for e in exclude:
if e in dirs:
dirs.remove(e)
files.extend([os.sep.join((root, f)) for f in temp_files
2010-11-20 02:25:42 +00:00
if True in [f.endswith(ext) for ext in extensions]])
2010-10-30 20:17:23 +01:00
return files
def _update_context(self, items):
"""Update the context with the given items from the currrent
processor.
"""
for item in items:
value = getattr(self, item)
if hasattr(value, 'items'):
value = value.items()
self.context[item] = value
class ArticlesGenerator(Generator):
"""Generate blog articles"""
def __init__(self, *args, **kwargs):
"""initialize properties"""
self.articles = []
self.dates = {}
self.tags = {}
self.categories = {}
super(ArticlesGenerator, self).__init__(*args, **kwargs)
def generate_feeds(self, writer):
"""Generate the feeds from the current context, and output files."""
writer.write_feed(self.articles, self.context, self.settings['FEED'])
if 'FEED_RSS' in self.settings:
writer.write_feed(self.articles, self.context,
self.settings['FEED_RSS'], feed_type='rss')
for cat, arts in self.categories.items():
arts.sort(key=attrgetter('date'), reverse=True)
writer.write_feed(arts, self.context,
self.settings['CATEGORY_FEED'] % cat)
if 'CATEGORY_FEED_RSS' in self.settings:
writer.write_feed(arts, self.context,
self.settings['CATEGORY_FEED_RSS'] % cat,
feed_type='rss')
def generate_pages(self, writer):
"""Generate the pages on the disk
TODO: change the name"""
templates = self.get_templates()
write = writer.write_file
for template in _DIRECT_TEMPLATES:
write('%s.html' % template, templates[template], self.context,
blog=True)
for tag in self.tags:
write('tag/%s.html' % tag, templates['tag'], self.context, tag=tag)
for cat in self.categories:
write('category/%s.html' % cat, templates['category'], self.context,
category=cat, articles=self.categories[cat])
for article in self.articles:
write('%s' % article.url,
templates['article'], self.context, article=article,
category=article.category)
def generate_context(self):
"""change the context"""
# return the list of files to use
files = self.get_files(self.path, exclude=['pages',])
for f in files:
content, metadatas = read_file(f)
# if no category is set, use the name of the path as a category
if 'category' not in metadatas.keys():
category = os.path.dirname(f).replace(
os.path.expanduser(self.path)+'/', '')
if category == self.path:
category = self.settings['DEFAULT_CATEGORY']
if category != '':
metadatas['category'] = unicode(category)
if 'date' not in metadatas.keys()\
and self.settings['FALLBACK_ON_FS_DATE']:
metadatas['date'] = datetime.fromtimestamp(os.stat(f).st_ctime)
article = Article(content, metadatas, settings=self.settings,
filename=f)
if not is_valid_content(article, f):
continue
update_dict(self.categories, article.category, article)
if hasattr(article, 'tags'):
for tag in article.tags:
update_dict(self.tags, tag, article)
self.articles.append(article)
# sort the articles by date
self.articles.sort(key=attrgetter('date'), reverse=True)
self.dates = list(self.articles)
self.dates.sort(key=attrgetter('date'))
# and generate the output :)
self._update_context(('articles', 'dates', 'tags', 'categories'))
def generate_output(self, writer):
self.generate_feeds(writer)
self.generate_pages(writer)
class PagesGenerator(Generator):
"""Generate pages"""
def __init__(self, *args, **kwargs):
self.pages = []
super(PagesGenerator, self).__init__(*args, **kwargs)
def generate_context(self):
for f in self.get_files(os.sep.join((self.path, 'pages'))):
content, metadatas = read_file(f)
page = Page(content, metadatas, settings=self.settings,
filename=f)
if not is_valid_content(page, f):
continue
self.pages.append(page)
self._update_context(('pages', ))
def generate_output(self, writer):
templates = self.get_templates()
for page in self.pages:
writer.write_file('pages/%s' % page.url, templates['page'],
self.context, page=page)
self._update_context(('pages',))
class StaticGenerator(Generator):
"""copy static paths to output"""
def _copy_paths(self, paths, source, destination, output_path,
final_path=None):
for path in paths:
copytree(path, source, os.path.join(output_path, destination),
final_path)
def generate_output(self, writer):
self._copy_paths(self.settings['STATIC_PATHS'], self.path,
'static', self.output_path)
self._copy_paths(self.settings['THEME_PATHS'], self.theme,
'theme', self.output_path, '.')
class PdfGenerator(Generator):
"""Generate PDFs on the output dir, for all articles and pages coming from
rst"""
def __init__(self, *args, **kwargs):
try:
from rst2pdf.createpdf import RstToPdf
self.pdfcreator = RstToPdf(breakside=0, stylesheets=['twelvepoint'])
except ImportError:
raise Exception("unable to find rst2pdf")
super(PdfGenerator, self).__init(*args, **kwargs)
def _create_pdf(self, obj, output_path):
if obj.filename.endswith(".rst"):
self.pdfcreator.createPdf(text=open(obj.filename).read(),
output=os.path.join(output_path, "%s.pdf" % obj.slug))
def generate_context(self):
pdf_path = os.path.join(self.output_path, 'pdf')
try:
os.mkdir(pdf_path)
except OSError:
pass
for article in self.context['articles']:
self._create_pdf(article, pdf_path)
for page in self.context['pages']:
self._create_pdf(page, pdf_path)