From ddac40e9cbdc002766548ea4d9535ff77e174949 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sat, 3 Mar 2012 18:26:07 +0100 Subject: [PATCH 01/34] Include .bat files in the distribution. Fix #221 --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index a092ecd0..fc46d905 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ include *.rst global-include *.py recursive-include pelican *.html *.css *png include LICENSE +global-include *.bat From bde06c401174d74d6c449bae0725f6853f3e726e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 6 Mar 2012 06:13:17 -0800 Subject: [PATCH 02/34] Improve English documentation with enhanced clarity, grammar, and spelling --- README.rst | 45 +++++---- docs/contribute.rst | 36 +++---- docs/faq.rst | 55 ++++++----- docs/getting_started.rst | 140 ++++++++++++++------------ docs/importer.rst | 8 +- docs/index.rst | 41 ++++---- docs/internals.rst | 91 +++++++++-------- docs/pelican-themes.rst | 14 +-- docs/settings.rst | 208 ++++++++++++++++++++------------------- docs/themes.rst | 170 ++++++++++++++++---------------- docs/tips.rst | 18 ++-- 11 files changed, 426 insertions(+), 400 deletions(-) diff --git a/README.rst b/README.rst index 8a6ef04c..0a27bdcd 100644 --- a/README.rst +++ b/README.rst @@ -3,51 +3,54 @@ Pelican Pelican is a simple weblog generator, written in `Python `_. -* Write your weblog entries directly with your editor of choice (vim!) and - directly in `reStructuredText `_, or `Markdown `_. -* A simple cli-tool to (re)generate the weblog. +* Write your weblog entries directly with your editor of choice (vim!) + in `reStructuredText `_ or `Markdown `_ +* Includes a simple CLI tool to (re)generate the weblog * Easy to interface with DVCSes and web hooks -* Completely static output, so easy to host anywhere ! +* Completely static output is easy to host anywhere Features -------- Pelican currently supports: -* blog articles and pages -* comments, via an external service (disqus). Please notice that while - it's useful, it's an external service, and you'll not manage the - comments by yourself. It could potentially eat your data. -* theming support (themes are done using `jinja2 `_) -* PDF generation of the articles/pages (optional). -* Translations -* Syntactic recognition +* Blog articles and pages +* Comments, via an external service (Disqus). (Please note that while + useful, Disqus is an external service, and thus the comment data will be + somewhat outside of your control and potentially subject to data loss.) +* Theming support (themes are created using `jinja2 `_) +* PDF generation of the articles/pages (optional) +* Publication of articles in multiple languages +* Atom/RSS feeds +* Code syntax highlighting +* Import from WordPress, Dotclear, or RSS feeds +* Integration with external tools: Twitter, Google Analytics, etc. (optional) -Have a look to `the documentation `_ for -more informations. +Have a look at `the documentation `_ for +more information. -Why the name "Pelican" ? +Why the name "Pelican"? ------------------------ -Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;) +Heh, you didn't notice? "Pelican" is an anagram for "Calepin" ;) Source code ----------- -You can access the source code via git on http://github.com/ametaireau/pelican/ +You can access the source code via git at: https://github.com/ametaireau/pelican -If you feel hackish, have a look to the `pelican's internals explanations -`_. +If you feel hackish, have a look at the `pelican's internals explanation +`_. Feedback / Contact us ===================== -If you want to see new features in Pelican, dont hesitate to tell me, to clone +If you want to see new features in Pelican, don't hesitate to tell me, to clone the repository, etc. That's open source, dude! Contact me at "alexis at notmyidea dot org" for any request/feedback! You can also join the team at `#pelican on irc.freenode.org `_ -(or if you don't have any IRC client, using `the webchat +(or if you don't have any IRC client, use `the webchat `_) for quick feedback. diff --git a/docs/contribute.rst b/docs/contribute.rst index 0aaa5771..84b99293 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -1,28 +1,28 @@ -How to contribute ? +How to contribute? ################### -There are many ways to contribute to pelican. You can enhance the -documentation, add missing features, fix bugs or just report them. +There are many ways to contribute to Pelican. You can enhance the +documentation, add missing features, and fix bugs (or just report them). -Don't hesitate to fork and make a pull request on github. +Don't hesitate to fork and make a pull request on GitHub. -Set up the development environment -================================== +Setting up the development environment +====================================== -You're free to setup up the environment in any way you like. Here is a way -using virtualenv and virtualenvwrapper. If you don't have them, you can install -them using:: +You're free to set up your development environment any way you like. Here is a +way using virtualenv and virtualenvwrapper. If you don't have them, you can +install these packages via:: $ pip install virtualenvwrapper -Virtual environments allow you to work on an installation of python which is -not the one installed on your system. Especially, it will install the different -projects under a different location. +Virtual environments allow you to work on Python projects which are isolated +from one another so you can use different packages (and package versions) with +different projects. -To create the virtualenv environment, you have to do:: +To create a virtual environment, use the following syntax:: - $ mkvirtualenv pelican --no-site-package + $ mkvirtualenv pelican -Then you would have to install all the dependencies:: +To manually install the dependencies:: $ pip install -r dev_requirements.txt $ python setup.py develop @@ -31,10 +31,10 @@ Running the test suite ====================== Each time you add a feature, there are two things to do regarding tests: -checking that the tests run in a right way, and be sure that you add tests for -the feature you are working on or the bug you're fixing. +checking that the existing tests pass, and adding tests for your new feature +or for the bug you're fixing. -The tests leaves under "pelican/tests" and you can run them using the +The tests live in "pelican/tests" and you can run them using the "discover" feature of unittest2:: $ unit2 discover diff --git a/docs/faq.rst b/docs/faq.rst index dbdf9692..b3dbca87 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -1,50 +1,51 @@ Frequently Asked Questions (FAQ) ################################ -Here is a summary of the frequently asked questions for pelican. +Here is a summary of the frequently asked questions for Pelican. -Is it mandatory to have a configuration file ? -============================================== +Is it mandatory to have a configuration file? +============================================= -No, it's not. Configurations files are just an easy way to configure pelican. -For the basic operations, it's possible to specify options while invoking -pelican with the command line (see `pelican --help` for more informations about -that) +No, it's not. Configuration files are just an easy way to configure Pelican. +For basic operations, it's possible to specify options while invoking Pelican +via the command line. See `pelican --help` for more information. -I'm creating my own theme, how to use pygments ? -================================================ +I'm creating my own theme. How do I use Pygments for syntax highlighting? +========================================================================= -Pygment add some classes to the generated content, so the theming of your theme -will be done thanks to a css file. You can have a look to the one proposed by -default `on the project website `_ +Pygments adds some classes to the generated content. These classes are used by +themes to style code syntax highlighting via CSS. Specifically, you can +customize the appearance of your syntax highlighting via the `.codehilite pre` +class in your theme's CSS file. To see how various styles can be used to render +Django code, for example, you can use the demo `on the project website +`_. -How do I create my own theme ? +How do I create my own theme? ============================== -Please refer yourself to :ref:`theming-pelican`. +Please refer to :ref:`theming-pelican`. -How can I help ? +How can I help? ================ -You have different options to help. First, you can use pelican, and report any -idea or problem you have on `the bugtracker +There are several ways to help out. First, you can use Pelican and report any +suggestions or problems you might have on `the bugtracker `_. -If you want to contribute, please have a look to `the git repository -`_, fork it, add your changes and do -a pull request, I'll review them as soon as possible. +If you want to contribute, please fork `the git repository +`_, make your changes, and issue +a pull request. I'll review your changes as soon as possible. -You can also contribute by creating themes, and making the documentation -better. +You can also contribute by creating themes and improving the documentation. -I want to use markdown, but I got an error -========================================== +I want to use Markdown, but I got an error. +=========================================== -Markdown is not a hard dependency for pelican, so you will need to install it -by yourself. You can do so by typing:: +Markdown is not a hard dependency for Pelican, so you will need to explicitly +install it. You can do so by typing:: $ (sudo) pip install markdown -In case you don't have pip installed, consider installing it by doing:: +In case you don't have pip installed, consider installing it via:: $ (sudo) easy_install pip diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 3e60b63a..1647fee4 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -4,13 +4,12 @@ Getting started Installing ========== -You're ready? Let's go ! You can install pelican in a lot of different ways, -the simpler one is via `pip `_:: +You're ready? Let's go! You can install Pelican via several different methods. The simplest is via `pip `_:: $ pip install pelican -If you have the sources, you can install pelican using the distutils command -install. I recommend to do so in a virtualenv:: +If you have the project source, you can install Pelican using the distutils +method. I recommend doing so in a virtualenv:: $ virtualenv pelican_venv $ source bin/activate @@ -19,30 +18,30 @@ install. I recommend to do so in a virtualenv:: Dependencies ------------ -At this time, pelican is dependent of the following python packages: +At this time, Pelican is dependent on the following Python packages: -* feedgenerator, to generate the ATOM feeds. -* jinja2, for templating support. +* feedgenerator, to generate the Atom feeds +* jinja2, for templating support -If you're not using python 2.7, you will also need `argparse`. +If you're not using Python 2.7, you will also need `argparse`. Optionally: -* docutils, for reST support -* pygments, to have syntactic colorization with resT input -* Markdown, for Markdown as an input format +* pygments, for syntax highlighting +* docutils, for supporting reStructuredText as an input format +* Markdown, for supporting Markdown as an input format -Writing articles using pelican +Writing articles using Pelican ============================== -Files metadata +File metadata -------------- -Pelican tries to be smart enough to get the informations it needs from the -file system (for instance, about the category of your articles), but you need to -provide by hand some of those informations in your files. +Pelican tries to be smart enough to get the information it needs from the +file system (for instance, about the category of your articles), but some +information you need to provide in the form of metadata inside your files. -You could provide the metadata in the restructured text files, using the +You can provide this metadata in reStructuredText text files via the following syntax (give your file the `.rst` extension):: My super title @@ -54,31 +53,35 @@ following syntax (give your file the `.rst` extension):: :author: Alexis Metaireau -You can also use a markdown syntax (with a file ending in `.md`):: +You can also use Markdown syntax (with a file ending in `.md`):: Date: 2010-12-03 Title: My super title + Tags: thats, awesome + Slug: my-super-post - Put you content here. + This is the content of my super blog post. -Note that none of those are mandatory: if the date is not specified, pelican will -rely on the mtime of your file, and the category can also be determined by the -directory where the rst file is. For instance, the category of -`python/foobar/myfoobar.rst` is `foobar`. +Note that, aside from the title, none of this metadata is mandatory: if the date +is not specified, Pelican will rely on the file's "mtime" timestamp, and the +category can be determined by the directory in which the file resides. For +example, a file located at `python/foobar/myfoobar.rst` will have a category of +`foobar`. Generate your blog ------------------ -To launch pelican, just use the `pelican` command:: +To launch Pelican, just use the `pelican` command:: $ pelican /path/to/your/content/ [-s path/to/your/settings.py] -And… that's all! You can see your weblog generated on the `content/` folder. +And… that's all! Your weblog will be generated and saved in the `content/` +folder. -This one will just generate a simple output, with the default theme. It's not -really sexy, as it's a simple HTML output (without any style). +The above command will use the default theme to produce a simple site. It's not +very sexy, as it's just simple HTML output (without any style). -You can create your own style if you want, have a look to the help to see all +You can create your own style if you want. Have a look at the help to see all the options you can use:: $ pelican --help @@ -88,7 +91,7 @@ Kickstart a blog You also can use the `pelican-quickstart` script to start a new blog in seconds, by just answering few questions. Just run `pelican-quickstart` and -you're done! (Added in pelican 3) +you're done! (Added in Pelican 3.0) Pages ----- @@ -102,26 +105,26 @@ the menu. Importing an existing blog -------------------------- -It is possible to import your blog from dotclear, wordpress and an RSS feed using +It is possible to import your blog from Dotclear, WordPress, and RSS feeds using a simple script. See :ref:`import`. Translations ------------ It is possible to translate articles. To do so, you need to add a `lang` meta -in your articles/pages, and to set a `DEFAULT_LANG` setting (which is en by -default). -Then, only articles with this default language will be listed, and -each article will have a translation list. +attribute to your articles/pages and set a `DEFAULT_LANG` setting (which is +English [en] by default). With those settings in place, only articles with the +default language will be listed, and each article will be accompanied by a list +of available translations for that article. -Pelican uses the "slug" of two articles to compare if they are translations of -each others. So it's possible to define (in restructured text) the slug -directly. +Pelican uses the article's URL "slug" to determine if two or more articles are +translations of one another. The slug can be set manually in the file's +metadata; if not set explicitly, Pelican will auto-generate the slug from the +title of the article. -Here is an exemple of two articles (one in english and the other one in -french). +Here is an example of two articles, one in English and the other in French. -The english one:: +The English article:: Foobar is not dead ################## @@ -129,9 +132,9 @@ The english one:: :slug: foobar-is-not-dead :lang: en - That's true, foobar is still alive ! + That's true, foobar is still alive! -And the french one:: +And the French version:: Foobar n'est pas mort ! ####################### @@ -141,56 +144,67 @@ And the french one:: Oui oui, foobar est toujours vivant ! -Despite the text quality, you can see that only the slug is the same here. -You're not forced to define the slug that way, and it's completely possible to -have two translations with the same title (which defines the slug) +Post content quality notwithstanding, you can see that only item in common +between the two articles is the slug, which is functioning here as an +identifier. If you'd rather not explicitly define the slug this way, you must +then instead ensure that the translated article titles are identical, since the +slug will be auto-generated from the article title. -Syntactic recognition +Syntax highlighting --------------------- -Pelican is able to regognise the syntax you are using, and to colorize the -right way your block codes. To do so, you have to use the following syntax:: +Pelican is able to provide colorized syntax highlighting for your code blocks. +To do so, you have to use the following convention for reStructuredText:: .. code-block:: identifier your code goes here -The identifier is one of the lexers available `here -`_. +For Markdown, format your code blocks thusly: -You also can use the default `::` syntax:: + ::identifier + your code goes here + +The specified identifier should be one that appears on the +`list of available lexers `_. + +You also can use the default `::` syntax, in which case it will be assumed +that your code is written in Python. For reStructuredText:: :: your code goes here -It will be assumed that your code is witten in python. +For Markdown: + + :: + your code goes here Autoreload ---------- -It's possible to tell pelican to watch for your modifications, instead of -manually launching it each time you need. Use the `-r` option, or -`--autoreload`. +It's possible to tell Pelican to watch for your modifications, instead of +manually launching it every time you want to see your changes. To enable this, +run the `pelican` command with the `-r` or `--autoreload` options. Publishing drafts ----------------- -If you want to publish an article as a draft, for friends to review it for -instance, you can add a ``status: draft`` to its metadata, it will then be -available under the ``drafts`` folder, and not be listed under the index page nor -any category page. +If you want to publish an article as a draft (for friends to review before +publishing, for example), you can add a ``status: draft`` attribute to its +metadata. That article will then be output to the ``drafts`` folder and not +listed on the index page nor on any category page. Viewing the generated files --------------------------- -The files generated by pelican are static files, so you don't actually need -something special to see what's hapenning with the generated files. +The files generated by Pelican are static files, so you don't actually need +anything special to see what's happening with the generated files. -You can either run your browser on the files on your disk:: +You can either use your browser to open the files on your disk:: $ firefox output/index.html -Or run a simple web server using python:: +Or run a simple web server using Python:: cd output && python -m SimpleHTTPServer diff --git a/docs/importer.rst b/docs/importer.rst index 6e5734ae..377820af 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -10,11 +10,11 @@ Description ``pelican-import`` is a command line tool for converting articles from other software to ReStructuredText. The supported formats are: -- Wordpress XML export +- WordPress XML export - Dotclear export -- RSS/ATOM feed +- RSS/Atom feed -The conversion from HTML to ReStructuredText relies on `pandoc +The conversion from HTML to reStructuredText relies on `pandoc `_. For Dotclear, if the source posts are written with Markdown syntax, they will not be converted (as Pelican also supports Markdown). @@ -40,7 +40,7 @@ Optional arguments: Examples ======== -for Wordpress:: +for WordPress:: $ pelican-import --wpfile -o ~/output ~/posts.xml diff --git a/docs/index.rst b/docs/index.rst index 38ebf49c..c6638997 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,57 +1,58 @@ Pelican ####### -Pelican is a simple weblog generator, written in python. +Pelican is a simple weblog generator, written in Python. -* Write your weblog entries directly with your editor of choice (vim!) and - directly in restructured text, or markdown. -* A simple cli-tool to (re)generate the weblog. +* Write your weblog entries directly with your editor of choice (vim!) in + reStructuredText or Markdown +* A simple CLI tool to (re)generate the weblog * Easy to interface with DVCSes and web hooks -* Completely static output, so easy to host anywhere ! +* Completely static output is easy to host anywhere Features ======== Pelican currently supports: -* blog articles and simple pages -* comments, via an external service (disqus). Please notice that while - it's useful, it's an external service, and you'll not manage the - comments by yourself. It could potentially eat your data. (optional) -* easy theming (themes are done using `jinja2 `_) -* PDF generation of the articles/pages (optional). -* publication of articles in various languages -* RSS/Atom feeds -* wordpress/dotclear or RSS imports -* integration with various tools: twitter/google analytics (optional) +* Blog articles and pages +* Comments, via an external service (Disqus). (Please note that while + useful, Disqus is an external service, and thus the comment data will be + somewhat outside of your control and potentially subject to data loss.) +* Theming support (themes are created using `jinja2 `_) +* PDF generation of the articles/pages (optional) +* Publication of articles in multiple languages +* Atom/RSS feeds +* Code syntax highlighting +* Import from WordPress, Dotclear, or RSS feeds +* Integration with external tools: Twitter, Google Analytics, etc. (optional) Why the name "Pelican" ? ======================== -Heh, you didn't noticed? "Pelican" is an anagram for "Calepin" ;) +Heh, you didn't notice? "Pelican" is an anagram for "Calepin" ;) Source code =========== -You can access the source code via git on http://github.com/ametaireau/pelican/ +You can access the source code via git at http://github.com/ametaireau/pelican/ Feedback / Contact us ===================== -If you want to see new features in Pelican, dont hesitate to tell me, to clone +If you want to see new features in Pelican, don't hesitate to tell me, to clone the repository, etc. That's open source, dude! Contact me at "alexis at notmyidea dot org" for any request/feedback! You can also join the team at `#pelican on irc.freenode.org `_ -(or if you don't have any IRC client, using `the webchat +(or if you don't have any IRC client, use `the webchat `_) for quick feedback. Documentation ============= -A french version of the documentation is available at :doc:`fr/index`. +A French version of the documentation is available at :doc:`fr/index`. .. toctree:: :maxdepth: 2 diff --git a/docs/internals.rst b/docs/internals.rst index 269fe415..f0934825 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1,49 +1,48 @@ Pelican internals ################# -This section describe how pelican is working internally. As you'll see, it's -quite simple, but a bit of documentation doesn't hurt :) +This section describe how Pelican works internally. As you'll see, it's +quite simple, but a bit of documentation doesn't hurt. :) -You can also find in :doc:`report` an excerpt of a report the original author -wrote, with some software design information. +You can also find in the :doc:`report` section an excerpt of a report the +original author wrote with some software design information. .. _report: :doc:`report` Overall structure ================= -What `pelican` does, is taking a list of files, and processing them, to some -sort of output. Usually, the files are restructured text and markdown files, -and the output is a blog, but it can be anything you want. +What `pelican` does is take a list of files and process them into some +sort of output. Usually, the input files are reStructuredText and Markdown +files, and the output is a blog, but both input and output can be anything you +want. -I've separated the logic in different classes and concepts: +The logic is separated into different classes and concepts: -* `writers` are responsible of all the writing process of the - files. It's writing .html files, RSS feeds and so on. Since those operations - are commonly used, the object is created once, and then passed to the - generators. +* `writers` are responsible for writing files: .html files, RSS feeds, and so + on. Since those operations are commonly used, the object is created once and + then passed to the generators. -* `readers` are used to read from various formats (Markdown, and Restructured - Text for now, but the system is extensible). Given a file, they return - metadata (author, tags, category etc) and content (HTML formated) +* `readers` are used to read from various formats (Markdown and + reStructuredText for now, but the system is extensible). Given a file, they return + metadata (author, tags, category, etc.) and content (HTML-formatted). -* `generators` generate the different outputs. For instance, pelican comes with - `ArticlesGenerator` and `PageGenerator`, into others. Given - a configurations, they can do whatever they want. Most of the time it's - generating files from inputs. +* `generators` generate the different outputs. For instance, Pelican comes with + `ArticlesGenerator` and `PageGenerator`. Given a configuration, they can do + whatever they want. Most of the time, it's generating files from inputs. -* `pelican` also uses `templates`, so it's easy to write you own theme. The +* `pelican` also uses `templates`, so it's easy to write your own theme. The syntax is `jinja2`, and, trust me, really easy to learn, so don't hesitate - a second. + to jump in and build your own theme. -How to implement a new reader ? -=============================== +How to implement a new reader? +============================== -There is an awesome markup language you want to add to pelican ? -Well, the only thing you have to do is to create a class that have a `read` -method, that is returning an HTML content and some metadata. +Is there an awesome markup language you want to add to Pelican? +Well, the only thing you have to do is to create a class with a `read` +method that returns HTML content and some metadata. -Take a look to the Markdown reader:: +Take a look at the Markdown reader:: class MarkdownReader(Reader): enabled = bool(Markdown) @@ -63,31 +62,31 @@ Take a look to the Markdown reader:: metadata[name.lower()] = meta return content, metadata -Simple isn't it ? +Simple, isn't it? -If your new reader requires additional Python dependencies then you should wrap -their `import` statements in `try...except`. Then inside the reader's class -set the `enabled` class attribute to mark import success or failure. This makes -it possible for users to continue using their favourite markup method without -needing to install modules for all the additional formats they don't use. +If your new reader requires additional Python dependencies, then you should wrap +their `import` statements in a `try...except` block. Then inside the reader's +class, set the `enabled` class attribute to mark import success or failure. +This makes it possible for users to continue using their favourite markup method +without needing to install modules for formats they don't use. -How to implement a new generator ? -================================== +How to implement a new generator? +================================= -Generators have basically two important methods. You're not forced to create -both, only the existing ones will be called. +Generators have two important methods. You're not forced to create +both; only the existing ones will be called. -* `generate_context`, that is called in a first place, for all the generators. +* `generate_context`, that is called first, for all the generators. Do whatever you have to do, and update the global context if needed. This context is shared between all generators, and will be passed to the - templates. For instance, the `PageGenerator` `generate_context` method find - all the pages, transform them into objects, and populate the context with - them. Be careful to *not* output anything using this context at this stage, - as it is likely to change by the effect of others generators. + templates. For instance, the `PageGenerator` `generate_context` method finds + all the pages, transforms them into objects, and populates the context with + them. Be careful *not* to output anything using this context at this stage, + as it is likely to change by the effect of other generators. -* `generate_output` is then called. And guess what is it made for ? Oh, - generating the output :) That's here that you may want to look at the context - and call the methods of the `writer` object, that is passed at the first +* `generate_output` is then called. And guess what is it made for? Oh, + generating the output. :) It's here that you may want to look at the context + and call the methods of the `writer` object that is passed as the first argument of this function. In the `PageGenerator` example, this method will - look at all the pages recorded in the global context, and output a file on + look at all the pages recorded in the global context and output a file on the disk (using the writer method `write_file`) for each page encountered. diff --git a/docs/pelican-themes.rst b/docs/pelican-themes.rst index bd2dba77..c7cbc5b7 100644 --- a/docs/pelican-themes.rst +++ b/docs/pelican-themes.rst @@ -57,11 +57,11 @@ With ``pelican-themes``, you can see the available themes by using the ``-l`` or two-column@ simple -In this example, we can see there is 3 themes available: ``notmyidea``, ``simple`` and ``two-column``. +In this example, we can see there are three themes available: ``notmyidea``, ``simple``, and ``two-column``. -``two-column`` is prefixed with an ``@`` because this theme is not copied to the Pelican theme path, but just linked to it (see `Creating symbolic links`_ for details about creating symbolic links). +``two-column`` is prefixed with an ``@`` because this theme is not copied to the Pelican theme path, but is instead just linked to it (see `Creating symbolic links`_ for details about creating symbolic links). -Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get a more verbose output, like this: +Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get more verbose output, like this: .. code-block:: console @@ -95,8 +95,8 @@ This option takes as argument the path(s) of the theme(s) you want to install, a Removing themes """"""""""""""" -Pelican themes can also removes themes from the Pelican themes path. -The ``-r`` or ``--remove`` takes as argument the name(s) of the theme(s) you want to remove, and can be combined with the ``--verbose`` option. +The ``pelican-themes`` command can also remove themes from the Pelican themes path. +The ``-r`` or ``--remove`` option takes as argument the name(s) of the theme(s) you want to remove, and can be combined with the ``--verbose`` option. .. code-block:: console @@ -113,7 +113,7 @@ The ``-r`` or ``--remove`` takes as argument the name(s) of the theme(s) you wan Creating symbolic links """"""""""""""""""""""" -``pelican-themes`` can also install themes by creating symbolic links instead of copying the whole themes in the Pelican themes path. +``pelican-themes`` can also install themes by creating symbolic links instead of copying entire themes into the Pelican themes path. To symbolically link a theme, you can use the ``-s`` or ``--symlink``, which works exactly as the ``--install`` option: @@ -152,7 +152,7 @@ The ``--install``, ``--remove`` and ``--symlink`` option are not mutually exclus --symlink ~/Dev/Python/pelican-themes/two-column \ --verbose -In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr`` +In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr`` diff --git a/docs/settings.rst b/docs/settings.rst index 9d34fae0..2d40d4ec 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -6,26 +6,26 @@ the command line:: $ pelican -s path/to/your/settingsfile.py path -Settings are given as the form of a python module (a file). You can have an +Settings are configured in the form of a Python module (a file). You can see an example by looking at `/samples/pelican.conf.py `_ -All the settings identifiers must be set in caps, otherwise they will not be +All the setting identifiers must be set in all-caps, otherwise they will not be processed. The settings you define in the configuration file will be passed to the -templates, it allows you to use them to add site-wide contents if you need. +templates, which allows you to use your settings to add site-wide content. -Here is a list of settings for pelican, regarding the different features. +Here is a list of settings for Pelican: Basic settings ============== ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== -`ARTICLE_PERMALINK_STRUCTURE` (``''``) Empty by default. Allows to render URLs in a - particular way, see below. +`ARTICLE_PERMALINK_STRUCTURE` (``''``) Empty by default. Enables some customization of URL + structure (see below for more detail). `AUTHOR` Default author (put your name) `CLEAN_URLS` (``False``) If set to `True`, the URLs will not be suffixed by `.html`, so you will have to setup URL rewriting on @@ -33,68 +33,71 @@ Setting name (default value) what does it do? `DATE_FORMATS` (``{}``) If you do manage multiple languages, you can set the date formatting here. See "Date format and locales" section below for details. -`DEFAULT_CATEGORY` (``'misc'``) The default category to fallback on. +`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on. `DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use. -`DISPLAY_PAGES_ON_MENU` (``True``) Display or not the pages on the menu of the - template. Templates can follow or not this - settings. -`FALLBACK_ON_FS_DATE` (``True``) If True, pelican will use the file system - dates infos (mtime) if it can't get - informations from the metadata +`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the + template. Templates may or not honor this + setting. +`FALLBACK_ON_FS_DATE` (``True``) If True, Pelican will use the file system + timestamp information (mtime) if it can't get + date information from the metadata. `JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use. -`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory and just +`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory as well as the generated files. `LOCALE` (''[#]_) Change the locale. A list of locales can be provided here or a single string representing one locale. When providing a list, all the locales will be tried until one works. `MARKUP` (``('rst', 'md')``) A list of available markup languages you want - to use. For the moment, only available values + to use. For the moment, the only available values are `rst` and `md`. -`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the markdown processor +`MD_EXTENSIONS` (``('codehilite','extra')``) A list of the extensions that the Markdown processor will use. Refer to the extensions chapter in the Python-Markdown documentation for a complete list of supported extensions. `OUTPUT_PATH` (``'output/'``) Where to output the generated files. -`PATH` (``None``) path to look at for input files. +`PATH` (``None``) Path to look at for input files. `PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions of your documents. You will need to install `rst2pdf`. -`RELATIVE_URLS` (``True``) Defines if pelican should use relative urls or +`RELATIVE_URLS` (``True``) Defines whether Pelican should use relative URLs or not. `SITENAME` (``'A Pelican Blog'``) Your site name -`SITEURL` base URL of your website. Note that this is - not a way to tell pelican to use relative urls - or static ones. You should rather use the - `RELATIVE_URL` setting for such use. +`SITEURL` Base URL of your website. Note that this is + not a way to tell Pelican whether to use relative URLs + or static ones. You should instead use the + `RELATIVE_URL` setting for that purpose. `STATIC_PATHS` (``['images']``) The static paths you want to have accessible on the output path "static". By default, - pelican will copy the 'images' folder to the + Pelican will copy the 'images' folder to the output folder. `TIMEZONE` The timezone used in the date information, to - generate atom and rss feeds. See the "timezone" + generate Atom and RSS feeds. See the "timezone" section below for more info. ================================================ ===================================================== -.. [#] Default is the system locale. Default is to delete the output directory. +.. [#] Default is the system locale. + Article permalink structure --------------------------- -Allow to render articles sorted by date, in case you specify a format as -specified in the example. It follows the python datetime directives: + +This setting allows you to output your articles sorted by date, provided that +you specify a format as specified below. This format follows the Python +``datetime`` directives: * %Y: Year with century as a decimal number. * %m: Month as a decimal number [01,12]. * %d: Day of the month as a decimal number [01,31]. -Note: if you specify a datetime directive, it will be substituted using the -date metadata field into the rest file. if the date is not specified, pelican -will rely on the mtime of your file. +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 +particular file, Pelican will rely on the file's mtime timestamp. -Check the python datetime documentation at http://bit.ly/cNcJUC for more +Check the Python datetime documentation at http://bit.ly/cNcJUC for more information. -Also, you can use any metadata in the restructured text files: +Also, you can use other file metadata attributes as well: * category: '%(category)s' * author: '%(author)s' @@ -103,20 +106,19 @@ Also, you can use any metadata in the restructured text files: Example usage: -* '/%Y/%m/' it will be something like '/2011/07/sample-post.html'. -* '/%Y/%(category)s/' it will be something like '/2011/life/sample-post.html'. +* '/%Y/%m/' will render something like '/2011/07/sample-post.html'. +* '/%Y/%(category)s/' will render something like '/2011/life/sample-post.html'. Timezone -------- -If no timezone is defined, UTC is assumed. This means that the generated atom -and rss feeds will have wrong date information if your locale is not UTC. +If no timezone is defined, UTC is assumed. This means that the generated Atom +and RSS feeds will contain incorrect date information if your locale is not UTC. Pelican issues a warning in case this setting is not defined, as it was not -mandatory in old versions. +mandatory in previous versions. -Have a look at `the wikipedia page`_ to get a list of values to set your -timezone. +Have a look at `the wikipedia page`_ to get a list of valid timezone values. .. _the wikipedia page: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones @@ -124,9 +126,9 @@ timezone. Date format and locale ---------------------- -If no DATE_FORMAT is set, fallback to DEFAULT_DATE_FORMAT. If you need to -maintain multiple languages with different date format, you can set this dict -using language name (``lang`` in your posts) as key. About available format +If no DATE_FORMAT is set, fall back to DEFAULT_DATE_FORMAT. If you need to +maintain multiple languages with different date formats, you can set this dict +using language name (``lang`` in your posts) as key. Regarding available format codes, see `strftime document of python`_ : DATE_FORMAT = { @@ -140,8 +142,8 @@ You can set locale to further control date format: 'en_US', 'ja_JP' # On Unix/Linux ) -Also, it is possible to set different locale settings for each language, if you -put (locale, format) tuple in dict, and this will override the LOCALE setting +Also, it is possible to set different locale settings for each language. If you +put (locale, format) tuples in the dict, this will override the LOCALE setting above: # On Unix/Linux @@ -156,8 +158,9 @@ above: 'jp': ('jpn','%Y-%m-%d(%a)'), } -For available list of `locales on Windows`_ . On Unix/Linux usually you can get -a list of available locales with command ``locale -a``, see manpage `locale(1)`_ for help. +This is a list of available `locales on Windows`_ . On Unix/Linux, usually you +can get a list of available locales via the ``locale -a`` command; see manpage +`locale(1)`_ for more information. .. _strftime document of python: http://docs.python.org/library/datetime.html#strftime-strptime-behavior @@ -169,26 +172,26 @@ a list of available locales with command ``locale -a``, see manpage `locale(1)`_ Feed settings ============= -By default, pelican uses atom feeds. However, it is possible to use RSS feeds -instead, at your covenience. +By default, Pelican uses Atom feeds. However, it is also possible to use RSS +feeds if you prefer. Pelican generates category feeds as well as feeds for all your articles. It does -not generate feeds for tags per default, but it is possible to do so using +not generate feeds for tags by default, but it is possible to do so using the ``TAG_FEED`` and ``TAG_FEED_RSS`` settings: ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== -`CATEGORY_FEED` ('feeds/%s.atom.xml'[2]_) Where to put the atom categories feeds. -`CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the categories rss feeds. -`FEED` (``'feeds/all.atom.xml'``) relative url to output the atom feed. -`FEED_RSS` (``None``, i.e. no RSS) relative url to output the rss feed. -`TAG_FEED` (``None``, ie no tag feed) relative url to output the tags atom feed. It should - be defined using a "%s" matchin the tag name -`TAG_FEED_RSS` (``None``, ie no RSS tag feed) relative url to output the tag RSS feed -`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feeds are - unrestricted by default. +`CATEGORY_FEED` ('feeds/%s.atom.xml'[2]_) Where to put the category Atom feeds. +`CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the category RSS feeds. +`FEED` (``'feeds/all.atom.xml'``) Relative URL to output the Atom feed. +`FEED_RSS` (``None``, i.e. no RSS) Relative URL to output the RSS feed. +`TAG_FEED` (``None``, ie no tag feed) Relative URL to output the tag Atom feed. It should + be defined using a "%s" match in the tag name. +`TAG_FEED_RSS` (``None``, ie no RSS tag feed) Relative URL to output the tag RSS feed +`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feed item + quantity is unrestricted by default. ================================================ ===================================================== .. [2] %s is the name of the category. @@ -196,14 +199,15 @@ Setting name (default value) what does it do? Pagination ========== -The default behaviour of pelican is to list all the articles titles alongside -with a short description of them on the index page. While it works pretty well -for little to medium blogs, it is convenient to have a way to paginate this. +The default behaviour of Pelican is to list all the article titles along +with a short description on the index page. While it works pretty well +for small-to-medium blogs, for sites with large quantity of articles it would +be convenient to have a way to paginate the list. You can use the following settings to configure the pagination. ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== `DEFAULT_ORPHANS` (0) The minimum number of articles allowed on the last page. Use this when you don't want to @@ -220,11 +224,11 @@ If you want to generate a tag cloud with all your tags, you can do so using the following settings. ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== `TAG_CLOUD_STEPS` (4) Count of different font sizes in the tag cloud. -`TAG_CLOUD_MAX_ITEMS` (100) Maximum tags count in the cloud. +`TAG_CLOUD_MAX_ITEMS` (100) Maximum number of tags in the cloud. ================================================ ===================================================== The default theme does not support tag clouds, but it is pretty easy to add:: @@ -235,34 +239,34 @@ The default theme does not support tag clouds, but it is pretty easy to add:: {% endfor %} -You should then also define a CSS with the appropriate classes (tag-0 to tag-N, where -N matches `TAG_CLOUD_STEPS` -1. +You should then also define a CSS style with the appropriate classes (tag-0 to tag-N, where +N matches `TAG_CLOUD_STEPS` -1). Translations ============ -Pelican offers a way to translate articles. See the section on getting started for -more information about that. +Pelican offers a way to translate articles. See the Getting Started section for +more information. ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== `DEFAULT_LANG` (``'en'``) The default language to use. -`TRANSLATION_FEED` ('feeds/all-%s.atom.xml'[3]_) Where to put the RSS feed for translations. +`TRANSLATION_FEED` ('feeds/all-%s.atom.xml'[3]_) Where to put the feed for translations. ================================================ ===================================================== .. [3] %s is the language -Ordering contents +Ordering content ================= ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== -`REVERSE_ARCHIVE_ORDER` (``False``) Reverse the archives order. (True makes it in - descending order: the newer first) -`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True makes it in - descending order, default is alphabetically) +`REVERSE_ARCHIVE_ORDER` (``False``) Reverse the archives list order. (True: orders by date + in descending order, with newer articles first.) +`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True: lists by reverse + alphabetical order; default lists alphabetically.) ================================================ ===================================================== Theming @@ -272,45 +276,45 @@ Theming is addressed in a dedicated section (see :ref:`theming-pelican`). However, here are the settings that are related to theming. ================================================ ===================================================== -Setting name (default value) what does it do? +Setting name (default value) What does it do? ================================================ ===================================================== -`THEME` theme to use to produce the output. can be the +`THEME` Theme to use to produce the output. Can be the complete static path to a theme folder, or chosen between the list of default themes (see below) `THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default - values is `static`, but if your theme has + value is `static`, but if your theme has other static paths, you can put them here. -`CSS_FILE` (``'main.css'``) specify the CSS file you want to load +`CSS_FILE` (``'main.css'``) Specify the CSS file you want to load. ================================================ ===================================================== -By default, two themes are availablee. You can specify them using the `-t` option: +By default, two themes are available. You can specify them using the `-t` option: * notmyidea * simple (a synonym for "full text" :) -You can define your own theme too, and specify it's emplacement in the same -way (be sure to specify the full absolute path to it). +You can define your own theme too, and specify its placement in the same +manner. (Be sure to specify the full absolute path to it.) Here is `a guide on how to create your theme -`_ +`_ You can find a list of themes at http://github.com/ametaireau/pelican-themes. -Pelican comes with :doc:`pelican-themes` a small script for managing themes. +Pelican comes with :doc:`pelican-themes`, a small script for managing themes. The `notmyidea` theme can make good use of the following settings. I recommend -to use them too in your themes. +using them in your themes as well. ======================= ======================================================= -Setting name what does it do ? +Setting name What does it do ? ======================= ======================================================= -`DISQUS_SITENAME` Pelican can handle disqus comments, specify the - sitename you've filled in on disqus -`GITHUB_URL` Your github URL (if you have one), it will then - use it to create a github ribbon. -`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate google analytics. -`MENUITEMS` A list of tuples (Title, Url) for additional menu +`DISQUS_SITENAME` Pelican can handle Disqus comments. Specify the + Disqus sitename identifier here. +`GITHUB_URL` Your GitHub URL (if you have one). It will then + use this information to create a GitHub ribbon. +`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate Google Analytics. +`MENUITEMS` A list of tuples (Title, URL) for additional menu items to appear at the beginning of the main menu. `PIWIK_URL` URL to your Piwik server - without 'http://' at the beginning. @@ -318,17 +322,17 @@ Setting name what does it do ? you have to include this setting too. (optional) `PIWIK_SITE_ID` ID for the monitored website. You can find the ID in the Piwik admin interface > settings > websites. -`LINKS` A list of tuples (Title, Url) for links to appear on +`LINKS` A list of tuples (Title, URL) for links to appear on the header. -`SOCIAL` A list of tuples (Title, Url) to appear in the +`SOCIAL` A list of tuples (Title, URL) to appear in the "social" section. -`TWITTER_USERNAME` Allows to add a button on the articles to tweet about - them. Add you twitter username if you want this - button to appear. +`TWITTER_USERNAME` Allows for adding a button to articles to encourage + others to tweet about them. Add your Twitter username + if you want this button to appear. ======================= ======================================================= -In addition, you can use the "wide" version of the `notmyidea` theme, by -adding that in your configuration:: +In addition, you can use the "wide" version of the `notmyidea` theme by +adding the following to your configuration:: CSS_FILE = "wide.css" diff --git a/docs/themes.rst b/docs/themes.rst index a20fd099..e0583882 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -1,10 +1,10 @@ .. _theming-pelican: -How to create themes for pelican +How to create themes for Pelican ################################ Pelican uses the great `jinja2 `_ templating engine to -generate it's HTML output. The jinja2 syntax is really simple. If you want to +generate its HTML output. The jinja2 syntax is really simple. If you want to create your own theme, feel free to take inspiration from the "simple" theme, which is available `here `_ @@ -29,179 +29,183 @@ To make your own theme, you must follow the following structure:: ├── tag.html // processed for each tag └── tags.html // must list all the tags. Can be a tag cloud. -* `static` contains all the static content. It will be copied on the output - `theme/static` folder then. I've put the css and image folders, but they are +* `static` contains all the static assets, which will be copied to the output + `theme/static` folder. I've put the CSS and image folders here, but they are just examples. Put what you need here. * `templates` contains all the templates that will be used to generate the content. - I've just put the mandatory templates here, you can define your own if it helps - you to organize yourself while doing the theme. + I've just put the mandatory templates here; you can define your own if it helps + you keep things organized while creating your theme. Templates and variables ======================= -It's using a simple syntax, that you can embbed into your html pages. -This document describes which templates should exist on a theme, and which -variables will be passed to each template, while generating it. +The idea is to use a simple syntax that you can embed into your HTML pages. +This document describes which templates should exist in a theme, and which +variables will be passed to each template at generation time. All templates will receive the variables defined in your settings file, if they -are in caps. You can access them directly. +are in all-caps. You can access them directly. Common variables ---------------- -All of those settings will be given to all templates. +All of these settings will be available to all templates. ============= =================================================== Variable Description ============= =================================================== -articles That's the list of articles, ordered desc. by date - all the elements are `Article` objects, so you can - access their properties (e.g. title, summary, author - etc.). -dates The same list of article, but ordered by date, - ascending. -tags A dict containing each tags (keys), and the list of - relative articles. -categories A dict containing each category (keys), and the - list of relative articles. -pages The list of pages. +articles The list of articles, ordered descending by date + All the elements are `Article` objects, so you can + access their attributes (e.g. title, summary, author + etc.) +dates The same list of articles, but ordered by date, + ascending +tags A key-value dict containing the tags (the keys) and + the list of respective articles (the values) +categories A key-value dict containing the categories (keys) + and the list of respective articles (values) +pages The list of pages ============= =================================================== index.html ---------- -Home page of your blog, will finally remain at output/index.html. +This is the home page of your blog, generated at output/index.html. -If pagination is active, next pages will remain at output/index`n`.html. +If pagination is active, subsequent pages will reside in output/index`n`.html. =================== =================================================== Variable Description =================== =================================================== -articles_paginator A paginator object of article list. -articles_page The current page of articles. -dates_paginator A paginator object of article list, ordered by date, - ascending. +articles_paginator A paginator object for the list of articles +articles_page The current page of articles +dates_paginator A paginator object for the article list, ordered by + date, ascending. dates_page The current page of articles, ordered by date, ascending. -page_name 'index'. Useful for pagination links. +page_name 'index' -- useful for pagination links =================== =================================================== author.html ------------- -This template will be processed for each of the existing authors, and will -finally remain at output/author/`author_name`.html. +This template will be processed for each of the existing authors, with +output generated at output/author/`author_name`.html. -If pagination is active, next pages will remain at +If pagination is active, subsequent pages will reside at output/author/`author_name``n`.html. =================== =================================================== Variable Description =================== =================================================== -author The name of the author being processed. -articles Articles of this author. -dates Articles of this author, but ordered by date, - ascending. -articles_paginator A paginator object of article list. -articles_page The current page of articles. -dates_paginator A paginator object of article list, ordered by date, - ascending. +author The name of the author being processed +articles Articles by this author +dates Articles by this author, but ordered by date, + ascending +articles_paginator A paginator object for the list of articles +articles_page The current page of articles +dates_paginator A paginator object for the article list, ordered by + date, ascending. dates_page The current page of articles, ordered by date, ascending. -page_name 'author/`author_name`'. Useful for pagination - links. +page_name 'author/`author_name`' -- useful for pagination + links =================== =================================================== category.html ------------- -This template will be processed for each of the existing categories, and will -finally remain at output/category/`category_name`.html. +This template will be processed for each of the existing categories, with +output generated at output/category/`category_name`.html. -If pagination is active, next pages will remain at +If pagination is active, subsequent pages will reside at output/category/`category_name``n`.html. =================== =================================================== Variable Description =================== =================================================== -category The name of the category being processed. -articles Articles of this category. -dates Articles of this category, but ordered by date, - ascending. -articles_paginator A paginator object of article list. -articles_page The current page of articles. -dates_paginator A paginator object of article list, ordered by date, - ascending. +category The name of the category being processed +articles Articles for this category +dates Articles for this category, but ordered by date, + ascending +articles_paginator A paginator object for the list of articles +articles_page The current page of articles +dates_paginator A paginator object for the list of articles, + ordered by date, ascending dates_page The current page of articles, ordered by date, - ascending. -page_name 'category/`category_name`'. Useful for pagination - links. + ascending +page_name 'category/`category_name`' -- useful for pagination + links =================== =================================================== article.html ------------- -This template will be processed for each article. .html files will be output -in output/`article_name`.html. Here are the specific variables it gets. +This template will be processed for each article, with .html files saved +as output/`article_name`.html. Here are the specific variables it gets. ============= =================================================== Variable Description ============= =================================================== -article The article object to be displayed. -category The name of the category of the current article. +article The article object to be displayed +category The name of the category for the current article ============= =================================================== page.html --------- -For each page, this template will be processed. It will create .html files in -output/`page_name`.html. +This template will be processed for each page, with corresponding .html files +saved as output/`page_name`.html. ============= =================================================== Variable Description ============= =================================================== -page The page object to be displayed. You can access to - its title, slug and content. +page The page object to be displayed. You can access its + title, slug, and content. ============= =================================================== tag.html -------- -For each tag, this template will be processed. It will create .html files in -output/tag/`tag_name`.html. +This template will be processed for each tag, with corresponding .html files +saved as output/tag/`tag_name`.html. -If pagination is active, next pages will remain at +If pagination is active, subsequent pages will reside at output/tag/`tag_name``n`.html. =================== =================================================== Variable Description =================== =================================================== -tag The name of the tag being processed. -articles Articles related to this tag. +tag The name of the tag being processed +articles Articles related to this tag dates Articles related to this tag, but ordered by date, - ascending. -articles_paginator A paginator object of article list. -articles_page The current page of articles. -dates_paginator A paginator object of article list, ordered by date, - ascending. + ascending +articles_paginator A paginator object for the list of articles +articles_page The current page of articles +dates_paginator A paginator object for the list of articles, + ordered by date, ascending dates_page The current page of articles, ordered by date, - ascending. -page_name 'tag/`tag_name`'. Useful for pagination links. + ascending +page_name 'tag/`tag_name`' -- useful for pagination links =================== =================================================== Inheritance =========== -Since version 3, pelican supports inheritance from the ``simple`` theme, so you can reuse the templates of the ``simple`` theme in your own themes: +Since version 3.0, Pelican supports inheritance from the ``simple`` theme, so +you can re-use the ``simple`` theme templates in your own themes. -If one of the mandatory files in the ``templates/`` directory of your theme is missing, it will be replaced by the matching template from the ``simple`` theme, so if the HTML structure of a template of the ``simple`` theme is right for you, you don't have to rewrite it from scratch. +If one of the mandatory files in the ``templates/`` directory of your theme is +missing, it will be replaced by the matching template from the ``simple`` theme. +So if the HTML structure of a template in the ``simple`` theme is right for you, +you don't have to write a new template from scratch. -You can also extend templates of the ``simple`` themes in your own themes by using the ``{% extends %}`` directive as in the following example: +You can also extend templates from the ``simple`` themes in your own themes by using the ``{% extends %}`` directive as in the following example: .. code-block:: html+jinja - {% extends "!simple/index.html" %} + {% extends "!simple/index.html" %} {% extends "index.html" %} @@ -226,10 +230,10 @@ The first file is the ``templates/base.html`` template: {% endblock %} -1. On the first line, we extend the ``base.html`` template of the ``simple`` theme, so we don't have to rewrite the entire file. -2. On the third line, we open the ``head`` block which has already been defined in the ``simple`` theme +1. On the first line, we extend the ``base.html`` template from the ``simple`` theme, so we don't have to rewrite the entire file. +2. On the third line, we open the ``head`` block which has already been defined in the ``simple`` theme. 3. On the fourth line, the function ``super()`` keeps the content previously inserted in the ``head`` block. -4. On the fifth line, we append a stylesheet to the page +4. On the fifth line, we append a stylesheet to the page. 5. On the last line, we close the ``head`` block. This file will be extended by all the other templates, so the stylesheet will be linked from all pages. diff --git a/docs/tips.rst b/docs/tips.rst index 9ff7f9ce..6ddc3d33 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -1,22 +1,22 @@ Tips #### -Here are some tips about pelican, which you might find useful. +Here are some tips about Pelican that you might find useful. -Publishing to github +Publishing to GitHub ==================== -Github comes with an interesting "pages" feature: you can upload things there -and it will be available directly from their servers. As pelican is a static +GitHub comes with an interesting "pages" feature: you can upload things there +and it will be available directly from their servers. As Pelican is a static file generator, we can take advantage of this. The excellent `ghp-import `_ makes this -eally easy. You would have to install it:: +really easy. You will have to install it:: $ pip install ghp-import -Then, considering a repository containing your articles, you would simply have -to run pelican and upload the output to github:: +Then, given a repository containing your articles, you would simply have +to run Pelican and upload the output to GitHub:: $ pelican -s pelican.conf.py . $ ghp-import output @@ -24,8 +24,8 @@ to run pelican and upload the output to github:: And that's it. -If you want you can put that directly into a post commit hook, so each time you -commit, your blog is up to date on github! +If you want, you can put that directly into a post-commit hook, so each time you +commit, your blog is up to date on GitHub! Put the following into `.git/hooks/post-commit`:: From 4861a15774c176e13f6e5ba811cf48762428622d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 6 Mar 2012 08:12:15 -0800 Subject: [PATCH 03/34] Update outdated links to documentation and add Contribute link to README for issue #227 --- README.rst | 9 +++++---- pelican/themes/notmyidea/templates/base.html | 2 +- pelican/themes/simple/templates/base.html | 4 ++-- setup.py | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 0a27bdcd..b521d430 100644 --- a/README.rst +++ b/README.rst @@ -39,14 +39,15 @@ Source code You can access the source code via git at: https://github.com/ametaireau/pelican -If you feel hackish, have a look at the `pelican's internals explanation +If you feel hackish, have a look at the explanation of `Pelican's internals `_. Feedback / Contact us -===================== +--------------------- -If you want to see new features in Pelican, don't hesitate to tell me, to clone -the repository, etc. That's open source, dude! +If you want to see new features in Pelican, don't hesitate to offer suggestions, +clone the repository, etc. There are many ways to `contribute +`_. That's open source, dude! Contact me at "alexis at notmyidea dot org" for any request/feedback! You can also join the team at `#pelican on irc.freenode.org diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index a34b5d5a..4a4c70c6 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -71,7 +71,7 @@
- Proudly powered by pelican, which takes great advantages of python. + Proudly powered by Pelican, which takes great advantage of Python.

The theme is by Smashing Magazine, thanks!

diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index dc266773..a8fad2db 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -29,8 +29,8 @@ {% endblock %} diff --git a/setup.py b/setup.py index e01448ed..9b299085 100755 --- a/setup.py +++ b/setup.py @@ -20,10 +20,10 @@ if sys.platform.startswith('win'): setup( name = "pelican", version = VERSION, - url = 'http://alexis.notmyidea.org/pelican/', + url = 'http://pelican.notmyidea.org/', author = 'Alexis Metaireau', author_email = 'alexis@notmyidea.org', - description = "A tool to generate a static blog, with restructured text (or markdown) input files.", + description = "A tool to generate a static blog from reStructuredText or Markdown input files.", long_description=open('README.rst').read(), packages = ['pelican'], include_package_data = True, From 67540997307caac11e4f61567a7edc2440adc9d5 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Thu, 22 Dec 2011 15:13:12 +0000 Subject: [PATCH 04/34] Create a Category class which has a url property --- pelican/contents.py | 20 +++++++++++++++++++ pelican/generators.py | 4 ++-- pelican/readers.py | 2 ++ .../notmyidea/templates/article_infos.html | 2 +- .../notmyidea/templates/categories.html | 2 +- .../themes/simple/templates/categories.html | 2 +- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 49f30316..aab6b330 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -143,6 +143,26 @@ class Quote(Page): base_properties = ('author', 'date') +class Category(object): + def __init__(self, category): + self.category = unicode(category) + + def __hash__(self): + return hash(self.category) + + def __eq__(self, other): + return self.category == unicode(other) + + def __str__(self): + return str(self.category) + + def __unicode__(self): + return self.category + + @property + def url(self): + return 'category/%s.html' % self + def is_valid_content(content, f): try: content.check_properties() diff --git a/pelican/generators.py b/pelican/generators.py index 6715126a..116c0b0b 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -187,7 +187,7 @@ class ArticlesGenerator(Generator): category_template = self.get_template('category') for cat, articles in self.categories: dates = [article for article in self.dates if article in articles] - write('category/%s.html' % cat, category_template, self.context, + write(cat.url, category_template, self.context, category=cat, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='category/%s' % cat) @@ -228,7 +228,7 @@ class ArticlesGenerator(Generator): category = os.path.basename(os.path.dirname(f)).decode('utf-8') if category != '': - metadata['category'] = unicode(category) + metadata['category'] = Category(category) if 'date' not in metadata.keys()\ and self.settings['FALLBACK_ON_FS_DATE']: diff --git a/pelican/readers.py b/pelican/readers.py index ee3ecd2c..1a62237a 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -15,6 +15,7 @@ except ImportError: Markdown = False import re +from pelican.contents import Category from pelican.utils import get_date, open @@ -22,6 +23,7 @@ _METADATA_PROCESSORS = { 'tags': lambda x: map(unicode.strip, unicode(x).split(',')), 'date': lambda x: get_date(x), 'status': unicode.strip, + 'category': Category, } def _process_metadata(name, value): diff --git a/pelican/themes/notmyidea/templates/article_infos.html b/pelican/themes/notmyidea/templates/article_infos.html index e1803be8..09ecd595 100644 --- a/pelican/themes/notmyidea/templates/article_infos.html +++ b/pelican/themes/notmyidea/templates/article_infos.html @@ -8,7 +8,7 @@ By {{ article.author }} {% endif %} -

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

+

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

{% include 'taglist.html' %} {% include 'translations.html' %}
diff --git a/pelican/themes/notmyidea/templates/categories.html b/pelican/themes/notmyidea/templates/categories.html index 7e4bd2c9..e4d9d0a7 100644 --- a/pelican/themes/notmyidea/templates/categories.html +++ b/pelican/themes/notmyidea/templates/categories.html @@ -2,7 +2,7 @@ {% block content %}
    {% for category, articles in categories %} -
  • {{ category }}
  • +
  • {{ category }}
  • {% endfor %}
{% endblock %} diff --git a/pelican/themes/simple/templates/categories.html b/pelican/themes/simple/templates/categories.html index 7e4bd2c9..e29be0ca 100644 --- a/pelican/themes/simple/templates/categories.html +++ b/pelican/themes/simple/templates/categories.html @@ -2,7 +2,7 @@ {% block content %}
    {% for category, articles in categories %} -
  • {{ category }}
  • +
  • {{ category }}
  • {% endfor %}
{% endblock %} From f9ed01bb643ab6ad30d06517751f9002568bd0fd Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Thu, 22 Dec 2011 15:43:44 +0000 Subject: [PATCH 05/34] Create a Tag class which has a url property --- pelican/contents.py | 20 +++++++++++++++++++ pelican/generators.py | 2 +- pelican/readers.py | 4 ++-- .../themes/notmyidea/templates/taglist.html | 2 +- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index aab6b330..cca01911 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -163,6 +163,26 @@ class Category(object): def url(self): return 'category/%s.html' % self +class Tag(object): + def __init__(self, tag): + self.tag = unicode.strip(tag) + + def __hash__(self): + return hash(self.tag) + + def __eq__(self, other): + return self.tag == unicode(tag) + + def __str__(self): + return str(self.tag) + + def __unicode__(self): + return self.tag + + @property + def url(self): + return 'tag/%s.html' % self + def is_valid_content(content, f): try: content.check_properties() diff --git a/pelican/generators.py b/pelican/generators.py index 116c0b0b..816a6755 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -179,7 +179,7 @@ class ArticlesGenerator(Generator): for tag, articles in self.tags.items(): articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in self.dates if article in articles] - write('tag/%s.html' % tag, tag_template, self.context, tag=tag, + write(tag.url, tag_template, self.context, tag=tag, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='tag/%s' % tag) diff --git a/pelican/readers.py b/pelican/readers.py index 1a62237a..6011c272 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -15,12 +15,12 @@ except ImportError: Markdown = False import re -from pelican.contents import Category +from pelican.contents import Category, Tag from pelican.utils import get_date, open _METADATA_PROCESSORS = { - 'tags': lambda x: map(unicode.strip, unicode(x).split(',')), + 'tags': lambda x: map(Tag, unicode(x).split(',')), 'date': lambda x: get_date(x), 'status': unicode.strip, 'category': Category, diff --git a/pelican/themes/notmyidea/templates/taglist.html b/pelican/themes/notmyidea/templates/taglist.html index 0f4862d0..c792fd7d 100644 --- a/pelican/themes/notmyidea/templates/taglist.html +++ b/pelican/themes/notmyidea/templates/taglist.html @@ -1,2 +1,2 @@ -{% if article.tags %}

tags: {% for tag in article.tags %}{{ tag }}{% endfor %}

{% endif %} +{% if article.tags %}

tags: {% for tag in article.tags %}{{ tag }}{% endfor %}

{% endif %} {% if PDF_PROCESSOR %}

get the pdf

{% endif %} From ff9c7861497e9056df911bed95d26567b97749d3 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Thu, 22 Dec 2011 16:22:34 +0000 Subject: [PATCH 06/34] Create a Author class which has a url property --- pelican/contents.py | 47 +++++++++---------- pelican/generators.py | 2 +- pelican/readers.py | 3 +- .../notmyidea/templates/article_infos.html | 2 +- pelican/themes/simple/templates/article.html | 2 +- pelican/themes/simple/templates/index.html | 2 +- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index cca01911..8cd36186 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -37,9 +37,9 @@ class Page(object): # default author to the one in settings if not defined if not hasattr(self, 'author'): if 'AUTHOR' in settings: - self.author = settings['AUTHOR'] + self.author = Author(settings['AUTHOR']) else: - self.author = getenv('USER', 'John Doe') + self.author = Author(getenv('USER', 'John Doe')) warning(u"Author of `{0}' unknow, assuming that his name is `{1}'".format(filename or self.title, self.author)) # manage languages @@ -142,47 +142,44 @@ class Article(Page): class Quote(Page): base_properties = ('author', 'date') - -class Category(object): - def __init__(self, category): - self.category = unicode(category) +class URLWrapper(object): + def __init__(self, name): + self.name = unicode(name) def __hash__(self): - return hash(self.category) + return hash(self.name) def __eq__(self, other): - return self.category == unicode(other) + return self.name == unicode(other) def __str__(self): - return str(self.category) + return str(self.name) def __unicode__(self): - return self.category + return self.name + @property + def url(self): + return '%s.html' % self.name + +class Category(URLWrapper): @property def url(self): return 'category/%s.html' % self -class Tag(object): - def __init__(self, tag): - self.tag = unicode.strip(tag) - - def __hash__(self): - return hash(self.tag) - - def __eq__(self, other): - return self.tag == unicode(tag) - - def __str__(self): - return str(self.tag) - - def __unicode__(self): - return self.tag +class Tag(URLWrapper): + def __init__(self, name): + self.name = unicode.strip(name) @property def url(self): return 'tag/%s.html' % self +class Author(URLWrapper): + @property + def url(self): + return 'author/%s.html' % self + def is_valid_content(content, f): try: content.check_properties() diff --git a/pelican/generators.py b/pelican/generators.py index 816a6755..d9d584a4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -195,7 +195,7 @@ class ArticlesGenerator(Generator): author_template = self.get_template('author') for aut, articles in self.authors: dates = [article for article in self.dates if article in articles] - write('author/%s.html' % aut, author_template, self.context, + write(aut.url, author_template, self.context, author=aut, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name='author/%s' % aut) diff --git a/pelican/readers.py b/pelican/readers.py index 6011c272..e760b662 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -15,7 +15,7 @@ except ImportError: Markdown = False import re -from pelican.contents import Category, Tag +from pelican.contents import Category, Tag, Author from pelican.utils import get_date, open @@ -24,6 +24,7 @@ _METADATA_PROCESSORS = { 'date': lambda x: get_date(x), 'status': unicode.strip, 'category': Category, + 'author': Author, } def _process_metadata(name, value): diff --git a/pelican/themes/notmyidea/templates/article_infos.html b/pelican/themes/notmyidea/templates/article_infos.html index 09ecd595..a1993a09 100644 --- a/pelican/themes/notmyidea/templates/article_infos.html +++ b/pelican/themes/notmyidea/templates/article_infos.html @@ -5,7 +5,7 @@ {% if article.author %}
- By {{ article.author }} + By {{ article.author }}
{% endif %}

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

diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 30d31fb6..d6c96a13 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -8,7 +8,7 @@ {% if article.author %}
- By {{ article.author }} + By {{ article.author }}
{% endif %} diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index fd8545df..ad2a3b2e 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -11,7 +11,7 @@

{{ article.title }}

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

    Pages

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