From a1145219145313b17e524550d2d98ffb0d369abf Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Wed, 18 Aug 2010 16:02:06 +0200 Subject: [PATCH] Add a way to specify CSS files. Add default settings too. --HG-- rename : samples/themes/notmyidea/archives.html => samples/themes/notmyidea/templates/archives.html rename : samples/themes/notmyidea/article.html => samples/themes/notmyidea/templates/article.html rename : samples/themes/notmyidea/base.html => samples/themes/notmyidea/templates/base.html rename : samples/themes/notmyidea/categories.html => samples/themes/notmyidea/templates/categories.html rename : samples/themes/notmyidea/category.html => samples/themes/notmyidea/templates/category.html rename : samples/themes/notmyidea/index.html => samples/themes/notmyidea/templates/index.html rename : samples/themes/notmyidea/tag.html => samples/themes/notmyidea/templates/tag.html rename : samples/themes/notmyidea/tags.html => samples/themes/notmyidea/templates/tags.html --- TODO | 1 - pelican/generator.py | 52 ++- pelican/pelican | 15 +- samples/test_settings.py | 2 + samples/themes/notmyidea/css/main.css | 405 ++++++++++++++++++ samples/themes/notmyidea/css/reset.css | 52 +++ .../notmyidea/{ => templates}/archives.html | 0 .../notmyidea/{ => templates}/article.html | 0 .../notmyidea/{ => templates}/base.html | 0 .../notmyidea/{ => templates}/categories.html | 0 .../notmyidea/{ => templates}/category.html | 0 .../notmyidea/{ => templates}/index.html | 0 .../themes/notmyidea/{ => templates}/tag.html | 0 .../notmyidea/{ => templates}/tags.html | 0 14 files changed, 501 insertions(+), 26 deletions(-) create mode 100644 samples/themes/notmyidea/css/main.css create mode 100644 samples/themes/notmyidea/css/reset.css rename samples/themes/notmyidea/{ => templates}/archives.html (100%) rename samples/themes/notmyidea/{ => templates}/article.html (100%) rename samples/themes/notmyidea/{ => templates}/base.html (100%) rename samples/themes/notmyidea/{ => templates}/categories.html (100%) rename samples/themes/notmyidea/{ => templates}/category.html (100%) rename samples/themes/notmyidea/{ => templates}/index.html (100%) rename samples/themes/notmyidea/{ => templates}/tag.html (100%) rename samples/themes/notmyidea/{ => templates}/tags.html (100%) diff --git a/TODO b/TODO index 8e81f02f..55456ea9 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -* Add separated CSS files somewhere * Fall back to settings + date of files when no metadata available * Filter to generate only the files with .rst extension * Add a licence diff --git a/pelican/generator.py b/pelican/generator.py index 8bc1747e..b44285e0 100644 --- a/pelican/generator.py +++ b/pelican/generator.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import re +import shutil from codecs import open from datetime import datetime from docutils import core @@ -14,29 +15,38 @@ import rstdirectives # import the directives to have pygments support _TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories', 'archives') _DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives') -_DEFAULT_TEMPLATE_PATH =\ +_DEFAULT_THEME =\ os.sep.join([os.path.dirname(os.path.abspath(__file__)), "themes"]) +_DEFAULT_CONFIG = {'PATH': None, + 'THEME': _DEFAULT_THEME, + 'OUTPUT_PATH': 'output/', + 'MARKUP': 'rst'} - -def generate_output(files, templates_path=None, output_path=None, markup=None, +def generate_output(path=None, theme=None, output_path=None, markup=None, settings=None): """Given a list of files, a template and a destination, output the static files. - :param files: the list of files to parse - :param templates_path: where to search for templates + :param path: the path where to find the files to parse + :param theme: where to search for templates :param output_path: where to output the generated files :param markup: the markup language to use while parsing :param settings: the settings file to use """ - if not templates_path: - templates_path = _DEFAULT_TEMPLATE_PATH - if not output_path: - output_path = './output' + # get the settings + context = read_settings(settings) + path = path or context['PATH'] + theme = theme or context['THEME'] + output_path = output_path or context['OUTPUT_PATH'] output_path = os.path.realpath(output_path) + markup = markup or context['MARKUP'] + + # get the list of files to parse + files = [] + for root, dirs, temp_files in os.walk(path, followlinks=True): + files.extend([os.sep.join((root, f)) for f in temp_files]) articles, dates, years, tags, categories = [], {}, {}, {}, {} - # for each file, get the informations. for f in files: f = os.path.abspath(f) @@ -53,15 +63,14 @@ def generate_output(files, templates_path=None, output_path=None, markup=None, # order the articles by date articles.sort(key=attrgetter('date'), reverse=True) - templates = get_templates(templates_path) - context = {} + templates = get_templates(theme) for item in ('articles', 'dates', 'years', 'tags', 'categories'): value = locals()[item] if hasattr(value, 'items'): value = value.items() context[item] = value - read_settings(context, settings) - + + # generate the output generate = partial(generate_file, output_path) for template in _DIRECT_TEMPLATES: generate('%s.html' % template, templates[template], context) @@ -74,6 +83,13 @@ def generate_output(files, templates_path=None, output_path=None, markup=None, generate('%s' % article.url, templates['article'], context, article=article) + # copy css path to output/css + try: + shutil.copytree(os.path.join(theme, 'css'), + os.path.join(output_path, 'css')) + except OSError: + pass + def generate_file(path, name, template, context, **kwargs): context.update(kwargs) @@ -89,6 +105,7 @@ def generate_file(path, name, template, context, **kwargs): def get_templates(path=None): + path = os.path.join(path, 'templates') env = Environment(loader=FileSystemLoader(path)) templates = {} for template in _TEMPLATES: @@ -102,9 +119,10 @@ def update_dict(mapping, key, value): mapping[key].append(value) -def read_settings(context, filename): +def read_settings(filename): """Load a Python file into a dictionary. """ + context = _DEFAULT_CONFIG.copy() if filename: tempdict = {} execfile(filename, tempdict) @@ -158,11 +176,11 @@ class Article(object): def __init__(self, string, markup=None): if markup == None: - markup = 'rest' + markup = 'rst' for key, value in parse_metadata(string).items(): setattr(self, key, value) - if markup == 'rest': + if markup == 'rst': extra_params = {'input_encoding': 'unicode', 'initial_header_level': '2'} rendered_content = core.publish_parts(string, writer_name='html', diff --git a/pelican/pelican b/pelican/pelican index 74fbc7ab..ff3a067d 100755 --- a/pelican/pelican +++ b/pelican/pelican @@ -8,25 +8,24 @@ files to read and a template to use. The main use case is to generate static-files-based blogs, to ease DVCSes as storages, but it could be used with others goal in mind.""") -parser.add_argument('-p', '--path', default='content', dest='path', + +parser.add_argument('-p', '--path', dest='path', help='Path where to find the content files (default is "content").') -parser.add_argument('-t', '--templates-path', default=None, dest='templates', +parser.add_argument('-t', '--templates-path', dest='templates', help='Path where to find the templates. If not specified, will uses the' ' ones included with pelican.') -parser.add_argument('-o', '--output', default=None, dest='output', +parser.add_argument('-o', '--output', dest='output', help='Where to output the generated files. If not specified, a directory' ' will be created, named "output" in the current path.') -parser.add_argument('-m', '--markup', default='rest', dest='markup', +parser.add_argument('-m', '--markup', default='rst', dest='markup', help='the markup language to use. Currently only ReSTreucturedtext is' ' available.') -parser.add_argument('-s', '--settings', default=None, dest='settings', +parser.add_argument('-s', '--settings', dest='settings', help='the settings of the application. Default to None.') if __name__ == '__main__': args = parser.parse_args() files = [] - for root, dirs, temp_files in os.walk(args.path, followlinks=True): - files.extend([os.sep.join((root, f)) for f in temp_files]) - generate_output(files, args.templates, args.output, args.markup, + generate_output(args.path, args.templates, args.output, args.markup, args.settings) print 'Done !' diff --git a/samples/test_settings.py b/samples/test_settings.py index bfb33089..fcdb836f 100644 --- a/samples/test_settings.py +++ b/samples/test_settings.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +PATH = 'samples/content' +THEME = 'samples/themes/notmyidea' BLOGNAME = 'NotMyIdea.org' BLOGSUBTITLE = u"Alexis Métaireau's weblog" diff --git a/samples/themes/notmyidea/css/main.css b/samples/themes/notmyidea/css/main.css new file mode 100644 index 00000000..2b0d852b --- /dev/null +++ b/samples/themes/notmyidea/css/main.css @@ -0,0 +1,405 @@ +/* + Name: Smashing HTML5 + Date: July 2009 + Description: Sample layout for HTML5 and CSS3 goodness. + Version: 1.0 + Author: Enrique Ramírez + Autor URI: http://enrique-ramirez.com +*/ + +/* Imports */ +@import url("reset.css"); + +/***** Global *****/ +/* Body */ + body { + background: #F5F4EF url('../images/bg.png'); + color: #000305; + font-size: 87.5%; /* Base font size: 14px */ + font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; + line-height: 1.429; + margin: 0; + padding: 0; + text-align: left; + } + +/* Headings */ +h2 {font-size: 1.571em} /* 22px */ +h3 {font-size: 1.429em} /* 20px */ +h4 {font-size: 1.286em} /* 18px */ +h5 {font-size: 1.143em} /* 16px */ +h6 {font-size: 1em} /* 14px */ + +h2, h3, h4, h5, h6 { + font-weight: 400; + line-height: 1.1; + margin-bottom: .8em; +} + +/* Anchors */ +a {outline: 0;} +a img {border: 0px; text-decoration: none;} +a:link, a:visited { + color: #C74350; + padding: 0 1px; + text-decoration: underline; +} +a:hover, a:active { + background-color: #C74350; + color: #fff; + text-decoration: none; + text-shadow: 1px 1px 1px #333; +} + +/* Paragraphs */ +p {margin-bottom: 1.143em;} +* p:last-child {margin-bottom: 0;} + +strong, b {font-weight: bold;} +em, i {font-style: italic;} + +::-moz-selection {background: #F6CF74; color: #fff;} +::selection {background: #F6CF74; color: #fff;} + +/* Lists */ +ul { + list-style: outside disc; + margin: 1em 0 1.5em 1.5em; +} + +ol { + list-style: outside decimal; + margin: 1em 0 1.5em 1.5em; +} + +dl {margin: 0 0 1.5em 0;} +dt {font-weight: bold;} +dd {margin-left: 1.5em;} + +/* Quotes */ +blockquote {font-style: italic;} +cite {} + +q {} + +/* Tables */ +table {margin: .5em auto 1.5em auto; width: 98%;} + + /* Thead */ + thead th {padding: .5em .4em; text-align: left;} + thead td {} + + /* Tbody */ + tbody td {padding: .5em .4em;} + tbody th {} + + tbody .alt td {} + tbody .alt th {} + + /* Tfoot */ + tfoot th {} + tfoot td {} + +/* HTML5 tags */ +header, section, footer, +aside, nav, article, figure { + display: block; +} + +/***** Layout *****/ +.body {clear: both; margin: 0 auto; width: 800px;} +img.right figure.right {float: right; margin: 0 0 2em 2em;} +img.left, figure.left {float: right; margin: 0 0 2em 2em;} + +/* + Header +*****************/ +#banner { + margin: 0 auto; + padding: 2.5em 0 0 0; +} + + /* Banner */ + #banner h1 {font-size: 3.571em; line-height: .6;} + #banner h1 a:link, #banner h1 a:visited { + color: #000305; + display: block; + font-weight: bold; + margin: 0 0 .6em .2em; + text-decoration: none; + width: 427px; + } + #banner h1 a:hover, #banner h1 a:active { + background: none; + color: #C74350; + text-shadow: none; + } + + #banner h1 strong {font-size: 0.36em; font-weight: normal;} + + /* Main Nav */ + #banner nav { + background: #000305; + font-size: 1.143em; + height: 40px; + line-height: 30px; + margin: 0 auto 2em auto; + padding: 0; + text-align: center; + width: 800px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + + #banner nav ul {list-style: none; margin: 0 auto; width: 800px;} + #banner nav li {float: left; display: inline; margin: 0;} + + #banner nav a:link, #banner nav a:visited { + color: #fff; + display: inline-block; + height: 30px; + padding: 5px 1.5em; + text-decoration: none; + } + #banner nav a:hover, #banner nav a:active, + #banner nav .active a:link, #banner nav .active a:visited { + background: #C74451; + color: #fff; + text-shadow: none !important; + } + + #banner nav li:first-child a { + border-top-left-radius: 5px; + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + + border-bottom-left-radius: 5px; + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; + } + +/* + Featured +*****************/ +#featured { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#featured figure { + border: 2px solid #eee; + float: right; + margin: 0.786em 2em 0 5em; + width: 248px; +} +#featured figure img {display: block; float: right;} + +#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;} +#featured h3 {font-size: 1.429em; margin-bottom: .5em;} + +#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;} +#featured h3 a:hover, #featured h3 a:active {color: #fff;} + +/* + Body +*****************/ +#content { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px 20px; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +/* + Extras +*****************/ +#extras {margin: 0 auto 3em auto; overflow: hidden;} + +#extras ul {list-style: none; margin: 0;} +#extras li {border-bottom: 1px solid #fff;} +#extras h2 { + color: #C74350; + font-size: 1.429em; + margin-bottom: .25em; + padding: 0 3px; +} + +#extras a:link, #extras a:visited { + color: #444; + display: block; + border-bottom: 1px solid #F4E3E3; + text-decoration: none; + 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 */ + #extras .blogroll { + float: left; + width: 615px; + } + + #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;} + + /* Social */ + #extras .social { + float: right; + width: 175px; + } + + #extras div[class='social'] a { + background-repeat: no-repeat; + background-position: 3px 6px; + padding-left: 25px; + } + + /* Icons */ + .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');} + .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');} + .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');} + .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');} + .social a[href*='/feed/'] {background-image: url('../images/icons/rss.png');} + .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} + +/* + About +*****************/ +#about { + background: #fff; + font-style: normal; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + text-align: left; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#about .primary {float: left; width: 165px;} +#about .primary strong {color: #C64350; display: block; font-size: 1.286em;} +#about .photo {float: left; margin: 5px 20px;} + +#about .url:link, #about .url:visited {text-decoration: none;} + +#about .bio {float: right; width: 500px;} + +/* + Footer +*****************/ +#contentinfo {padding-bottom: 2em; text-align: right;} + +/***** Sections *****/ +/* Blog */ +.hentry { + border-bottom: 1px solid #eee; + padding: 1.5em 0; +} +li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +#content > .hentry {padding: 1em 0;} + +.entry-title {font-size: 1.429em; margin-bottom: 0;} +.entry-title a:link, .entry-title a:visited {text-decoration: none;} + +.hentry .post-info * {font-style: normal;} + + /* Content */ + .hentry footer {margin-bottom: 2em;} + .hentry footer address {display: inline;} + #posts-list footer address {display: block;} + + /* Blog Index */ + #posts-list {list-style: none; margin: 0;} + #posts-list .hentry {padding-left: 200px; position: relative;} + #posts-list .hentry:hover { + background: #C64350; + color: #fff; + } + #posts-list .hentry:hover a:link, #posts-list .hentry:hover a:visited { + color: #F6CF74; + text-shadow: 1px 1px 1px #333; + } + + #posts-list footer { + left: 10px; + position: absolute; + top: 1.5em; + width: 190px; + } + + /* About the Author */ + #about-author { + background: #f9f9f9; + clear: both; + font-style: normal; + margin: 2em 0; + padding: 10px 20px 15px 20px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + + #about-author strong { + color: #C64350; + clear: both; + display: block; + font-size: 1.429em; + } + + #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;} + + /* Comments */ + #comments-list {list-style: none; margin: 0 1em;} + #comments-list blockquote { + background: #f8f8f8; + clear: both; + font-style: normal; + margin: 0; + padding: 15px 20px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + #comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;} + + #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} + + /* Add a Comment */ + #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment input[type='text'], + #add-comment input[type='email'], + #add-comment input[type='url'] {float: left; width: 200px;} + + #add-comment textarea {float: left; height: 150px; width: 495px;} + + #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} + + #add-comment input[type='submit'] {float: right; margin: 0 .5em;} + #add-comment * {margin-bottom: .5em;} diff --git a/samples/themes/notmyidea/css/reset.css b/samples/themes/notmyidea/css/reset.css new file mode 100644 index 00000000..1e217566 --- /dev/null +++ b/samples/themes/notmyidea/css/reset.css @@ -0,0 +1,52 @@ +/* + Name: Reset Stylesheet + Description: Resets browser's default CSS + Author: Eric Meyer + Author URI: http://meyerweb.com/eric/tools/css/reset/ +*/ + +/* v1.0 | 20080212 */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + background: transparent; + border: 0; + font-size: 100%; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; +} + +body {line-height: 1;} + +ol, ul {list-style: none;} + +blockquote, q {quotes: none;} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins {text-decoration: none;} +del {text-decoration: line-through;} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/samples/themes/notmyidea/archives.html b/samples/themes/notmyidea/templates/archives.html similarity index 100% rename from samples/themes/notmyidea/archives.html rename to samples/themes/notmyidea/templates/archives.html diff --git a/samples/themes/notmyidea/article.html b/samples/themes/notmyidea/templates/article.html similarity index 100% rename from samples/themes/notmyidea/article.html rename to samples/themes/notmyidea/templates/article.html diff --git a/samples/themes/notmyidea/base.html b/samples/themes/notmyidea/templates/base.html similarity index 100% rename from samples/themes/notmyidea/base.html rename to samples/themes/notmyidea/templates/base.html diff --git a/samples/themes/notmyidea/categories.html b/samples/themes/notmyidea/templates/categories.html similarity index 100% rename from samples/themes/notmyidea/categories.html rename to samples/themes/notmyidea/templates/categories.html diff --git a/samples/themes/notmyidea/category.html b/samples/themes/notmyidea/templates/category.html similarity index 100% rename from samples/themes/notmyidea/category.html rename to samples/themes/notmyidea/templates/category.html diff --git a/samples/themes/notmyidea/index.html b/samples/themes/notmyidea/templates/index.html similarity index 100% rename from samples/themes/notmyidea/index.html rename to samples/themes/notmyidea/templates/index.html diff --git a/samples/themes/notmyidea/tag.html b/samples/themes/notmyidea/templates/tag.html similarity index 100% rename from samples/themes/notmyidea/tag.html rename to samples/themes/notmyidea/templates/tag.html diff --git a/samples/themes/notmyidea/tags.html b/samples/themes/notmyidea/templates/tags.html similarity index 100% rename from samples/themes/notmyidea/tags.html rename to samples/themes/notmyidea/templates/tags.html