Merge branch 'master' into html_list_tags
3
.gitignore
vendored
|
|
@ -13,3 +13,6 @@ tags
|
|||
htmlcov
|
||||
six-*.egg/
|
||||
*.orig
|
||||
venv
|
||||
samples/output
|
||||
*.pem
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ Before you ask for help, please make sure you do the following:
|
|||
* no plugins or only those related to the issue
|
||||
|
||||
**NOTE:** The most common sources of problems are anomalies in (1) themes,
|
||||
(2) settings files, and (3) ``make``/``fab`` automation wrappers. If you can't
|
||||
(2) settings files, and (3) ``make``/``invoke`` automation wrappers. If you can't
|
||||
reproduce your problem when using the following steps to generate your site,
|
||||
then the problem is almost certainly with your chosen theme and/or settings
|
||||
file (and not Pelican itself)::
|
||||
|
|
@ -76,8 +76,8 @@ The #pelican IRC channel
|
|||
can manually join the ``#pelican`` IRC channel on the `freenode IRC network`_.
|
||||
|
||||
.. _webchat: https://kiwiirc.com/client/irc.freenode.net/?#pelican
|
||||
.. _`IRC link`: irc://irc.freenode.org/pelican
|
||||
.. _`freenode IRC network`: http://www.freenode.org/
|
||||
.. _`IRC link`: irc://irc.freenode.net/pelican
|
||||
.. _`freenode IRC network`: https://freenode.net/
|
||||
|
||||
|
||||
Contributing code
|
||||
|
|
@ -114,15 +114,21 @@ Using Git and GitHub
|
|||
`install hub <https://github.com/github/hub/#installation>`_ and then run
|
||||
`hub pull-request <https://github.com/github/hub/#git-pull-request>`_ to
|
||||
turn your GitHub issue into a pull request containing your code.
|
||||
* After you have issued a pull request, Travis will run the test suite for all
|
||||
supported Python versions and check for PEP8 compliance. If any of these
|
||||
checks fail, you should fix them. (If tests fail on Travis but seem to pass
|
||||
locally, ensure that local test runs aren't skipping any tests.)
|
||||
|
||||
Contribution quality standards
|
||||
------------------------------
|
||||
|
||||
* Adhere to `PEP8 coding standards`_ whenever possible. This can be eased via
|
||||
the `pep8 <http://pypi.python.org/pypi/pep8>`_ or `flake8
|
||||
* Adhere to `PEP8 coding standards`_. This can be eased via the `pycodestyle
|
||||
<https://pypi.org/project/pycodestyle>`_ or `flake8
|
||||
<http://pypi.python.org/pypi/flake8/>`_ tools, the latter of which in
|
||||
particular will give you some useful hints about ways in which the
|
||||
code/formatting can be improved.
|
||||
code/formatting can be improved. If you are relying on your editor for PEP8
|
||||
compliance, note that the line length specified by PEP8 is 79 (excluding the
|
||||
line break).
|
||||
* Ensure your code is compatible with the latest Python 2.7 and 3.x releases — see our
|
||||
`compatibility cheatsheet`_ for more details.
|
||||
* Add docs and tests for your changes. Undocumented and untested features will
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
include *.rst
|
||||
recursive-include pelican *.html *.css *png *.in *.rst *.markdown *.md *.mkd *.xml *.py
|
||||
recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py
|
||||
include LICENSE THANKS docs/changelog.rst
|
||||
|
|
|
|||
BIN
docs/_static/overall.png
vendored
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 4.9 KiB |
|
|
@ -5,7 +5,16 @@ Next release
|
|||
============
|
||||
|
||||
* New signal: ``feed_generated``
|
||||
* Make the HTML reader parse multiple occurences of metadata tags as list
|
||||
* Replace Fabric by Invoke and ``fabfile.py`` template by ``tasks.py``.
|
||||
* Replace ``SLUG_SUBSTITUTIONS`` (and friends) by ``SLUG_REGEX_SUBSTITUTIONS``
|
||||
for more finegrained control
|
||||
* ``'{base_name}'`` value in ``PAGINATION_PATTERNS`` setting no longer strips
|
||||
``'bar'`` from ``'foo/bar.html'`` (unless ``'bar' == 'index'``).
|
||||
* ``ARTICLE_ORDER_BY`` and ``PAGE_ORDER_BY`` now also affect 1) category, tag
|
||||
and author pages 2) feeds 3) draft and hidden articles and pages
|
||||
* New ``ARTICLE_TRANSLATION_ID`` and ``PAGE_TRANSLATION_ID`` settings to specify
|
||||
metadata attributes used to identify translations; or to disable translations
|
||||
* Make the HTML reader parse multiple occurrences of metadata tags as a list
|
||||
|
||||
3.7.1 (2017-01-10)
|
||||
==================
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ The idea behind "pages" is that they are usually not temporal in nature and are
|
|||
used for content that does not change very often (e.g., "About" or "Contact"
|
||||
pages).
|
||||
|
||||
You can find sample content in the repository at: ``pelican/samples/content/``
|
||||
You can find sample content in the repository at ``samples/content/``.
|
||||
|
||||
.. _internal_metadata:
|
||||
|
||||
|
|
@ -70,6 +70,22 @@ Metadata syntax for Markdown posts should follow this pattern::
|
|||
|
||||
This is the content of my super blog post.
|
||||
|
||||
You can also have your own metadata keys (so long as they don't conflict with reserved metadata keywords) for use in your python templates. The following is the list of reserved metadata keywords:
|
||||
|
||||
* `Title`
|
||||
* `Tags`
|
||||
* `Date`
|
||||
* `Modified`
|
||||
* `Status`
|
||||
* `Category`
|
||||
* `Author`
|
||||
* `Authors`
|
||||
* `Slug`
|
||||
* `Summary`
|
||||
* `Template`
|
||||
* `Save_as`
|
||||
* `Url`
|
||||
|
||||
Readers for additional formats (such as AsciiDoc_) are available via plugins.
|
||||
Refer to `pelican-plugins`_ repository for those.
|
||||
|
||||
|
|
@ -370,8 +386,9 @@ of available translations for that article.
|
|||
language. For such advanced functionality the `i18n_subsites
|
||||
plugin`_ can be used.
|
||||
|
||||
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
|
||||
By default, Pelican uses the article's URL "slug" to determine if two or more
|
||||
articles are translations of one another. (This can be changed with the
|
||||
``ARTICLE_TRANSLATION_ID`` setting.) 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.
|
||||
|
||||
|
|
@ -517,9 +534,9 @@ your settings file.
|
|||
Publishing drafts
|
||||
=================
|
||||
|
||||
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
|
||||
If you want to publish an article or a page 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 or tag page.
|
||||
|
||||
If your articles should be automatically published as a draft (to not accidentally
|
||||
|
|
@ -535,6 +552,6 @@ metadata to include ``Status: published``.
|
|||
.. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime
|
||||
.. _AsciiDoc: http://www.methods.co.nz/asciidoc/
|
||||
.. _pelican-plugins: http://github.com/getpelican/pelican-plugins
|
||||
.. _Markdown Extensions: http://pythonhosted.org/Markdown/extensions/
|
||||
.. _CodeHilite extension: http://pythonhosted.org/Markdown/extensions/code_hilite.html#syntax
|
||||
.. _Markdown Extensions: https://python-markdown.github.io/extensions/
|
||||
.. _CodeHilite extension: https://python-markdown.github.io/extensions/code_hilite/#syntax
|
||||
.. _i18n_subsites plugin: http://github.com/getpelican/pelican-plugins/tree/master/i18n_subsites
|
||||
|
|
|
|||
|
|
@ -14,32 +14,32 @@ When doing so, please adhere to the following guidelines.
|
|||
Setting up the development environment
|
||||
======================================
|
||||
|
||||
While there are many ways to set up one's development environment, following
|
||||
is a method that uses `virtualenv <https://virtualenv.pypa.io/en/stable/>`_.
|
||||
While there are many ways to set up one's development environment, we recommend
|
||||
using `Virtualenv <https://virtualenv.pypa.io/en/stable/>`_. This tool allows
|
||||
you to set up separate environments for separate Python projects that are
|
||||
isolated from one another so you can use different packages (and package
|
||||
versions) for each.
|
||||
|
||||
If you don't have ``virtualenv`` installed, you can install it via::
|
||||
|
||||
$ pip install virtualenv
|
||||
|
||||
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 and activate a virtual environment, use the following syntax::
|
||||
Use ``virtualenv`` to create and activate a virtual environment::
|
||||
|
||||
$ virtualenv ~/virtualenvs/pelican
|
||||
$ cd ~/virtualenvs/pelican
|
||||
$ . bin/activate
|
||||
|
||||
To clone the Pelican source::
|
||||
Clone the Pelican source into a subfolder called ``src/pelican``::
|
||||
|
||||
$ git clone https://github.com/getpelican/pelican.git src/pelican
|
||||
|
||||
To install the development dependencies::
|
||||
|
||||
$ cd src/pelican
|
||||
|
||||
Install the development dependencies::
|
||||
|
||||
$ pip install -r requirements/developer.pip
|
||||
|
||||
To install Pelican and its dependencies::
|
||||
Install Pelican and its dependencies::
|
||||
|
||||
$ python setup.py develop
|
||||
|
||||
|
|
@ -47,7 +47,8 @@ Or using ``pip``::
|
|||
|
||||
$ pip install -e .
|
||||
|
||||
To conveniently test on multiple Python versions, we also provide a .tox file.
|
||||
To conveniently test on multiple Python versions, we also provide a ``.tox``
|
||||
file.
|
||||
|
||||
|
||||
Building the docs
|
||||
|
|
@ -57,7 +58,7 @@ If you make changes to the documentation, you should preview your changes
|
|||
before committing them::
|
||||
|
||||
$ pip install -r requirements/docs.pip
|
||||
$ cd src/pelican/docs
|
||||
$ cd docs
|
||||
$ make html
|
||||
|
||||
Open ``_build/html/index.html`` in your browser to preview the documentation.
|
||||
|
|
@ -79,8 +80,8 @@ mentioning that "some generated files differ from the expected functional tests
|
|||
output." If you have made changes that affect the HTML output generated by
|
||||
Pelican, and the changes to that output are expected and deemed correct given
|
||||
the nature of your changes, then you should update the output used by the
|
||||
functional tests. To do so, **make sure you have both ``en_EN.utf8`` and
|
||||
``fr_FR.utf8`` locales installed**, and then run the following two commands::
|
||||
functional tests. To do so, **make sure you have both** ``en_EN.utf8`` **and**
|
||||
``fr_FR.utf8`` **locales installed**, and then run the following two commands::
|
||||
|
||||
$ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \
|
||||
-s samples/pelican.conf.py samples/content/
|
||||
|
|
@ -89,56 +90,60 @@ functional tests. To do so, **make sure you have both ``en_EN.utf8`` and
|
|||
$ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \
|
||||
samples/content/
|
||||
|
||||
Testing on Python 2 and 3
|
||||
-------------------------
|
||||
You may also find that some tests are skipped because some dependency (e.g.,
|
||||
Pandoc) is not installed. This does not automatically mean that these tests
|
||||
have passed; you should at least verify that any skipped tests are not
|
||||
affected by your changes.
|
||||
|
||||
Testing on Python 3 currently requires some extra steps: installing
|
||||
Python 3-compatible versions of dependent packages and plugins.
|
||||
You should run the test suite under each of the supported versions of Python.
|
||||
This is best done by creating a separate Python environment for each version.
|
||||
Tox_ is a useful tool to automate running tests inside ``virtualenv``
|
||||
environments.
|
||||
|
||||
Tox_ is a useful tool to run tests on both versions. It will install the
|
||||
Python 3-compatible version of dependent packages.
|
||||
.. _Tox: https://tox.readthedocs.io/en/latest/
|
||||
|
||||
.. _Tox: http://testrun.org/tox/latest/
|
||||
Python 2/3 compatibility development tips
|
||||
=========================================
|
||||
|
||||
Python 3 development tips
|
||||
=========================
|
||||
Here are some tips that may be useful for writing code that is compatible with
|
||||
both Python 2.7 and Python 3:
|
||||
|
||||
Here are some tips that may be useful when doing some code for both Python 2.7
|
||||
and Python 3 at the same time:
|
||||
- Use new syntax. For example:
|
||||
|
||||
- Assume every string and literal is unicode (import unicode_literals):
|
||||
- ``print .. -> print(..)``
|
||||
- ``except .., e -> except .. as e``
|
||||
|
||||
- Do not use prefix ``u'``.
|
||||
- Do not encode/decode strings in the middle of sth. Follow the code to the
|
||||
source (or target) of a string and encode/decode at the first/last possible
|
||||
point.
|
||||
- In other words, write your functions to expect and to return unicode.
|
||||
- Encode/decode strings if e.g. the source is a Python function that is known
|
||||
to handle this badly, e.g. strftime() in Python 2.
|
||||
- Use new methods. For example:
|
||||
|
||||
- Use new syntax: print function, "except ... *as* e" (not comma), etc.
|
||||
- Refactor method calls like ``dict.iteritems()``, ``xrange()``, etc. in a way
|
||||
that runs without code change in both Python versions.
|
||||
- Do not use magic method ``__unicode()__`` in new classes. Use only ``__str()__``
|
||||
and decorate the class with ``@python_2_unicode_compatible``.
|
||||
- Do not start int literals with a zero. This is a syntax error in Py3k.
|
||||
- Unfortunately I did not find an octal notation that is valid in both
|
||||
Pythons. Use decimal instead.
|
||||
- use six, e.g.:
|
||||
- ``dict.iteritems() -> dict.items()``
|
||||
- ``xrange(..) - > list(range(..))``
|
||||
|
||||
- Use ``six`` where necessary. For example:
|
||||
|
||||
- ``isinstance(.., basestring) -> isinstance(.., six.string_types)``
|
||||
- ``isinstance(.., unicode) -> isinstance(.., six.text_type)``
|
||||
|
||||
- ``setlocale()`` in Python 2 bails when we give the locale name as unicode,
|
||||
- Assume every string and literal is Unicode:
|
||||
|
||||
- Use ``from __future__ import unicode_literals``
|
||||
- Do not use the prefix ``u'`` before strings.
|
||||
- Do not encode/decode strings in the middle of something. Follow the code to
|
||||
the source/target of a string and encode/decode at the first/last possible
|
||||
point.
|
||||
- In particular, write your functions to expect and to return Unicode.
|
||||
- Encode/decode strings if the string is the output of a Python function that
|
||||
is known to handle this badly. For example, ``strftime()`` in Python 2.
|
||||
- Do not use the magic method ``__unicode()__`` in new classes. Use only
|
||||
``__str()__`` and decorate the class with ``@python_2_unicode_compatible``.
|
||||
|
||||
- ``setlocale()`` in Python 2 fails when we give the locale name as Unicode,
|
||||
and since we are using ``from __future__ import unicode_literals``, we do
|
||||
that everywhere! As a workaround, I enclosed the localename with ``str()``;
|
||||
in Python 2 this casts the name to a byte string, in Python 3 this should do
|
||||
nothing, because the locale name already had been unicode.
|
||||
|
||||
- Kept range() almost everywhere as-is (2to3 suggests list(range())), just
|
||||
changed it where I felt necessary.
|
||||
|
||||
- Changed xrange() back to range(), so it is valid in both Python versions.
|
||||
that everywhere! As a workaround, enclose the locale name with ``str()``;
|
||||
in Python 2 this casts the name to a byte string, while in Python 3 this
|
||||
should do nothing, because the locale name was already Unicode.
|
||||
- Do not start integer literals with a zero. This is a syntax error in Python 3.
|
||||
- Unfortunately there seems to be no octal notation that is valid in both
|
||||
Python 2 and 3. Use decimal notation instead.
|
||||
|
||||
|
||||
Logging tips
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ Description
|
|||
``pelican-import`` is a command-line tool for converting articles from other
|
||||
software to reStructuredText or Markdown. The supported import formats are:
|
||||
|
||||
- WordPress XML export
|
||||
- Blogger XML export
|
||||
- Dotclear export
|
||||
- Posterous API
|
||||
- Tumblr API
|
||||
- WordPress XML export
|
||||
- RSS/Atom feed
|
||||
|
||||
The conversion from HTML to reStructuredText or Markdown relies on `Pandoc`_.
|
||||
|
|
@ -40,9 +41,9 @@ Usage
|
|||
|
||||
::
|
||||
|
||||
pelican-import [-h] [--wpfile] [--dotclear] [--posterous] [--tumblr] [--feed] [-o OUTPUT]
|
||||
[-m MARKUP] [--dir-cat] [--dir-page] [--strip-raw] [--disable-slugs]
|
||||
[-e EMAIL] [-p PASSWORD] [-b BLOGNAME]
|
||||
pelican-import [-h] [--blogger] [--dotclear] [--posterous] [--tumblr] [--wpfile] [--feed]
|
||||
[-o OUTPUT] [-m MARKUP] [--dir-cat] [--dir-page] [--strip-raw] [--wp-custpost]
|
||||
[--wp-attach] [--disable-slugs] [-e EMAIL] [-p PASSWORD] [-b BLOGNAME]
|
||||
input|api_token|api_key
|
||||
|
||||
Positional arguments
|
||||
|
|
@ -57,10 +58,11 @@ Optional arguments
|
|||
------------------
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
--wpfile WordPress XML export (default: False)
|
||||
--blogger Blogger XML export (default: False)
|
||||
--dotclear Dotclear export (default: False)
|
||||
--posterous Posterous API (default: False)
|
||||
--tumblr Tumblr API (default: False)
|
||||
--wpfile WordPress XML export (default: False)
|
||||
--feed Feed to parse (default: False)
|
||||
-o OUTPUT, --output OUTPUT
|
||||
Output path (default: content)
|
||||
|
|
@ -70,11 +72,24 @@ Optional arguments
|
|||
--dir-cat Put files in directories with categories name
|
||||
(default: False)
|
||||
--dir-page Put files recognised as pages in "pages/" sub-
|
||||
directory (wordpress import only) (default: False)
|
||||
--filter-author Import only post from the specified author.
|
||||
directory (blogger and wordpress import only)
|
||||
(default: False)
|
||||
--filter-author Import only post from the specified author
|
||||
--strip-raw Strip raw HTML code that can't be converted to markup
|
||||
such as flash embeds or iframes (wordpress import
|
||||
only) (default: False)
|
||||
--wp-custpost Put wordpress custom post types in directories. If
|
||||
used with --dir-cat option directories will be created
|
||||
as "/post_type/category/" (wordpress import only)
|
||||
--wp-attach Download files uploaded to wordpress as attachments.
|
||||
Files will be added to posts as a list in the post
|
||||
header and links to the files within the post will be
|
||||
updated. All files will be downloaded, even if they
|
||||
aren't associated with a post. Files will be downloaded
|
||||
with their original path inside the output directory,
|
||||
e.g. "output/wp-uploads/date/postname/file.jpg".
|
||||
(wordpress import only) (requires an internet
|
||||
connection)
|
||||
--disable-slugs Disable storing slugs from imported posts within
|
||||
output. With this disabled, your Pelican URLs may not
|
||||
be consistent with your original posts. (default:
|
||||
|
|
@ -90,9 +105,9 @@ Optional arguments
|
|||
Examples
|
||||
========
|
||||
|
||||
For WordPress::
|
||||
For Blogger::
|
||||
|
||||
$ pelican-import --wpfile -o ~/output ~/posts.xml
|
||||
$ pelican-import --blogger -o ~/output ~/posts.xml
|
||||
|
||||
For Dotclear::
|
||||
|
||||
|
|
@ -106,10 +121,14 @@ For Tumblr::
|
|||
|
||||
$ pelican-import --tumblr -o ~/output --blogname=<blogname> <api_token>
|
||||
|
||||
For WordPress::
|
||||
|
||||
$ pelican-import --wpfile -o ~/output ~/posts.xml
|
||||
|
||||
Tests
|
||||
=====
|
||||
|
||||
To test the module, one can use sample files:
|
||||
|
||||
- for WordPress: http://wpcandy.com/made/the-sample-post-collection
|
||||
- for WordPress: http://www.wpbeginner.com/wp-themes/how-to-add-dummy-content-for-theme-development-in-wordpress/
|
||||
- for Dotclear: http://media.dotaddict.org/tda/downloads/lorem-backup.txt
|
||||
|
|
|
|||
|
|
@ -101,10 +101,9 @@ can optionally add yourself if you plan to create non-chronological content)::
|
|||
|
||||
yourproject/
|
||||
├── content
|
||||
│ └── (pages)
|
||||
│ └── (pages)
|
||||
├── output
|
||||
├── develop_server.sh
|
||||
├── fabfile.py
|
||||
├── tasks.py
|
||||
├── Makefile
|
||||
├── pelicanconf.py # Main settings file
|
||||
└── publishconf.py # Settings to use when ready to publish
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ page_generator_preread page_generator invoked befor
|
|||
use if code needs to do something before every page is parsed.
|
||||
page_generator_init page_generator invoked in the PagesGenerator.__init__
|
||||
page_generator_finalized page_generator invoked at the end of PagesGenerator.generate_context
|
||||
page_generator_write_page page_generator, content invoked before writing each page, the page is passed as content
|
||||
page_writer_finalized page_generator, writer invoked after all pages have been written, but before the page generator
|
||||
is closed.
|
||||
static_generator_context static_generator, metadata
|
||||
|
|
@ -215,6 +216,7 @@ Adding a new generator is also really easy. You might want to have a look at
|
|||
# define a new generator here if you need to
|
||||
return MyGenerator
|
||||
|
||||
signals.get_generators.connect(get_generators)
|
||||
def register():
|
||||
signals.get_generators.connect(get_generators)
|
||||
|
||||
.. _pelican-plugins bug #314: https://github.com/getpelican/pelican-plugins/issues/314
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ argument, like so::
|
|||
pelican --write-selected output/posts/my-post-title.html
|
||||
|
||||
Note that you must specify the path to the generated *output* file — not the
|
||||
source content. To determine the output file path, use the ``--debug`` flag to
|
||||
determine the correct file name and location. If desired, ``--write-selected``
|
||||
source content. To determine the output file name and location, use the
|
||||
``--debug`` flag. If desired, ``--write-selected``
|
||||
can take a comma-separated list of paths or can be configured as a setting.
|
||||
(See: :ref:`writing_only_selected_content`)
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ While the ``pelican`` command is the canonical way to generate your site,
|
|||
automation tools can be used to streamline the generation and publication
|
||||
flow. One of the questions asked during the ``pelican-quickstart`` process
|
||||
pertains to whether you want to automate site generation and publication.
|
||||
If you answered "yes" to that question, a ``fabfile.py`` and
|
||||
If you answered "yes" to that question, a ``tasks.py`` and
|
||||
``Makefile`` will be generated in the root of your project. These files,
|
||||
pre-populated with certain information gleaned from other answers provided
|
||||
during the ``pelican-quickstart`` process, are meant as a starting point and
|
||||
|
|
@ -113,55 +113,43 @@ files can deleted at any time and will not affect usage of the canonical
|
|||
Following are automation tools that "wrap" the ``pelican`` command and can
|
||||
simplify the process of generating, previewing, and uploading your site.
|
||||
|
||||
Fabric
|
||||
Invoke
|
||||
------
|
||||
|
||||
The advantage of Fabric_ is that it is written in Python and thus can be used
|
||||
The advantage of Invoke_ is that it is written in Python and thus can be used
|
||||
in a wide range of environments. The downside is that it must be installed
|
||||
separately. Use the following command to install Fabric, prefixing with
|
||||
separately. Use the following command to install Invoke, prefixing with
|
||||
``sudo`` if your environment requires it::
|
||||
|
||||
pip install Fabric
|
||||
pip install invoke
|
||||
|
||||
.. note:: Installing PyCrypto on Windows
|
||||
|
||||
Fabric depends upon PyCrypto_, which is tricky to install
|
||||
if your system doesn't have a C compiler.
|
||||
For Windows users, before installing Fabric, use
|
||||
``easy_install http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe``
|
||||
per this `StackOverflow suggestion <http://stackoverflow.com/a/11405769/6364>`_
|
||||
You're more likely to have success
|
||||
with the Win32 versions of Python 2.7 and PyCrypto,
|
||||
than with the Win64—\
|
||||
even if your operating system is a 64-bit version of Windows.
|
||||
|
||||
Take a moment to open the ``fabfile.py`` file that was generated in your
|
||||
Take a moment to open the ``tasks.py`` file that was generated in your
|
||||
project root. You will see a number of commands, any one of which can be
|
||||
renamed, removed, and/or customized to your liking. Using the out-of-the-box
|
||||
configuration, you can generate your site via::
|
||||
|
||||
fab build
|
||||
invoke build
|
||||
|
||||
If you'd prefer to have Pelican automatically regenerate your site every time a
|
||||
change is detected (which is handy when testing locally), use the following
|
||||
command instead::
|
||||
|
||||
fab regenerate
|
||||
invoke regenerate
|
||||
|
||||
To serve the generated site so it can be previewed in your browser at
|
||||
http://localhost:8000/::
|
||||
|
||||
fab serve
|
||||
invoke serve
|
||||
|
||||
If during the ``pelican-quickstart`` process you answered "yes" when asked
|
||||
whether you want to upload your site via SSH, you can use the following command
|
||||
to publish your site via rsync over SSH::
|
||||
|
||||
fab publish
|
||||
invoke publish
|
||||
|
||||
These are just a few of the commands available by default, so feel free to
|
||||
explore ``fabfile.py`` and see what other commands are available. More
|
||||
importantly, don't hesitate to customize ``fabfile.py`` to suit your specific
|
||||
explore ``tasks.py`` and see what other commands are available. More
|
||||
importantly, don't hesitate to customize ``tasks.py`` to suit your specific
|
||||
needs and preferences.
|
||||
|
||||
Make
|
||||
|
|
@ -201,10 +189,7 @@ separate terminal sessions, but you can run both at once via::
|
|||
make devserver
|
||||
|
||||
The above command will simultaneously run Pelican in regeneration mode as well
|
||||
as serve the output at http://localhost:8000. Once you are done testing your
|
||||
changes, you should stop the development server via::
|
||||
|
||||
./develop_server.sh stop
|
||||
as serve the output at http://localhost:8000.
|
||||
|
||||
When you're ready to publish your site, you can upload it via the method(s) you
|
||||
chose during the ``pelican-quickstart`` questionnaire. For this example, we'll
|
||||
|
|
@ -219,5 +204,4 @@ That's it! Your site should now be live.
|
|||
executables, such as ``python3``, you can set the ``PY`` and ``PELICAN``
|
||||
environment variables, respectively, to override the default executable names.)
|
||||
|
||||
.. _Fabric: http://fabfile.org/
|
||||
.. _PyCrypto: http://pycrypto.org
|
||||
.. _Invoke: http://www.pyinvoke.org
|
||||
|
|
|
|||
|
|
@ -61,11 +61,10 @@ ignored for now.)
|
|||
Preview your site
|
||||
-----------------
|
||||
|
||||
Open a new terminal session and run the following commands to switch to your
|
||||
``output`` directory and launch Pelican's web server::
|
||||
Open a new terminal session, navigate to your site directory and run the
|
||||
following command to launch Pelican's web server::
|
||||
|
||||
cd ~/projects/yoursite/output
|
||||
python -m pelican.server
|
||||
pelican --listen
|
||||
|
||||
Preview your site by navigating to http://localhost:8000/ in your browser.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ Settings are configured in the form of a Python module (a file). There is an
|
|||
<https://github.com/getpelican/pelican/raw/master/samples/pelican.conf.py>`_
|
||||
available for reference.
|
||||
|
||||
To see a list of current settings in your environment, including both default
|
||||
and any customized values, run the following command (append one or more
|
||||
specific setting names as arguments to see values for those settings only)::
|
||||
|
||||
pelican --print-settings
|
||||
|
||||
All the setting identifiers must be set in all-caps, otherwise they will not be
|
||||
processed. Setting values that are numbers (5, 20, etc.), booleans (True,
|
||||
False, None, etc.), dictionaries, or tuples should *not* be enclosed in
|
||||
|
|
@ -28,10 +34,9 @@ quotation marks. All other values (i.e., strings) *must* be enclosed in
|
|||
quotation marks.
|
||||
|
||||
Unless otherwise specified, settings that refer to paths can be either absolute
|
||||
or relative to the configuration file.
|
||||
|
||||
The settings you define in the configuration file will be passed to the
|
||||
templates, which allows you to use your settings to add site-wide content.
|
||||
or relative to the configuration file. The settings you define in the
|
||||
configuration file will be passed to the templates, which allows you to use your
|
||||
settings to add site-wide content.
|
||||
|
||||
Here is a list of settings for Pelican:
|
||||
|
||||
|
|
@ -131,7 +136,7 @@ Basic settings
|
|||
|
||||
Extra configuration settings for the Markdown processor. Refer to the Python
|
||||
Markdown documentation's `Options section
|
||||
<http://pythonhosted.org/Markdown/reference.html#markdown>`_ for a complete
|
||||
<https://python-markdown.github.io/reference/#markdown>`_ for a complete
|
||||
list of supported options. The ``extensions`` option will be automatically
|
||||
computed from the ``extension_configs`` option.
|
||||
|
||||
|
|
@ -328,6 +333,15 @@ Basic settings
|
|||
A list of metadata fields containing reST/Markdown content to be parsed and
|
||||
translated to HTML.
|
||||
|
||||
.. data:: PORT = 8000
|
||||
|
||||
The TCP port to serve content from the output folder via HTTP when pelican
|
||||
is run with --listen
|
||||
|
||||
.. data:: BIND = ''
|
||||
|
||||
The IP to which to bind the HTTP server.
|
||||
|
||||
|
||||
URL settings
|
||||
============
|
||||
|
|
@ -436,6 +450,24 @@ respectively.
|
|||
|
||||
The location we will save the page which doesn't use the default language.
|
||||
|
||||
.. data:: DRAFT_PAGE_URL = 'drafts/pages/{slug}.html'
|
||||
|
||||
The URL used to link to a page draft.
|
||||
|
||||
.. data:: DRAFT_PAGE_SAVE_AS = 'drafts/pages/{slug}.html'
|
||||
|
||||
The actual location a page draft is saved at.
|
||||
|
||||
.. data:: DRAFT_PAGE_LANG_URL = 'drafts/pages/{slug}-{lang}.html'
|
||||
|
||||
The URL used to link to a page draft which doesn't use the default
|
||||
language.
|
||||
|
||||
.. data:: DRAFT_PAGE_LANG_SAVE_AS = 'drafts/pages/{slug}-{lang}.html'
|
||||
|
||||
The actual location a page draft which doesn't use the default language is
|
||||
saved at.
|
||||
|
||||
.. data:: CATEGORY_URL = 'category/{slug}.html'
|
||||
|
||||
The URL to use for a category.
|
||||
|
|
@ -464,34 +496,28 @@ respectively.
|
|||
|
||||
The location to save per-year archives of your posts.
|
||||
|
||||
.. data:: YEAR_ARCHIVE_URL = ''
|
||||
|
||||
The URL to use for per-year archives of your posts. Used only if you have
|
||||
the ``{url}`` placeholder in ``PAGINATION_PATTERNS``.
|
||||
|
||||
.. data:: MONTH_ARCHIVE_SAVE_AS = ''
|
||||
|
||||
The location to save per-month archives of your posts.
|
||||
|
||||
.. data:: MONTH_ARCHIVE_URL = ''
|
||||
|
||||
The URL to use for per-month archives of your posts. Used only if you have
|
||||
the ``{url}`` placeholder in ``PAGINATION_PATTERNS``.
|
||||
|
||||
.. data:: DAY_ARCHIVE_SAVE_AS = ''
|
||||
|
||||
The location to save per-day archives of your posts.
|
||||
|
||||
.. data:: SLUG_SUBSTITUTIONS = ()
|
||||
.. data:: DAY_ARCHIVE_URL = ''
|
||||
|
||||
Substitutions to make prior to stripping out non-alphanumerics when
|
||||
generating slugs. Specified as a list of 3-tuples of ``(from, to, skip)``
|
||||
which are applied in order. ``skip`` is a boolean indicating whether or not
|
||||
to skip replacement of non-alphanumeric characters. Useful for backward
|
||||
compatibility with existing URLs.
|
||||
|
||||
.. data:: AUTHOR_SUBSTITUTIONS = ()
|
||||
|
||||
Substitutions for authors. ``SLUG_SUBSTITUTIONS`` is not taken into account
|
||||
here!
|
||||
|
||||
.. data:: CATEGORY_SUBSTITUTIONS = ()
|
||||
|
||||
Added to ``SLUG_SUBSTITUTIONS`` for categories.
|
||||
|
||||
.. data:: TAG_SUBSTITUTIONS = ()
|
||||
|
||||
Added to ``SLUG_SUBSTITUTIONS`` for tags.
|
||||
The URL to use for per-day archives of your posts. Used only if you have the
|
||||
``{url}`` placeholder in ``PAGINATION_PATTERNS``.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -500,24 +526,6 @@ respectively.
|
|||
set the corresponding ``*_SAVE_AS`` setting to ``''`` to prevent the
|
||||
relevant page from being generated.
|
||||
|
||||
.. note::
|
||||
|
||||
Substitutions are applied in order with the side effect that keeping
|
||||
non-alphanum characters applies to the whole string when a replacement
|
||||
is made.
|
||||
|
||||
For example if you have the following setting::
|
||||
|
||||
SLUG_SUBSTITUTIONS = (('C++', 'cpp'), ('keep dot', 'keep.dot', True))
|
||||
|
||||
the string ``Keep Dot`` will be converted to ``keep.dot``, however
|
||||
``C++ will keep dot`` will be converted to ``cpp will keep.dot`` instead
|
||||
of ``cpp-will-keep.dot``!
|
||||
|
||||
If you want to keep non-alphanum characters only for tags or categories
|
||||
but not other slugs then configure ``TAG_SUBSTITUTIONS`` and
|
||||
``CATEGORY_SUBSTITUTIONS`` respectively!
|
||||
|
||||
Pelican can optionally create per-year, per-month, and per-day archives of your
|
||||
posts. These secondary archives are disabled by default but are automatically
|
||||
enabled if you supply format strings for their respective ``_SAVE_AS`` settings.
|
||||
|
|
@ -579,6 +587,33 @@ URLs for direct template pages are theme-dependent. Some themes use
|
|||
corresponding ``*_URL`` setting as string, while others hard-code them:
|
||||
``'archives.html'``, ``'authors.html'``, ``'categories.html'``, ``'tags.html'``.
|
||||
|
||||
.. data:: SLUG_REGEX_SUBSTITUTIONS = [
|
||||
(r'[^\w\s-]', ''), # remove non-alphabetical/whitespace/'-' chars
|
||||
(r'(?u)\A\s*', ''), # strip leading whitespace
|
||||
(r'(?u)\s*\Z', ''), # strip trailing whitespace
|
||||
(r'[-\s]+', '-'), # reduce multiple whitespace or '-' to single '-'
|
||||
]
|
||||
|
||||
Regex substitutions to make when generating slugs of articles and pages.
|
||||
Specified as a list of pairs of ``(from, to)`` which are applied in order,
|
||||
ignoring case. The default substitutions have the effect of removing
|
||||
non-alphanumeric characters and converting internal whitespace to dashes.
|
||||
Apart from these substitutions, slugs are always converted to lowercase
|
||||
ascii characters and leading and trailing whitespace is stripped. Useful for
|
||||
backward compatibility with existing URLs.
|
||||
|
||||
.. data:: AUTHOR_REGEX_SUBSTITUTIONS = SLUG_REGEX_SUBSTITUTIONS
|
||||
|
||||
Regex substitutions for author slugs. Defaults to ``SLUG_REGEX_SUBSTITUTIONS``.
|
||||
|
||||
.. data:: CATEGORY_REGEX_SUBSTITUTIONS = SLUG_REGEX_SUBSTITUTIONS
|
||||
|
||||
Regex substitutions for category slugs. Defaults to ``SLUG_REGEX_SUBSTITUTIONS``.
|
||||
|
||||
.. data:: TAG_REGEX_SUBSTITUTIONS = SLUG_REGEX_SUBSTITUTIONS
|
||||
|
||||
Regex substitutions for tag slugs. Defaults to ``SLUG_REGEX_SUBSTITUTIONS``.
|
||||
|
||||
Time and Date
|
||||
=============
|
||||
|
||||
|
|
@ -699,6 +734,10 @@ Template pages
|
|||
'src/resume.html': 'dest/resume.html',
|
||||
'src/contact.html': 'dest/contact.html'}
|
||||
|
||||
.. data:: TEMPLATE_EXTENSION = ['.html']
|
||||
|
||||
The extensions to use when looking up template files from template names.
|
||||
|
||||
.. data:: DIRECT_TEMPLATES = ['index', 'categories', 'authors', 'archives']
|
||||
|
||||
List of templates that are used directly to render content. Typically direct
|
||||
|
|
@ -706,15 +745,8 @@ Template pages
|
|||
tags and category index pages). If the tag and category collections are not
|
||||
needed, set ``DIRECT_TEMPLATES = ['index', 'archives']``
|
||||
|
||||
.. data:: PAGINATED_DIRECT_TEMPLATES = ['index']
|
||||
|
||||
Provides the direct templates that should be paginated.
|
||||
|
||||
.. data:: EXTRA_TEMPLATES_PATHS = []
|
||||
|
||||
A list of paths you want Jinja2 to search for templates. Can be used to
|
||||
separate templates from the theme. Example: projects, resume, profile ...
|
||||
These templates need to use ``DIRECT_TEMPLATES`` setting.
|
||||
``DIRECT_TEMPLATES`` are searched for over paths maintained in
|
||||
``THEME_TEMPLATES_OVERRIDES``.
|
||||
|
||||
|
||||
Metadata
|
||||
|
|
@ -811,46 +843,95 @@ the ``TAG_FEED_ATOM`` and ``TAG_FEED_RSS`` settings:
|
|||
|
||||
.. data:: FEED_ATOM = None, i.e. no Atom feed
|
||||
|
||||
Relative URL to output the Atom feed.
|
||||
The location to save the Atom feed.
|
||||
|
||||
.. data:: FEED_ATOM_URL = None
|
||||
|
||||
Relative URL of the Atom feed. If not set, ``FEED_ATOM`` is used both for
|
||||
save location and URL.
|
||||
|
||||
.. data:: FEED_RSS = None, i.e. no RSS
|
||||
|
||||
Relative URL to output the RSS feed.
|
||||
The location to save the RSS feed.
|
||||
|
||||
.. data:: FEED_RSS_URL = None
|
||||
|
||||
Relative URL of the RSS feed. If not set, ``FEED_RSS`` is used both for save
|
||||
location and URL.
|
||||
|
||||
.. data:: FEED_ALL_ATOM = 'feeds/all.atom.xml'
|
||||
|
||||
Relative URL to output the all-posts Atom feed: this feed will contain all
|
||||
The location to save the all-posts Atom feed: this feed will contain all
|
||||
posts regardless of their language.
|
||||
|
||||
.. data:: FEED_ALL_ATOM_URL = None
|
||||
|
||||
Relative URL of the all-posts Atom feed. If not set, ``FEED_ALL_ATOM`` is
|
||||
used both for save location and URL.
|
||||
|
||||
.. data:: FEED_ALL_RSS = None, i.e. no all-posts RSS
|
||||
|
||||
Relative URL to output the all-posts RSS feed: this feed will contain all
|
||||
The location to save the the all-posts RSS feed: this feed will contain all
|
||||
posts regardless of their language.
|
||||
|
||||
.. data:: FEED_ALL_RSS_URL = None
|
||||
|
||||
Relative URL of the all-posts RSS feed. If not set, ``FEED_ALL_RSS`` is used
|
||||
both for save location and URL.
|
||||
|
||||
.. data:: CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
|
||||
|
||||
Where to put the category Atom feeds. [2]_
|
||||
The location to save the category Atom feeds. [2]_
|
||||
|
||||
.. data:: CATEGORY_FEED_ATOM_URL = None
|
||||
|
||||
Relative URL of the category Atom feeds, including the ``%s`` placeholder.
|
||||
[2]_ If not set, ``CATEGORY_FEED_ATOM`` is used both for save location and
|
||||
URL.
|
||||
|
||||
.. data:: CATEGORY_FEED_RSS = None, i.e. no RSS
|
||||
|
||||
Where to put the category RSS feeds.
|
||||
The location to save the category RSS feeds, including the ``%s``
|
||||
placeholder. [2]_
|
||||
|
||||
.. data:: CATEGORY_FEED_RSS_URL = None
|
||||
|
||||
Relative URL of the category RSS feeds, including the ``%s`` placeholder.
|
||||
[2]_ If not set, ``CATEGORY_FEED_RSS`` is used both for save location and
|
||||
URL.
|
||||
|
||||
.. data:: AUTHOR_FEED_ATOM = 'feeds/%s.atom.xml'
|
||||
|
||||
Where to put the author Atom feeds. [2]_
|
||||
The location to save the author Atom feeds. [2]_
|
||||
|
||||
.. data:: AUTHOR_FEED_ATOM_URL = None
|
||||
|
||||
Relative URL of the author Atom feeds, including the ``%s`` placeholder.
|
||||
[2]_ If not set, ``AUTHOR_FEED_ATOM`` is used both for save location and
|
||||
URL.
|
||||
|
||||
.. data:: AUTHOR_FEED_RSS = 'feeds/%s.rss.xml'
|
||||
|
||||
Where to put the author RSS feeds. [2]_
|
||||
The location to save the author RSS feeds. [2]_
|
||||
|
||||
.. data:: AUTHOR_FEED_RSS_URL = None
|
||||
|
||||
Relative URL of the author RSS feeds, including the ``%s`` placeholder. [2]_
|
||||
If not set, ``AUTHOR_FEED_RSS`` is used both for save location and URL.
|
||||
|
||||
.. data:: TAG_FEED_ATOM = None, i.e. no tag feed
|
||||
|
||||
Relative URL to output the tag Atom feed. It should be defined using a "%s"
|
||||
match in the tag name.
|
||||
The location to save the tag Atom feed, including the ``%s`` placeholder.
|
||||
[2]_
|
||||
|
||||
.. data:: TAG_FEED_ATOM_URL = None
|
||||
|
||||
Relative URL of the tag Atom feed, including the ``%s`` placeholder. [2]_
|
||||
|
||||
.. data:: TAG_FEED_RSS = None, i.e. no RSS tag feed
|
||||
|
||||
Relative URL to output the tag RSS feed
|
||||
Relative URL to output the tag RSS feed, including the ``%s`` placeholder.
|
||||
If not set, ``TAG_FEED_RSS`` is used both for save location and URL.
|
||||
|
||||
.. data:: FEED_MAX_ITEMS
|
||||
|
||||
|
|
@ -865,7 +946,7 @@ the ``TAG_FEED_ATOM`` and ``TAG_FEED_RSS`` settings:
|
|||
|
||||
If you don't want to generate some or any of these feeds, set the above variables to ``None``.
|
||||
|
||||
.. [2] %s is the name of the category.
|
||||
.. [2] ``%s`` is replaced by name of the category / author / tag.
|
||||
|
||||
|
||||
FeedBurner
|
||||
|
|
@ -908,7 +989,15 @@ You can use the following settings to configure the pagination.
|
|||
The maximum number of articles to include on a page, not including orphans.
|
||||
False to disable pagination.
|
||||
|
||||
.. data:: PAGINATION_PATTERNS
|
||||
.. data:: PAGINATED_TEMPLATES = {'index': None, 'tag': None, 'category': None, 'author': None}
|
||||
|
||||
The templates to use pagination with, and the number of articles to include
|
||||
on a page. If this value is ``None``, it defaults to ``DEFAULT_PAGINATION``.
|
||||
|
||||
.. data:: PAGINATION_PATTERNS = (
|
||||
(1, '{name}{extension}', '{name}{extension}'),
|
||||
(2, '{name}{number}{extension}', '{name}{number}{extension}'),
|
||||
)
|
||||
|
||||
A set of patterns that are used to determine advanced pagination output.
|
||||
|
||||
|
|
@ -916,25 +1005,28 @@ You can use the following settings to configure the pagination.
|
|||
Using Pagination Patterns
|
||||
-------------------------
|
||||
|
||||
The ``PAGINATION_PATTERNS`` setting can be used to configure where
|
||||
subsequent pages are created. The setting is a sequence of three
|
||||
element tuples, where each tuple consists of::
|
||||
By default, pages subsequent to ``.../foo.html`` are created as
|
||||
``.../foo2.html``, etc. The ``PAGINATION_PATTERNS`` setting can be used to
|
||||
change this. It takes a sequence of triples, where each triple consists of::
|
||||
|
||||
(minimum page, URL setting, SAVE_AS setting,)
|
||||
(minimum_page, page_url, page_save_as,)
|
||||
|
||||
For example, if you wanted the first page to just be ``/``, and the
|
||||
second (and subsequent) pages to be ``/page/2/``, you would set
|
||||
``PAGINATION_PATTERNS`` as follows::
|
||||
For ``page_url`` and ``page_save_as``, you may use a number of variables.
|
||||
``{url}`` and ``{save_as}`` correspond respectively to the ``*_URL`` and
|
||||
``*_SAVE_AS`` values of the corresponding page type (e.g. ``ARTICLE_SAVE_AS``).
|
||||
If ``{save_as} == foo/bar.html``, then ``{name} == foo/bar`` and
|
||||
``{extension} == .html``. ``{base_name}`` equals ``{name}`` except that it
|
||||
strips trailing ``/index`` if present. ``{number}`` equals the page number.
|
||||
|
||||
For example, if you want to leave the first page unchanged, but place
|
||||
subsequent pages at ``.../page/2/`` etc, you could set ``PAGINATION_PATTERNS``
|
||||
as follows::
|
||||
|
||||
PAGINATION_PATTERNS = (
|
||||
(1, '{base_name}/', '{base_name}/index.html'),
|
||||
(1, '{url}', '{save_as}`,
|
||||
(2, '{base_name}/page/{number}/', '{base_name}/page/{number}/index.html'),
|
||||
)
|
||||
|
||||
This would cause the first page to be written to
|
||||
``{base_name}/index.html``, and subsequent ones would be written into
|
||||
``page/{number}`` directories.
|
||||
|
||||
|
||||
Translations
|
||||
============
|
||||
|
|
@ -946,14 +1038,38 @@ more information.
|
|||
|
||||
The default language to use.
|
||||
|
||||
.. data:: ARTICLE_TRANSLATION_ID = 'slug'
|
||||
|
||||
The metadata attribute(s) used to identify which articles are translations
|
||||
of one another. May be a string or a collection of strings. Set to ``None``
|
||||
or ``False`` to disable the identification of translations.
|
||||
|
||||
.. data:: PAGE_TRANSLATION_ID = 'slug'
|
||||
|
||||
The metadata attribute(s) used to identify which pages are translations
|
||||
of one another. May be a string or a collection of strings. Set to ``None``
|
||||
or ``False`` to disable the identification of translations.
|
||||
|
||||
.. data:: TRANSLATION_FEED_ATOM = 'feeds/all-%s.atom.xml'
|
||||
|
||||
Where to put the Atom feed for translations. [3]_
|
||||
The location to save the Atom feed for translations. [3]_
|
||||
|
||||
.. data:: TRANSLATION_FEED_ATOM_URL = None
|
||||
|
||||
Relative URL of the Atom feed for translations, including the ``%s``
|
||||
placeholder. [3]_ If not set, ``TRANSLATION_FEED_ATOM`` is used both for
|
||||
save location and URL.
|
||||
|
||||
.. data:: TRANSLATION_FEED_RSS = None, i.e. no RSS
|
||||
|
||||
Where to put the RSS feed for translations.
|
||||
|
||||
.. data:: TRANSLATION_FEED_RSS_URL = None
|
||||
|
||||
Relative URL of the RSS feed for translations, including the ``%s``
|
||||
placeholder. [3]_ If not set, ``TRANSLATION_FEED_RSS`` is used both for save
|
||||
location and URL.
|
||||
|
||||
.. [3] %s is the language
|
||||
|
||||
|
||||
|
|
@ -981,7 +1097,7 @@ Ordering content
|
|||
|
||||
.. data:: PAGE_ORDER_BY = 'basename'
|
||||
|
||||
Defines how the pages (``PAGES`` variable in the template) are sorted.
|
||||
Defines how the pages (``pages`` variable in the template) are sorted.
|
||||
Options are same as ``ARTICLE_ORDER_BY``. The default value, ``'basename'``
|
||||
will sort pages by their basename.
|
||||
|
||||
|
|
@ -1011,6 +1127,21 @@ However, here are the settings that are related to themes.
|
|||
with the same names are included in the paths defined in this settings, they
|
||||
will be progressively overwritten.
|
||||
|
||||
.. data:: THEME_TEMPLATES_OVERRIDES = []
|
||||
|
||||
A list of paths you want Jinja2 to search for templates before searching the
|
||||
theme's ``templates/`` directory. Allows for overriding individual theme
|
||||
template files without having to fork an existing theme. Jinja2 searches in
|
||||
the following order: files in ``THEME_TEMPLATES_OVERRIDES`` first, then the
|
||||
theme's ``templates/``.
|
||||
|
||||
You can also extend templates from the theme using the ``{% extends %}``
|
||||
directive utilizing the ``!theme`` prefix as shown in the following example:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
{% extends '!theme/article.html' %}
|
||||
|
||||
.. data:: CSS_FILE = 'main.css'
|
||||
|
||||
Specify the CSS file you want to load.
|
||||
|
|
@ -1130,15 +1261,17 @@ Simply populate the list with the log messages you want to hide, and they will
|
|||
be filtered out.
|
||||
|
||||
For example::
|
||||
|
||||
[(logging.WARN, 'TAG_SAVE_AS is set to False')]
|
||||
|
||||
import logging
|
||||
LOG_FILTER = [(logging.WARN, 'TAG_SAVE_AS is set to False')]
|
||||
|
||||
It is possible to filter out messages by a template. Check out source code to
|
||||
obtain a template.
|
||||
|
||||
For example::
|
||||
|
||||
[(logging.WARN, 'Empty alt attribute for image %s in %s')]
|
||||
import logging
|
||||
LOG_FILTER = [(logging.WARN, 'Empty alt attribute for image %s in %s')]
|
||||
|
||||
.. Warning::
|
||||
Silencing messages by templates is a dangerous feature. It is possible to
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ categories A list of (category, articles) tuples, containing
|
|||
all the categories and corresponding articles (values)
|
||||
pages The list of pages
|
||||
hidden_pages The list of hidden pages
|
||||
draft_pages The list of draft pages
|
||||
============= ===================================================
|
||||
|
||||
|
||||
|
|
@ -154,8 +155,8 @@ author.html
|
|||
|
||||
This template will be processed for each of the existing authors, with
|
||||
output generated according to the ``AUTHOR_SAVE_AS`` setting (`Default:`
|
||||
``author/{author_name}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``author/{author_name}{number}.html``.
|
||||
``author/{slug}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``author/{slug}{number}.html``.
|
||||
|
||||
====================== ===================================================
|
||||
Variable Description
|
||||
|
|
@ -188,8 +189,8 @@ category.html
|
|||
|
||||
This template will be processed for each of the existing categories, with
|
||||
output generated according to the ``CATEGORY_SAVE_AS`` setting (`Default:`
|
||||
``category/{category_name}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``category/{category_name}{number}.html``.
|
||||
``category/{slug}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``category/{slug}{number}.html``.
|
||||
|
||||
====================== ===================================================
|
||||
Variable Description
|
||||
|
|
@ -222,7 +223,7 @@ article.html
|
|||
|
||||
This template will be processed for each article, with
|
||||
output generated according to the ``ARTICLE_SAVE_AS`` setting (`Default:`
|
||||
``{article_name}.html``). The following variables are available when
|
||||
``{slug}.html``). The following variables are available when
|
||||
rendering.
|
||||
|
||||
============= ===================================================
|
||||
|
|
@ -263,7 +264,7 @@ page.html
|
|||
|
||||
This template will be processed for each page, with
|
||||
output generated according to the ``PAGE_SAVE_AS`` setting (`Default:`
|
||||
``pages/{page_name}.html``). The following variables are available when
|
||||
``pages/{slug}.html``). The following variables are available when
|
||||
rendering.
|
||||
|
||||
============= ===================================================
|
||||
|
|
@ -279,8 +280,8 @@ tag.html
|
|||
|
||||
This template will be processed for each tag, with
|
||||
output generated according to the ``TAG_SAVE_AS`` setting (`Default:`
|
||||
``tag/{tag_name}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``tag/{tag_name}{number}.html``.
|
||||
``tag/{slug}.html``). If pagination is active, subsequent pages will by
|
||||
default reside at ``tag/{slug}{number}.html``.
|
||||
|
||||
====================== ===================================================
|
||||
Variable Description
|
||||
|
|
@ -423,7 +424,7 @@ metadata Page header metadata `dict`.
|
|||
save_as Location to save the page.
|
||||
slug Page slug.
|
||||
source_path Full system path of the page source file.
|
||||
status The page status, can be any of 'published' or
|
||||
status The page status, can be any of 'published', 'hidden' or
|
||||
'draft'.
|
||||
summary Rendered summary content.
|
||||
tags List of :ref:`Tag <object-author_cat_tag>`
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ repository, and if you want to publish that Pelican site in the form of Project
|
|||
Pages to this repository, you can then use the following::
|
||||
|
||||
$ pelican content -o output -s pelicanconf.py
|
||||
$ ghp-import output
|
||||
$ ghp-import output -b gh-pages
|
||||
$ git push origin gh-pages
|
||||
|
||||
The ``ghp-import output`` command updates the local ``gh-pages`` branch with
|
||||
|
|
@ -67,7 +67,7 @@ already exist). The ``git push origin gh-pages`` command updates the remote
|
|||
``gh-pages`` branch, effectively publishing the Pelican site.
|
||||
|
||||
.. note::
|
||||
The ``github`` target of the Makefile (and the ``gh_pages`` task of the Fabfile)
|
||||
The ``github`` target of the Makefile (and the ``gh_pages`` task of ``tasks.py``)
|
||||
created by the ``pelican-quickstart`` command
|
||||
publishes the Pelican site as Project Pages, as described above.
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ your ``<username>.github.io`` repository on GitHub.
|
|||
Again, you can take advantage of ``ghp-import``::
|
||||
|
||||
$ pelican content -o output -s pelicanconf.py
|
||||
$ ghp-import output
|
||||
$ ghp-import output -b gh-pages
|
||||
$ git push git@github.com:elemoine/elemoine.github.io.git gh-pages:master
|
||||
|
||||
The ``git push`` command pushes the local ``gh-pages`` branch (freshly updated
|
||||
|
|
@ -98,6 +98,18 @@ by the ``ghp-import`` command) to the ``elemoine.github.io`` repository's
|
|||
|
||||
To publish your Pelican site as User Pages, feel free to adjust the
|
||||
``github`` target of the Makefile.
|
||||
|
||||
Another option for publishing to User Pages is to generate the output files in the root directory of the project.
|
||||
|
||||
For example, your main project folder is ``<username>.github.io`` and you can create the Pelican project in a subdirectory called ``Pelican``. Then from inside the ``Pelican`` folder you can run::
|
||||
|
||||
$ pelican content -o .. -s pelicanconf.py
|
||||
|
||||
Now you can push the whole project ``<username>.github.io`` to the master branch of your GitHub repository::
|
||||
|
||||
$ git push origin master
|
||||
|
||||
(assuming origin is set to your remote repository).
|
||||
|
||||
Custom 404 Pages
|
||||
----------------
|
||||
|
|
@ -147,3 +159,25 @@ embed videos in the markup. You can use `reST video directive
|
|||
<https://gist.github.com/dbrgn/2922648>`_ for reST or `mdx_video plugin
|
||||
<https://github.com/italomaia/mdx-video>`_ for Markdown.
|
||||
|
||||
|
||||
Develop Locally Using SSL
|
||||
==================================
|
||||
|
||||
Here's how you can set up your local pelican server to support SSL.
|
||||
|
||||
First, create a self-signed certificate and key using ``openssl`` (this creates ``cert.pem`` and ``key.pem``)::
|
||||
|
||||
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
|
||||
|
||||
And use this command to launch the server (the server starts within your ``output`` directory)::
|
||||
|
||||
python -m pelican.server 8443 --key=../key.pem --cert=../cert.pem
|
||||
|
||||
If you are using ``develop-server.sh``, add this to the top::
|
||||
|
||||
CERT="$BASEDIR/cert.pem"
|
||||
KEY="$BASEDIR/key.pem"
|
||||
|
||||
and modify the ``pelican.server`` line as follows::
|
||||
|
||||
$PY -m pelican.server $port --ssl --cert="$CERT" --key="$KEY" &
|
||||
|
|
@ -5,21 +5,25 @@ import argparse
|
|||
import collections
|
||||
import locale
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import six
|
||||
|
||||
# pelican.log has to be the first pelican module to be loaded
|
||||
# because logging.setLoggerClass has to be called before logging.getLogger
|
||||
from pelican.log import init
|
||||
from pelican.log import init as init_logging
|
||||
from pelican import signals # noqa
|
||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||
SourceFileGenerator, StaticGenerator,
|
||||
TemplatePagesGenerator)
|
||||
from pelican.readers import Readers
|
||||
from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer
|
||||
from pelican.settings import read_settings
|
||||
from pelican.utils import (clean_output_dir, file_watcher,
|
||||
folder_watcher, maybe_pluralize)
|
||||
|
|
@ -170,6 +174,10 @@ class Pelican(object):
|
|||
if hasattr(p, 'generate_context'):
|
||||
p.generate_context()
|
||||
|
||||
for p in generators:
|
||||
if hasattr(p, 'refresh_metadata_intersite_links'):
|
||||
p.refresh_metadata_intersite_links()
|
||||
|
||||
signals.all_generators_finalized.send(generators)
|
||||
|
||||
writer = self.get_writer()
|
||||
|
|
@ -205,13 +213,20 @@ class Pelican(object):
|
|||
len(pages_generator.hidden_translations)),
|
||||
'hidden page',
|
||||
'hidden pages')
|
||||
pluralized_draft_pages = maybe_pluralize(
|
||||
(len(pages_generator.draft_pages) +
|
||||
len(pages_generator.draft_translations)),
|
||||
'draft page',
|
||||
'draft pages')
|
||||
|
||||
print('Done: Processed {}, {}, {} and {} in {:.2f} seconds.'.format(
|
||||
pluralized_articles,
|
||||
pluralized_drafts,
|
||||
pluralized_pages,
|
||||
pluralized_hidden_pages,
|
||||
time.time() - start_time))
|
||||
print('Done: Processed {}, {}, {}, {} and {} in {:.2f} seconds.'
|
||||
.format(
|
||||
pluralized_articles,
|
||||
pluralized_drafts,
|
||||
pluralized_pages,
|
||||
pluralized_hidden_pages,
|
||||
pluralized_draft_pages,
|
||||
time.time() - start_time))
|
||||
|
||||
def get_generator_classes(self):
|
||||
generators = [ArticlesGenerator, PagesGenerator]
|
||||
|
|
@ -255,6 +270,32 @@ class Pelican(object):
|
|||
return writer(self.output_path, settings=self.settings)
|
||||
|
||||
|
||||
class PrintSettings(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string):
|
||||
instance, settings = get_instance(namespace)
|
||||
|
||||
if values:
|
||||
# One or more arguments provided, so only print those settings
|
||||
for setting in values:
|
||||
if setting in settings:
|
||||
# Only add newline between setting name and value if dict
|
||||
if isinstance(settings[setting], dict):
|
||||
setting_format = '\n{}:\n{}'
|
||||
else:
|
||||
setting_format = '\n{}: {}'
|
||||
print(setting_format.format(
|
||||
setting,
|
||||
pprint.pformat(settings[setting])))
|
||||
else:
|
||||
print('\n{} is not a recognized setting.'.format(setting))
|
||||
break
|
||||
else:
|
||||
# No argument was given to --print-settings, so print all settings
|
||||
pprint.pprint(settings)
|
||||
|
||||
parser.exit()
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='A tool to generate a static blog, '
|
||||
|
|
@ -305,6 +346,12 @@ def parse_arguments():
|
|||
help='Relaunch pelican each time a modification occurs'
|
||||
' on the content files.')
|
||||
|
||||
parser.add_argument('--print-settings', dest='print_settings', nargs='*',
|
||||
action=PrintSettings, metavar='SETTING_NAME',
|
||||
help='Print current configuration settings and exit. '
|
||||
'Append one or more setting name arguments to see the '
|
||||
'values for specific settings only.')
|
||||
|
||||
parser.add_argument('--relative-urls', dest='relative_paths',
|
||||
action='store_true',
|
||||
help='Use relative urls in output, '
|
||||
|
|
@ -327,7 +374,29 @@ def parse_arguments():
|
|||
help=('Exit the program with non-zero status if any '
|
||||
'errors/warnings encountered.'))
|
||||
|
||||
return parser.parse_args()
|
||||
parser.add_argument('--logs-dedup-min-level', default='WARNING',
|
||||
choices=('DEBUG', 'INFO', 'WARNING', 'ERROR'),
|
||||
help=('Only enable log de-duplication for levels equal'
|
||||
' to or above the specified value'))
|
||||
|
||||
parser.add_argument('-l', '--listen', dest='listen', action='store_true',
|
||||
help='Serve content files via HTTP and port 8000.')
|
||||
|
||||
parser.add_argument('-p', '--port', dest='port', type=int,
|
||||
help='Port to serve HTTP files at. (default: 8000)')
|
||||
|
||||
parser.add_argument('-b', '--bind', dest='bind',
|
||||
help='IP to bind to when serving files via HTTP '
|
||||
'(default: 127.0.0.1)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.port is not None and not args.listen:
|
||||
logger.warning('--port without --listen has no effect')
|
||||
if args.bind is not None and not args.listen:
|
||||
logger.warning('--bind without --listen has no effect')
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def get_config(args):
|
||||
|
|
@ -350,6 +419,10 @@ def get_config(args):
|
|||
config['WRITE_SELECTED'] = args.selected_paths.split(',')
|
||||
if args.relative_paths:
|
||||
config['RELATIVE_URLS'] = args.relative_paths
|
||||
if args.port is not None:
|
||||
config['PORT'] = args.port
|
||||
if args.bind is not None:
|
||||
config['BIND'] = args.bind
|
||||
config['DEBUG'] = args.verbosity == logging.DEBUG
|
||||
|
||||
# argparse returns bytes in Py2. There is no definite answer as to which
|
||||
|
|
@ -382,16 +455,118 @@ def get_instance(args):
|
|||
return cls(settings), settings
|
||||
|
||||
|
||||
def autoreload(watchers, args, old_static, reader_descs, excqueue=None):
|
||||
while True:
|
||||
try:
|
||||
# Check source dir for changed files ending with the given
|
||||
# extension in the settings. In the theme dir is no such
|
||||
# restriction; all files are recursively checked if they
|
||||
# have changed, no matter what extension the filenames
|
||||
# have.
|
||||
modified = {k: next(v) for k, v in watchers.items()}
|
||||
|
||||
if modified['settings']:
|
||||
pelican, settings = get_instance(args)
|
||||
|
||||
# Adjust static watchers if there are any changes
|
||||
new_static = settings.get("STATIC_PATHS", [])
|
||||
|
||||
# Added static paths
|
||||
# Add new watchers and set them as modified
|
||||
new_watchers = set(new_static).difference(old_static)
|
||||
for static_path in new_watchers:
|
||||
static_key = '[static]%s' % static_path
|
||||
watchers[static_key] = folder_watcher(
|
||||
os.path.join(pelican.path, static_path),
|
||||
[''],
|
||||
pelican.ignore_files)
|
||||
modified[static_key] = next(watchers[static_key])
|
||||
|
||||
# Removed static paths
|
||||
# Remove watchers and modified values
|
||||
old_watchers = set(old_static).difference(new_static)
|
||||
for static_path in old_watchers:
|
||||
static_key = '[static]%s' % static_path
|
||||
watchers.pop(static_key)
|
||||
modified.pop(static_key)
|
||||
|
||||
# Replace old_static with the new one
|
||||
old_static = new_static
|
||||
|
||||
if any(modified.values()):
|
||||
print('\n-> Modified: {}. re-generating...'.format(
|
||||
', '.join(k for k, v in modified.items() if v)))
|
||||
|
||||
if modified['content'] is None:
|
||||
logger.warning(
|
||||
'No valid files found in content for '
|
||||
+ 'the active readers:\n'
|
||||
+ '\n'.join(reader_descs))
|
||||
|
||||
if modified['theme'] is None:
|
||||
logger.warning('Empty theme folder. Using `basic` '
|
||||
'theme.')
|
||||
|
||||
pelican.run()
|
||||
|
||||
except KeyboardInterrupt as e:
|
||||
logger.warning("Keyboard interrupt, quitting.")
|
||||
if excqueue is not None:
|
||||
excqueue.put(traceback.format_exception_only(type(e), e)[-1])
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
if (args.verbosity == logging.DEBUG):
|
||||
if excqueue is not None:
|
||||
excqueue.put(
|
||||
traceback.format_exception_only(type(e), e)[-1])
|
||||
else:
|
||||
raise
|
||||
logger.warning(
|
||||
'Caught exception "%s". Reloading.', e)
|
||||
|
||||
finally:
|
||||
time.sleep(.5) # sleep to avoid cpu load
|
||||
|
||||
|
||||
def listen(server, port, output, excqueue=None):
|
||||
RootedHTTPServer.allow_reuse_address = True
|
||||
try:
|
||||
httpd = RootedHTTPServer(
|
||||
output, (server, port), ComplexHTTPRequestHandler)
|
||||
except OSError as e:
|
||||
logging.error("Could not listen on port %s, server %s.", port, server)
|
||||
if excqueue is not None:
|
||||
excqueue.put(traceback.format_exception_only(type(e), e)[-1])
|
||||
return
|
||||
|
||||
logging.info("Serving at port %s, server %s.", port, server)
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except Exception as e:
|
||||
if excqueue is not None:
|
||||
excqueue.put(traceback.format_exception_only(type(e), e)[-1])
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_arguments()
|
||||
init(args.verbosity, args.fatal)
|
||||
logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level)
|
||||
init_logging(args.verbosity, args.fatal,
|
||||
logs_dedup_min_level=logs_dedup_min_level)
|
||||
|
||||
logger.debug('Pelican version: %s', __version__)
|
||||
logger.debug('Python version: %s', sys.version.split()[0])
|
||||
|
||||
try:
|
||||
pelican, settings = get_instance(args)
|
||||
|
||||
readers = Readers(settings)
|
||||
reader_descs = sorted(set(['%s (%s)' %
|
||||
(type(r).__name__,
|
||||
', '.join(r.file_extensions))
|
||||
for r in readers.readers.values()
|
||||
if r.enabled]))
|
||||
|
||||
watchers = {'content': folder_watcher(pelican.path,
|
||||
readers.extensions,
|
||||
|
|
@ -410,76 +585,34 @@ def main():
|
|||
[''],
|
||||
pelican.ignore_files)
|
||||
|
||||
if args.autoreload:
|
||||
if args.autoreload and args.listen:
|
||||
excqueue = multiprocessing.Queue()
|
||||
p1 = multiprocessing.Process(
|
||||
target=autoreload,
|
||||
args=(watchers, args, old_static, reader_descs, excqueue))
|
||||
p2 = multiprocessing.Process(
|
||||
target=listen,
|
||||
args=(settings.get('BIND'), settings.get('PORT'),
|
||||
settings.get("OUTPUT_PATH"), excqueue))
|
||||
p1.start()
|
||||
p2.start()
|
||||
exc = excqueue.get()
|
||||
p1.terminate()
|
||||
p2.terminate()
|
||||
logger.critical(exc)
|
||||
elif args.autoreload:
|
||||
print(' --- AutoReload Mode: Monitoring `content`, `theme` and'
|
||||
' `settings` for changes. ---')
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Check source dir for changed files ending with the given
|
||||
# extension in the settings. In the theme dir is no such
|
||||
# restriction; all files are recursively checked if they
|
||||
# have changed, no matter what extension the filenames
|
||||
# have.
|
||||
modified = {k: next(v) for k, v in watchers.items()}
|
||||
|
||||
if modified['settings']:
|
||||
pelican, settings = get_instance(args)
|
||||
|
||||
# Adjust static watchers if there are any changes
|
||||
new_static = settings.get("STATIC_PATHS", [])
|
||||
|
||||
# Added static paths
|
||||
# Add new watchers and set them as modified
|
||||
new_watchers = set(new_static).difference(old_static)
|
||||
for static_path in new_watchers:
|
||||
static_key = '[static]%s' % static_path
|
||||
watchers[static_key] = folder_watcher(
|
||||
os.path.join(pelican.path, static_path),
|
||||
[''],
|
||||
pelican.ignore_files)
|
||||
modified[static_key] = next(watchers[static_key])
|
||||
|
||||
# Removed static paths
|
||||
# Remove watchers and modified values
|
||||
old_watchers = set(old_static).difference(new_static)
|
||||
for static_path in old_watchers:
|
||||
static_key = '[static]%s' % static_path
|
||||
watchers.pop(static_key)
|
||||
modified.pop(static_key)
|
||||
|
||||
# Replace old_static with the new one
|
||||
old_static = new_static
|
||||
|
||||
if any(modified.values()):
|
||||
print('\n-> Modified: {}. re-generating...'.format(
|
||||
', '.join(k for k, v in modified.items() if v)))
|
||||
|
||||
if modified['content'] is None:
|
||||
logger.warning('No valid files found in content.')
|
||||
|
||||
if modified['theme'] is None:
|
||||
logger.warning('Empty theme folder. Using `basic` '
|
||||
'theme.')
|
||||
|
||||
pelican.run()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.warning("Keyboard interrupt, quitting.")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
if (args.verbosity == logging.DEBUG):
|
||||
raise
|
||||
logger.warning(
|
||||
'Caught exception "%s". Reloading.', e)
|
||||
|
||||
finally:
|
||||
time.sleep(.5) # sleep to avoid cpu load
|
||||
|
||||
autoreload(watchers, args, old_static, reader_descs)
|
||||
elif args.listen:
|
||||
listen(settings.get('BIND'), settings.get('PORT'),
|
||||
settings.get("OUTPUT_PATH"))
|
||||
else:
|
||||
if next(watchers['content']) is None:
|
||||
logger.warning('No valid files found in content.')
|
||||
logger.warning(
|
||||
'No valid files found in content for '
|
||||
+ 'the active readers:\n'
|
||||
+ '\n'.join(reader_descs))
|
||||
|
||||
if next(watchers['theme']) is None:
|
||||
logger.warning('Empty theme folder. Using `basic` theme.')
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import sys
|
|||
import pytz
|
||||
|
||||
import six
|
||||
from six.moves.urllib.parse import urlparse, urlunparse
|
||||
from six.moves.urllib.parse import urljoin, urlparse, urlunparse
|
||||
|
||||
from pelican import signals
|
||||
from pelican.settings import DEFAULT_CONFIG
|
||||
|
|
@ -98,14 +98,16 @@ class Content(object):
|
|||
if not hasattr(self, 'slug'):
|
||||
if (settings['SLUGIFY_SOURCE'] == 'title' and
|
||||
hasattr(self, 'title')):
|
||||
self.slug = slugify(self.title,
|
||||
settings.get('SLUG_SUBSTITUTIONS', ()))
|
||||
self.slug = slugify(
|
||||
self.title,
|
||||
regex_subs=settings.get('SLUG_REGEX_SUBSTITUTIONS', []))
|
||||
elif (settings['SLUGIFY_SOURCE'] == 'basename' and
|
||||
source_path is not None):
|
||||
basename = os.path.basename(
|
||||
os.path.splitext(source_path)[0])
|
||||
self.slug = slugify(
|
||||
basename, settings.get('SLUG_SUBSTITUTIONS', ()))
|
||||
basename,
|
||||
regex_subs=settings.get('SLUG_REGEX_SUBSTITUTIONS', []))
|
||||
|
||||
self.source_path = source_path
|
||||
|
||||
|
|
@ -140,9 +142,8 @@ class Content(object):
|
|||
if not hasattr(self, 'status'):
|
||||
self.status = getattr(self, 'default_status', None)
|
||||
|
||||
# store the summary metadata if it is set
|
||||
if 'summary' in metadata:
|
||||
self._summary = metadata['summary']
|
||||
if len(self._context.get('filenames', [])) > 0:
|
||||
self.refresh_metadata_intersite_links()
|
||||
|
||||
signals.content_object_init.send(self)
|
||||
|
||||
|
|
@ -228,6 +229,87 @@ class Content(object):
|
|||
key = key if self.in_default_lang else 'lang_%s' % key
|
||||
return self._expand_settings(key)
|
||||
|
||||
def _link_replacer(self, siteurl, m):
|
||||
what = m.group('what')
|
||||
value = urlparse(m.group('value'))
|
||||
path = value.path
|
||||
origin = m.group('path')
|
||||
|
||||
# urllib.parse.urljoin() produces `a.html` for urljoin("..", "a.html")
|
||||
# so if RELATIVE_URLS are enabled, we fall back to os.path.join() to
|
||||
# properly get `../a.html`. However, os.path.join() produces
|
||||
# `baz/http://foo/bar.html` for join("baz", "http://foo/bar.html")
|
||||
# instead of correct "http://foo/bar.html", so one has to pick a side
|
||||
# as there is no silver bullet.
|
||||
if self.settings['RELATIVE_URLS']:
|
||||
joiner = os.path.join
|
||||
else:
|
||||
joiner = urljoin
|
||||
|
||||
# However, it's not *that* simple: urljoin("blog", "index.html")
|
||||
# produces just `index.html` instead of `blog/index.html` (unlike
|
||||
# os.path.join()), so in order to get a correct answer one needs to
|
||||
# append a trailing slash to siteurl in that case. This also makes
|
||||
# the new behavior fully compatible with Pelican 3.7.1.
|
||||
if not siteurl.endswith('/'):
|
||||
siteurl += '/'
|
||||
|
||||
# XXX Put this in a different location.
|
||||
if what in {'filename', 'attach'}:
|
||||
if path.startswith('/'):
|
||||
path = path[1:]
|
||||
else:
|
||||
# relative to the source path of this content
|
||||
path = self.get_relative_source_path(
|
||||
os.path.join(self.relative_dir, path)
|
||||
)
|
||||
|
||||
if path not in self._context['filenames']:
|
||||
unquoted_path = path.replace('%20', ' ')
|
||||
|
||||
if unquoted_path in self._context['filenames']:
|
||||
path = unquoted_path
|
||||
|
||||
linked_content = self._context['filenames'].get(path)
|
||||
if linked_content:
|
||||
if what == 'attach':
|
||||
if isinstance(linked_content, Static):
|
||||
linked_content.attach_to(self)
|
||||
else:
|
||||
logger.warning(
|
||||
"%s used {attach} link syntax on a "
|
||||
"non-static file. Use {filename} instead.",
|
||||
self.get_relative_source_path())
|
||||
origin = joiner(siteurl, linked_content.url)
|
||||
origin = origin.replace('\\', '/') # for Windows paths.
|
||||
else:
|
||||
logger.warning(
|
||||
"Unable to find '%s', skipping url replacement.",
|
||||
value.geturl(), extra={
|
||||
'limit_msg': ("Other resources were not found "
|
||||
"and their urls not replaced")})
|
||||
elif what == 'category':
|
||||
origin = joiner(siteurl, Category(path, self.settings).url)
|
||||
elif what == 'tag':
|
||||
origin = joiner(siteurl, Tag(path, self.settings).url)
|
||||
elif what == 'index':
|
||||
origin = joiner(siteurl, self.settings['INDEX_SAVE_AS'])
|
||||
elif what == 'author':
|
||||
origin = joiner(siteurl, Author(path, self.settings).url)
|
||||
else:
|
||||
logger.warning(
|
||||
"Replacement Indicator '%s' not recognized, "
|
||||
"skipping replacement",
|
||||
what)
|
||||
|
||||
# keep all other parts, such as query, fragment, etc.
|
||||
parts = list(value)
|
||||
parts[2] = origin
|
||||
origin = urlunparse(parts)
|
||||
|
||||
return ''.join((m.group('markup'), m.group('quote'), origin,
|
||||
m.group('quote')))
|
||||
|
||||
def _update_content(self, content, siteurl):
|
||||
"""Update the content attribute.
|
||||
|
||||
|
|
@ -251,69 +333,7 @@ class Content(object):
|
|||
\2""".format(instrasite_link_regex)
|
||||
hrefs = re.compile(regex, re.X)
|
||||
|
||||
def replacer(m):
|
||||
what = m.group('what')
|
||||
value = urlparse(m.group('value'))
|
||||
path = value.path
|
||||
origin = m.group('path')
|
||||
|
||||
# XXX Put this in a different location.
|
||||
if what in {'filename', 'attach'}:
|
||||
if path.startswith('/'):
|
||||
path = path[1:]
|
||||
else:
|
||||
# relative to the source path of this content
|
||||
path = self.get_relative_source_path(
|
||||
os.path.join(self.relative_dir, path)
|
||||
)
|
||||
|
||||
if path not in self._context['filenames']:
|
||||
unquoted_path = path.replace('%20', ' ')
|
||||
|
||||
if unquoted_path in self._context['filenames']:
|
||||
path = unquoted_path
|
||||
|
||||
linked_content = self._context['filenames'].get(path)
|
||||
if linked_content:
|
||||
if what == 'attach':
|
||||
if isinstance(linked_content, Static):
|
||||
linked_content.attach_to(self)
|
||||
else:
|
||||
logger.warning(
|
||||
"%s used {attach} link syntax on a "
|
||||
"non-static file. Use {filename} instead.",
|
||||
self.get_relative_source_path())
|
||||
origin = '/'.join((siteurl, linked_content.url))
|
||||
origin = origin.replace('\\', '/') # for Windows paths.
|
||||
else:
|
||||
logger.warning(
|
||||
"Unable to find '%s', skipping url replacement.",
|
||||
value.geturl(), extra={
|
||||
'limit_msg': ("Other resources were not found "
|
||||
"and their urls not replaced")})
|
||||
elif what == 'category':
|
||||
origin = '/'.join((siteurl, Category(path, self.settings).url))
|
||||
elif what == 'tag':
|
||||
origin = '/'.join((siteurl, Tag(path, self.settings).url))
|
||||
elif what == 'index':
|
||||
origin = '/'.join((siteurl, self.settings['INDEX_SAVE_AS']))
|
||||
elif what == 'author':
|
||||
origin = '/'.join((siteurl, Author(path, self.settings).url))
|
||||
else:
|
||||
logger.warning(
|
||||
"Replacement Indicator '%s' not recognized, "
|
||||
"skipping replacement",
|
||||
what)
|
||||
|
||||
# keep all other parts, such as query, fragment, etc.
|
||||
parts = list(value)
|
||||
parts[2] = origin
|
||||
origin = urlunparse(parts)
|
||||
|
||||
return ''.join((m.group('markup'), m.group('quote'), origin,
|
||||
m.group('quote')))
|
||||
|
||||
return hrefs.sub(replacer, content)
|
||||
return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content)
|
||||
|
||||
def get_siteurl(self):
|
||||
return self._context.get('localsiteurl', '')
|
||||
|
|
@ -337,8 +357,8 @@ class Content(object):
|
|||
This is based on the summary metadata if set, otherwise truncate the
|
||||
content.
|
||||
"""
|
||||
if hasattr(self, '_summary'):
|
||||
return self._update_content(self._summary, siteurl)
|
||||
if 'summary' in self.metadata:
|
||||
return self.metadata['summary']
|
||||
|
||||
if self.settings['SUMMARY_MAX_LENGTH'] is None:
|
||||
return self.content
|
||||
|
|
@ -413,13 +433,27 @@ class Content(object):
|
|||
os.path.abspath(self.source_path),
|
||||
os.path.abspath(self.settings['PATH']))))
|
||||
|
||||
def refresh_metadata_intersite_links(self):
|
||||
for key in self.settings['FORMATTED_FIELDS']:
|
||||
if key in self.metadata:
|
||||
value = self._update_content(
|
||||
self.metadata[key],
|
||||
self.get_siteurl()
|
||||
)
|
||||
self.metadata[key] = value
|
||||
setattr(self, key.lower(), value)
|
||||
|
||||
|
||||
class Page(Content):
|
||||
mandatory_properties = ('title',)
|
||||
allowed_statuses = ('published', 'hidden')
|
||||
allowed_statuses = ('published', 'hidden', 'draft')
|
||||
default_status = 'published'
|
||||
default_template = 'page'
|
||||
|
||||
def _expand_settings(self, key):
|
||||
klass = 'draft_page' if self.status == 'draft' else None
|
||||
return super(Page, self)._expand_settings(key, klass)
|
||||
|
||||
|
||||
class Article(Content):
|
||||
mandatory_properties = ('title', 'date', 'category')
|
||||
|
|
@ -444,7 +478,7 @@ class Article(Content):
|
|||
self.date = SafeDatetime.max
|
||||
|
||||
def _expand_settings(self, key):
|
||||
klass = 'article' if self.status == 'published' else 'draft'
|
||||
klass = 'draft' if self.status == 'draft' else 'article'
|
||||
return super(Article, self)._expand_settings(key, klass)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ from pelican import signals
|
|||
from pelican.cache import FileStampDataCacher
|
||||
from pelican.contents import Article, Page, Static
|
||||
from pelican.readers import Readers
|
||||
from pelican.utils import (DateFormatter, copy, mkdir_p, posixize_path,
|
||||
process_translations, python_2_unicode_compatible)
|
||||
from pelican.utils import (DateFormatter, copy, mkdir_p, order_content,
|
||||
posixize_path, process_translations,
|
||||
python_2_unicode_compatible)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -51,20 +52,25 @@ class Generator(object):
|
|||
|
||||
# templates cache
|
||||
self._templates = {}
|
||||
self._templates_path = []
|
||||
self._templates_path.append(os.path.expanduser(
|
||||
os.path.join(self.theme, 'templates')))
|
||||
self._templates_path += self.settings['EXTRA_TEMPLATES_PATHS']
|
||||
self._templates_path = list(self.settings['THEME_TEMPLATES_OVERRIDES'])
|
||||
|
||||
theme_path = os.path.dirname(os.path.abspath(__file__))
|
||||
theme_templates_path = os.path.expanduser(
|
||||
os.path.join(self.theme, 'templates'))
|
||||
self._templates_path.append(theme_templates_path)
|
||||
theme_loader = FileSystemLoader(theme_templates_path)
|
||||
|
||||
simple_theme_path = os.path.dirname(os.path.abspath(__file__))
|
||||
simple_loader = FileSystemLoader(
|
||||
os.path.join(simple_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),
|
||||
simple_loader, # implicit inheritance
|
||||
PrefixLoader({'!simple': simple_loader}) # explicit one
|
||||
PrefixLoader({
|
||||
'!simple': simple_loader,
|
||||
'!theme': theme_loader
|
||||
}) # explicit ones
|
||||
]),
|
||||
**self.settings['JINJA_ENVIRONMENT']
|
||||
)
|
||||
|
|
@ -86,12 +92,19 @@ class Generator(object):
|
|||
templates ready to use with Jinja2.
|
||||
"""
|
||||
if name not in self._templates:
|
||||
try:
|
||||
self._templates[name] = self.env.get_template(name + '.html')
|
||||
except TemplateNotFound:
|
||||
for ext in self.settings['TEMPLATE_EXTENSIONS']:
|
||||
try:
|
||||
self._templates[name] = self.env.get_template(name + ext)
|
||||
break
|
||||
except TemplateNotFound:
|
||||
continue
|
||||
|
||||
if name not in self._templates:
|
||||
raise PelicanTemplateNotFound(
|
||||
'[templates] unable to load {}.html from {}'.format(
|
||||
name, self._templates_path))
|
||||
'[templates] unable to load {}[{}] from {}'.format(
|
||||
name, ', '.join(self.settings['TEMPLATE_EXTENSIONS']),
|
||||
self._templates_path))
|
||||
|
||||
return self._templates[name]
|
||||
|
||||
def _include_path(self, path, extensions=None):
|
||||
|
|
@ -255,7 +268,7 @@ class TemplatePagesGenerator(Generator):
|
|||
template = self.env.get_template(source)
|
||||
rurls = self.settings['RELATIVE_URLS']
|
||||
writer.write_file(dest, template, self.context, rurls,
|
||||
override_output=True)
|
||||
override_output=True, url='')
|
||||
finally:
|
||||
del self.env.loader.loaders[0]
|
||||
|
||||
|
|
@ -282,67 +295,100 @@ class ArticlesGenerator(CachingGenerator):
|
|||
|
||||
if self.settings.get('FEED_ATOM'):
|
||||
writer.write_feed(self.articles, self.context,
|
||||
self.settings['FEED_ATOM'])
|
||||
self.settings['FEED_ATOM'],
|
||||
self.settings.get('FEED_ATOM_URL',
|
||||
self.settings['FEED_ATOM']))
|
||||
|
||||
if self.settings.get('FEED_RSS'):
|
||||
writer.write_feed(self.articles, self.context,
|
||||
self.settings['FEED_RSS'], feed_type='rss')
|
||||
self.settings['FEED_RSS'],
|
||||
self.settings.get('FEED_RSS_URL',
|
||||
self.settings['FEED_RSS']),
|
||||
feed_type='rss')
|
||||
|
||||
if (self.settings.get('FEED_ALL_ATOM') or
|
||||
self.settings.get('FEED_ALL_RSS')):
|
||||
all_articles = list(self.articles)
|
||||
for article in self.articles:
|
||||
all_articles.extend(article.translations)
|
||||
all_articles.sort(key=attrgetter('date'), reverse=True)
|
||||
order_content(all_articles,
|
||||
order_by=self.settings['ARTICLE_ORDER_BY'])
|
||||
|
||||
if self.settings.get('FEED_ALL_ATOM'):
|
||||
writer.write_feed(all_articles, self.context,
|
||||
self.settings['FEED_ALL_ATOM'])
|
||||
self.settings['FEED_ALL_ATOM'],
|
||||
self.settings.get(
|
||||
'FEED_ALL_ATOM_URL',
|
||||
self.settings['FEED_ALL_ATOM']))
|
||||
|
||||
if self.settings.get('FEED_ALL_RSS'):
|
||||
writer.write_feed(all_articles, self.context,
|
||||
self.settings['FEED_ALL_RSS'],
|
||||
self.settings.get(
|
||||
'FEED_ALL_RSS_URL',
|
||||
self.settings['FEED_ALL_RSS']),
|
||||
feed_type='rss')
|
||||
|
||||
for cat, arts in self.categories:
|
||||
arts.sort(key=attrgetter('date'), reverse=True)
|
||||
if self.settings.get('CATEGORY_FEED_ATOM'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['CATEGORY_FEED_ATOM']
|
||||
% cat.slug,
|
||||
self.settings.get(
|
||||
'CATEGORY_FEED_ATOM_URL',
|
||||
self.settings['CATEGORY_FEED_ATOM'])
|
||||
% cat.slug, feed_title=cat.name)
|
||||
|
||||
if self.settings.get('CATEGORY_FEED_RSS'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['CATEGORY_FEED_RSS']
|
||||
% cat.slug,
|
||||
self.settings.get(
|
||||
'CATEGORY_FEED_RSS_URL',
|
||||
self.settings['CATEGORY_FEED_RSS'])
|
||||
% cat.slug, feed_title=cat.name,
|
||||
feed_type='rss')
|
||||
|
||||
for auth, arts in self.authors:
|
||||
arts.sort(key=attrgetter('date'), reverse=True)
|
||||
if self.settings.get('AUTHOR_FEED_ATOM'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['AUTHOR_FEED_ATOM']
|
||||
% auth.slug,
|
||||
self.settings.get(
|
||||
'AUTHOR_FEED_ATOM_URL',
|
||||
self.settings['AUTHOR_FEED_ATOM'])
|
||||
% auth.slug, feed_title=auth.name)
|
||||
|
||||
if self.settings.get('AUTHOR_FEED_RSS'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['AUTHOR_FEED_RSS']
|
||||
% auth.slug,
|
||||
self.settings.get(
|
||||
'AUTHOR_FEED_RSS_URL',
|
||||
self.settings['AUTHOR_FEED_RSS'])
|
||||
% auth.slug, feed_title=auth.name,
|
||||
feed_type='rss')
|
||||
|
||||
if (self.settings.get('TAG_FEED_ATOM') or
|
||||
self.settings.get('TAG_FEED_RSS')):
|
||||
for tag, arts in self.tags.items():
|
||||
arts.sort(key=attrgetter('date'), reverse=True)
|
||||
if self.settings.get('TAG_FEED_ATOM'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['TAG_FEED_ATOM']
|
||||
% tag.slug,
|
||||
self.settings.get(
|
||||
'TAG_FEED_ATOM_URL',
|
||||
self.settings['TAG_FEED_ATOM'])
|
||||
% tag.slug, feed_title=tag.name)
|
||||
|
||||
if self.settings.get('TAG_FEED_RSS'):
|
||||
writer.write_feed(arts, self.context,
|
||||
self.settings['TAG_FEED_RSS'] % tag.slug,
|
||||
feed_title=tag.name, feed_type='rss')
|
||||
self.settings.get(
|
||||
'TAG_FEED_RSS_URL',
|
||||
self.settings['TAG_FEED_RSS'])
|
||||
% tag.slug, feed_title=tag.name,
|
||||
feed_type='rss')
|
||||
|
||||
if (self.settings.get('TRANSLATION_FEED_ATOM') or
|
||||
self.settings.get('TRANSLATION_FEED_RSS')):
|
||||
|
|
@ -351,15 +397,22 @@ class ArticlesGenerator(CachingGenerator):
|
|||
translations_feeds[article.lang].append(article)
|
||||
|
||||
for lang, items in translations_feeds.items():
|
||||
items.sort(key=attrgetter('date'), reverse=True)
|
||||
items = order_content(
|
||||
items, order_by=self.settings['ARTICLE_ORDER_BY'])
|
||||
if self.settings.get('TRANSLATION_FEED_ATOM'):
|
||||
writer.write_feed(
|
||||
items, self.context,
|
||||
self.settings['TRANSLATION_FEED_ATOM'] % lang)
|
||||
self.settings['TRANSLATION_FEED_ATOM'] % lang,
|
||||
self.settings.get(
|
||||
'TRANSLATION_FEED_ATOM_URL',
|
||||
self.settings['TRANSLATION_FEED_ATOM']) % lang)
|
||||
if self.settings.get('TRANSLATION_FEED_RSS'):
|
||||
writer.write_feed(
|
||||
items, self.context,
|
||||
self.settings['TRANSLATION_FEED_RSS'] % lang,
|
||||
self.settings.get(
|
||||
'TRANSLATION_FEED_RSS_URL',
|
||||
self.settings['TRANSLATION_FEED_RSS']) % lang,
|
||||
feed_type='rss')
|
||||
|
||||
def generate_articles(self, write):
|
||||
|
|
@ -369,7 +422,7 @@ class ArticlesGenerator(CachingGenerator):
|
|||
write(article.save_as, self.get_template(article.template),
|
||||
self.context, article=article, category=article.category,
|
||||
override_output=hasattr(article, 'override_save_as'),
|
||||
blog=True)
|
||||
url=article.url, blog=True)
|
||||
|
||||
def generate_period_archives(self, write):
|
||||
"""Generate per-year, per-month, and per-day archives."""
|
||||
|
|
@ -384,24 +437,32 @@ class ArticlesGenerator(CachingGenerator):
|
|||
'day': self.settings['DAY_ARCHIVE_SAVE_AS'],
|
||||
}
|
||||
|
||||
period_url = {
|
||||
'year': self.settings['YEAR_ARCHIVE_URL'],
|
||||
'month': self.settings['MONTH_ARCHIVE_URL'],
|
||||
'day': self.settings['DAY_ARCHIVE_URL'],
|
||||
}
|
||||
|
||||
period_date_key = {
|
||||
'year': attrgetter('date.year'),
|
||||
'month': attrgetter('date.year', 'date.month'),
|
||||
'day': attrgetter('date.year', 'date.month', 'date.day')
|
||||
}
|
||||
|
||||
def _generate_period_archives(dates, key, save_as_fmt):
|
||||
def _generate_period_archives(dates, key, save_as_fmt, url_fmt):
|
||||
"""Generate period archives from `dates`, grouped by
|
||||
`key` and written to `save_as`.
|
||||
"""
|
||||
# `dates` is already sorted by date
|
||||
for _period, group in groupby(dates, key=key):
|
||||
archive = list(group)
|
||||
articles = [a for a in self.articles if a in archive]
|
||||
# arbitrarily grab the first date so that the usual
|
||||
# format string syntax can be used for specifying the
|
||||
# period archive dates
|
||||
date = archive[0].date
|
||||
save_as = save_as_fmt.format(date=date)
|
||||
url = url_fmt.format(date=date)
|
||||
context = self.context.copy()
|
||||
|
||||
if key == period_date_key['year']:
|
||||
|
|
@ -418,62 +479,60 @@ class ArticlesGenerator(CachingGenerator):
|
|||
month_name,
|
||||
_period[2])
|
||||
|
||||
write(save_as, template, context,
|
||||
dates=archive, blog=True)
|
||||
write(save_as, template, context, articles=articles,
|
||||
dates=archive, template_name='period_archives',
|
||||
blog=True, url=url)
|
||||
|
||||
for period in 'year', 'month', 'day':
|
||||
save_as = period_save_as[period]
|
||||
url = period_url[period]
|
||||
if save_as:
|
||||
key = period_date_key[period]
|
||||
_generate_period_archives(self.dates, key, save_as)
|
||||
_generate_period_archives(self.dates, key, save_as, url)
|
||||
|
||||
def generate_direct_templates(self, write):
|
||||
"""Generate direct templates pages"""
|
||||
PAGINATED_TEMPLATES = self.settings['PAGINATED_DIRECT_TEMPLATES']
|
||||
for template in self.settings['DIRECT_TEMPLATES']:
|
||||
paginated = {}
|
||||
if template in PAGINATED_TEMPLATES:
|
||||
paginated = {'articles': self.articles, 'dates': self.dates}
|
||||
save_as = self.settings.get("%s_SAVE_AS" % template.upper(),
|
||||
'%s.html' % template)
|
||||
url = self.settings.get("%s_URL" % template.upper(),
|
||||
'%s.html' % template)
|
||||
if not save_as:
|
||||
continue
|
||||
|
||||
write(save_as, self.get_template(template),
|
||||
self.context, blog=True, paginated=paginated,
|
||||
page_name=os.path.splitext(save_as)[0])
|
||||
write(save_as, self.get_template(template), self.context,
|
||||
articles=self.articles, dates=self.dates, blog=True,
|
||||
template_name=template,
|
||||
page_name=os.path.splitext(save_as)[0], url=url)
|
||||
|
||||
def generate_tags(self, write):
|
||||
"""Generate Tags pages."""
|
||||
tag_template = self.get_template('tag')
|
||||
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.save_as, tag_template, self.context, tag=tag,
|
||||
articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates}, blog=True,
|
||||
page_name=tag.page_name, all_articles=self.articles)
|
||||
url=tag.url, articles=articles, dates=dates,
|
||||
template_name='tag', blog=True, page_name=tag.page_name,
|
||||
all_articles=self.articles)
|
||||
|
||||
def generate_categories(self, write):
|
||||
"""Generate category pages."""
|
||||
category_template = self.get_template('category')
|
||||
for cat, articles in self.categories:
|
||||
articles.sort(key=attrgetter('date'), reverse=True)
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
write(cat.save_as, category_template, self.context,
|
||||
write(cat.save_as, category_template, self.context, url=cat.url,
|
||||
category=cat, articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates}, blog=True,
|
||||
page_name=cat.page_name, all_articles=self.articles)
|
||||
template_name='category', blog=True, page_name=cat.page_name,
|
||||
all_articles=self.articles)
|
||||
|
||||
def generate_authors(self, write):
|
||||
"""Generate Author pages."""
|
||||
author_template = self.get_template('author')
|
||||
for aut, articles in self.authors:
|
||||
articles.sort(key=attrgetter('date'), reverse=True)
|
||||
dates = [article for article in self.dates if article in articles]
|
||||
write(aut.save_as, author_template, self.context,
|
||||
author=aut, articles=articles, dates=dates,
|
||||
paginated={'articles': articles, 'dates': dates}, blog=True,
|
||||
url=aut.url, author=aut, articles=articles, dates=dates,
|
||||
template_name='author', blog=True,
|
||||
page_name=aut.page_name, all_articles=self.articles)
|
||||
|
||||
def generate_drafts(self, write):
|
||||
|
|
@ -482,7 +541,7 @@ class ArticlesGenerator(CachingGenerator):
|
|||
write(draft.save_as, self.get_template(draft.template),
|
||||
self.context, article=draft, category=draft.category,
|
||||
override_output=hasattr(draft, 'override_save_as'),
|
||||
blog=True, all_articles=self.articles)
|
||||
blog=True, all_articles=self.articles, url=draft.url)
|
||||
|
||||
def generate_pages(self, writer):
|
||||
"""Generate the pages on the disk"""
|
||||
|
|
@ -538,11 +597,14 @@ class ArticlesGenerator(CachingGenerator):
|
|||
all_drafts.append(article)
|
||||
self.add_source_path(article)
|
||||
|
||||
self.articles, self.translations = process_translations(
|
||||
all_articles,
|
||||
order_by=self.settings['ARTICLE_ORDER_BY'])
|
||||
self.drafts, self.drafts_translations = \
|
||||
process_translations(all_drafts)
|
||||
def _process(arts):
|
||||
origs, translations = process_translations(
|
||||
arts, translation_id=self.settings['ARTICLE_TRANSLATION_ID'])
|
||||
origs = order_content(origs, self.settings['ARTICLE_ORDER_BY'])
|
||||
return origs, translations
|
||||
|
||||
self.articles, self.translations = _process(all_articles)
|
||||
self.drafts, self.drafts_translations = _process(all_drafts)
|
||||
|
||||
signals.article_generator_pretaxonomy.send(self)
|
||||
|
||||
|
|
@ -581,20 +643,32 @@ class ArticlesGenerator(CachingGenerator):
|
|||
self.generate_pages(writer)
|
||||
signals.article_writer_finalized.send(self, writer=writer)
|
||||
|
||||
def refresh_metadata_intersite_links(self):
|
||||
for e in chain(self.articles,
|
||||
self.translations,
|
||||
self.drafts,
|
||||
self.drafts_translations):
|
||||
if hasattr(e, 'refresh_metadata_intersite_links'):
|
||||
e.refresh_metadata_intersite_links()
|
||||
|
||||
|
||||
class PagesGenerator(CachingGenerator):
|
||||
"""Generate pages"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.pages = []
|
||||
self.translations = []
|
||||
self.hidden_pages = []
|
||||
self.hidden_translations = []
|
||||
self.draft_pages = []
|
||||
self.draft_translations = []
|
||||
super(PagesGenerator, self).__init__(*args, **kwargs)
|
||||
signals.page_generator_init.send(self)
|
||||
|
||||
def generate_context(self):
|
||||
all_pages = []
|
||||
hidden_pages = []
|
||||
draft_pages = []
|
||||
for f in self.get_files(
|
||||
self.settings['PAGE_PATHS'],
|
||||
exclude=self.settings['PAGE_EXCLUDES']):
|
||||
|
|
@ -625,15 +699,21 @@ class PagesGenerator(CachingGenerator):
|
|||
all_pages.append(page)
|
||||
elif page.status == "hidden":
|
||||
hidden_pages.append(page)
|
||||
elif page.status == "draft":
|
||||
draft_pages.append(page)
|
||||
self.add_source_path(page)
|
||||
|
||||
self.pages, self.translations = process_translations(
|
||||
all_pages,
|
||||
order_by=self.settings['PAGE_ORDER_BY'])
|
||||
self.hidden_pages, self.hidden_translations = \
|
||||
process_translations(hidden_pages)
|
||||
def _process(pages):
|
||||
origs, translations = process_translations(
|
||||
pages, translation_id=self.settings['PAGE_TRANSLATION_ID'])
|
||||
origs = order_content(origs, self.settings['PAGE_ORDER_BY'])
|
||||
return origs, translations
|
||||
|
||||
self._update_context(('pages', 'hidden_pages'))
|
||||
self.pages, self.translations = _process(all_pages)
|
||||
self.hidden_pages, self.hidden_translations = _process(hidden_pages)
|
||||
self.draft_pages, self.draft_translations = _process(draft_pages)
|
||||
|
||||
self._update_context(('pages', 'hidden_pages', 'draft_pages'))
|
||||
|
||||
self.save_cache()
|
||||
self.readers.save_cache()
|
||||
|
|
@ -641,14 +721,26 @@ class PagesGenerator(CachingGenerator):
|
|||
|
||||
def generate_output(self, writer):
|
||||
for page in chain(self.translations, self.pages,
|
||||
self.hidden_translations, self.hidden_pages):
|
||||
self.hidden_translations, self.hidden_pages,
|
||||
self.draft_translations, self.draft_pages):
|
||||
signals.page_generator_write_page.send(self, content=page)
|
||||
writer.write_file(
|
||||
page.save_as, self.get_template(page.template),
|
||||
self.context, page=page,
|
||||
relative_urls=self.settings['RELATIVE_URLS'],
|
||||
override_output=hasattr(page, 'override_save_as'))
|
||||
override_output=hasattr(page, 'override_save_as'),
|
||||
url=page.url)
|
||||
signals.page_writer_finalized.send(self, writer=writer)
|
||||
|
||||
def refresh_metadata_intersite_links(self):
|
||||
for e in chain(self.pages,
|
||||
self.hidden_pages,
|
||||
self.hidden_translations,
|
||||
self.draft_pages,
|
||||
self.draft_translations):
|
||||
if hasattr(e, 'refresh_metadata_intersite_links'):
|
||||
e.refresh_metadata_intersite_links()
|
||||
|
||||
|
||||
class StaticGenerator(Generator):
|
||||
"""copy static paths (what you want to copy, like images, medias etc.
|
||||
|
|
@ -696,14 +788,21 @@ class StaticGenerator(Generator):
|
|||
final_path=None):
|
||||
"""Copy all the paths from source to destination"""
|
||||
for path in paths:
|
||||
source_path = os.path.join(source, path)
|
||||
|
||||
if final_path:
|
||||
copy(os.path.join(source, path),
|
||||
os.path.join(output_path, destination, final_path),
|
||||
self.settings['IGNORE_FILES'])
|
||||
if os.path.isfile(source_path):
|
||||
destination_path = os.path.join(output_path, destination,
|
||||
final_path,
|
||||
os.path.basename(path))
|
||||
else:
|
||||
destination_path = os.path.join(output_path, destination,
|
||||
final_path)
|
||||
else:
|
||||
copy(os.path.join(source, path),
|
||||
os.path.join(output_path, destination, path),
|
||||
self.settings['IGNORE_FILES'])
|
||||
destination_path = os.path.join(output_path, destination, path)
|
||||
|
||||
copy(source_path, destination_path,
|
||||
self.settings['IGNORE_FILES'])
|
||||
|
||||
def _file_update_required(self, staticfile):
|
||||
source_path = os.path.join(self.path, staticfile.source_path)
|
||||
|
|
@ -726,7 +825,7 @@ class StaticGenerator(Generator):
|
|||
save_as = os.path.join(self.output_path, staticfile.save_as)
|
||||
s_mtime = os.path.getmtime(source_path)
|
||||
d_mtime = os.path.getmtime(save_as)
|
||||
return s_mtime > d_mtime
|
||||
return s_mtime - d_mtime > 0.000001
|
||||
|
||||
def _link_or_copy_staticfile(self, sc):
|
||||
if self.settings['STATIC_CREATE_LINKS']:
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ class LimitFilter(logging.Filter):
|
|||
E.g.: log.warning(('43 is not the answer', 'More erroneous answers'))
|
||||
"""
|
||||
|
||||
LOGS_DEDUP_MIN_LEVEL = logging.WARNING
|
||||
|
||||
_ignore = set()
|
||||
_raised_messages = set()
|
||||
_threshold = 5
|
||||
|
|
@ -98,7 +100,7 @@ class LimitFilter(logging.Filter):
|
|||
|
||||
def filter(self, record):
|
||||
# don't limit log messages for anything above "warning"
|
||||
if record.levelno > logging.WARN:
|
||||
if record.levelno > self.LOGS_DEDUP_MIN_LEVEL:
|
||||
return True
|
||||
|
||||
# extract group
|
||||
|
|
@ -226,7 +228,8 @@ def get_formatter():
|
|||
return TextFormatter()
|
||||
|
||||
|
||||
def init(level=None, fatal='', handler=logging.StreamHandler(), name=None):
|
||||
def init(level=None, fatal='', handler=logging.StreamHandler(), name=None,
|
||||
logs_dedup_min_level=None):
|
||||
FatalLogger.warnings_fatal = fatal.startswith('warning')
|
||||
FatalLogger.errors_fatal = bool(fatal)
|
||||
|
||||
|
|
@ -237,6 +240,8 @@ def init(level=None, fatal='', handler=logging.StreamHandler(), name=None):
|
|||
|
||||
if level:
|
||||
logger.setLevel(level)
|
||||
if logs_dedup_min_level:
|
||||
LimitFilter.LOGS_DEDUP_MIN_LEVEL = logs_dedup_min_level
|
||||
|
||||
|
||||
def log_warnings():
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ PaginationRule = namedtuple(
|
|||
|
||||
|
||||
class Paginator(object):
|
||||
def __init__(self, name, object_list, settings):
|
||||
def __init__(self, name, url, object_list, settings, per_page=None):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.object_list = object_list
|
||||
self.settings = settings
|
||||
|
||||
if settings.get('DEFAULT_PAGINATION'):
|
||||
self.per_page = settings.get('DEFAULT_PAGINATION')
|
||||
self.orphans = settings.get('DEFAULT_ORPHANS')
|
||||
if per_page:
|
||||
self.per_page = per_page
|
||||
self.orphans = settings['DEFAULT_ORPHANS']
|
||||
else:
|
||||
self.per_page = len(object_list)
|
||||
self.orphans = 0
|
||||
|
|
@ -37,8 +37,8 @@ class Paginator(object):
|
|||
top = bottom + self.per_page
|
||||
if top + self.orphans >= self.count:
|
||||
top = self.count
|
||||
return Page(self.name, self.object_list[bottom:top], number, self,
|
||||
self.settings)
|
||||
return Page(self.name, self.url, self.object_list[bottom:top], number,
|
||||
self, self.settings)
|
||||
|
||||
def _get_count(self):
|
||||
"Returns the total number of objects, across all pages."
|
||||
|
|
@ -65,8 +65,12 @@ class Paginator(object):
|
|||
|
||||
|
||||
class Page(object):
|
||||
def __init__(self, name, object_list, number, paginator, settings):
|
||||
def __init__(self, name, url, object_list, number, paginator, settings):
|
||||
self.full_name = name
|
||||
self.name, self.extension = os.path.splitext(name)
|
||||
dn, fn = os.path.split(name)
|
||||
self.base_name = dn if fn in ('index.htm', 'index.html') else self.name
|
||||
self.base_url = url
|
||||
self.object_list = object_list
|
||||
self.number = number
|
||||
self.paginator = paginator
|
||||
|
|
@ -133,24 +137,16 @@ class Page(object):
|
|||
|
||||
# URL or SAVE_AS is a string, format it with a controlled context
|
||||
context = {
|
||||
'name': self.name.replace(os.sep, '/'),
|
||||
'object_list': self.object_list,
|
||||
'number': self.number,
|
||||
'paginator': self.paginator,
|
||||
'settings': self.settings,
|
||||
'base_name': os.path.dirname(self.name),
|
||||
'number_sep': '/',
|
||||
'save_as': self.full_name,
|
||||
'url': self.base_url,
|
||||
'name': self.name,
|
||||
'base_name': self.base_name,
|
||||
'extension': self.extension,
|
||||
'number': self.number,
|
||||
}
|
||||
|
||||
if self.number == 1:
|
||||
# no page numbers on the first page
|
||||
context['number'] = ''
|
||||
context['number_sep'] = ''
|
||||
|
||||
ret = prop_value.format(**context)
|
||||
if ret[0] == '/':
|
||||
ret = ret[1:]
|
||||
ret = ret.lstrip('/')
|
||||
return ret
|
||||
|
||||
url = property(functools.partial(_from_settings, key='URL'))
|
||||
|
|
|
|||
|
|
@ -31,6 +31,20 @@ except ImportError:
|
|||
# This means that _filter_discardable_metadata() must be called on processed
|
||||
# metadata dicts before use, to remove the items with the special value.
|
||||
_DISCARD = object()
|
||||
|
||||
DUPLICATES_DEFINITIONS_ALLOWED = {
|
||||
'tags': False,
|
||||
'date': False,
|
||||
'modified': False,
|
||||
'status': False,
|
||||
'category': False,
|
||||
'author': False,
|
||||
'save_as': False,
|
||||
'url': False,
|
||||
'authors': False,
|
||||
'slug': False
|
||||
}
|
||||
|
||||
METADATA_PROCESSORS = {
|
||||
'tags': lambda x, y: ([
|
||||
Tag(tag, y)
|
||||
|
|
@ -203,11 +217,18 @@ class RstReader(BaseReader):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(RstReader, self).__init__(*args, **kwargs)
|
||||
|
||||
def _parse_metadata(self, document):
|
||||
def _parse_metadata(self, document, source_path):
|
||||
"""Return the dict containing document metadata"""
|
||||
formatted_fields = self.settings['FORMATTED_FIELDS']
|
||||
|
||||
output = {}
|
||||
|
||||
if document.first_child_matching_class(docutils.nodes.title) is None:
|
||||
logger.warning(
|
||||
'Document title missing in file %s: '
|
||||
'Ensure exactly one top level section',
|
||||
source_path)
|
||||
|
||||
for docinfo in document.traverse(docutils.nodes.docinfo):
|
||||
for element in docinfo.children:
|
||||
if element.tagname == 'field': # custom fields (e.g. summary)
|
||||
|
|
@ -234,6 +255,7 @@ class RstReader(BaseReader):
|
|||
extra_params = {'initial_header_level': '2',
|
||||
'syntax_highlight': 'short',
|
||||
'input_encoding': 'utf-8',
|
||||
'language_code': self.settings.get('DEFAULT_LANG'),
|
||||
'exit_status_level': 2,
|
||||
'embed_stylesheet': False}
|
||||
user_params = self.settings.get('DOCUTILS_SETTINGS')
|
||||
|
|
@ -256,7 +278,7 @@ class RstReader(BaseReader):
|
|||
parts = pub.writer.parts
|
||||
content = parts.get('body')
|
||||
|
||||
metadata = self._parse_metadata(pub.document)
|
||||
metadata = self._parse_metadata(pub.document, source_path)
|
||||
metadata.setdefault('title', parts.get('title'))
|
||||
|
||||
return content, metadata
|
||||
|
|
@ -294,7 +316,7 @@ class MarkdownReader(BaseReader):
|
|||
self._md.reset()
|
||||
formatted = self._md.convert(formatted_values)
|
||||
output[name] = self.process_metadata(name, formatted)
|
||||
elif name in METADATA_PROCESSORS:
|
||||
elif not DUPLICATES_DEFINITIONS_ALLOWED.get(name, True):
|
||||
if len(value) > 1:
|
||||
logger.warning(
|
||||
'Duplicate definition of `%s` '
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import posixpath
|
||||
import ssl
|
||||
import sys
|
||||
|
||||
try:
|
||||
|
|
@ -10,37 +13,81 @@ try:
|
|||
except ImportError:
|
||||
magic_from_file = None
|
||||
|
||||
from six.moves import BaseHTTPServer
|
||||
from six.moves import SimpleHTTPServer as srvmod
|
||||
from six.moves import socketserver
|
||||
from six.moves import urllib
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Pelican Development Server',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument("port", default=8000, type=int, nargs="?",
|
||||
help="Port to Listen On")
|
||||
parser.add_argument("server", default="", nargs="?",
|
||||
help="Interface to Listen On")
|
||||
parser.add_argument('--ssl', action="store_true",
|
||||
help='Activate SSL listener')
|
||||
parser.add_argument('--cert', default="./cert.pem", nargs="?",
|
||||
help='Path to certificate file. ' +
|
||||
'Relative to current directory')
|
||||
parser.add_argument('--key', default="./key.pem", nargs="?",
|
||||
help='Path to certificate key file. ' +
|
||||
'Relative to current directory')
|
||||
parser.add_argument('--path', default=".",
|
||||
help='Path to pelican source directory to serve. ' +
|
||||
'Relative to current directory')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler):
|
||||
SUFFIXES = ['', '.html', '/index.html']
|
||||
SUFFIXES = ['.html', '/index.html', '/', '']
|
||||
|
||||
def translate_path(self, path):
|
||||
# abandon query parameters
|
||||
path = path.split('?', 1)[0]
|
||||
path = path.split('#', 1)[0]
|
||||
# Don't forget explicit trailing slash when normalizing. Issue17324
|
||||
trailing_slash = path.rstrip().endswith('/')
|
||||
path = urllib.parse.unquote(path)
|
||||
path = posixpath.normpath(path)
|
||||
words = path.split('/')
|
||||
words = filter(None, words)
|
||||
path = self.base_path
|
||||
for word in words:
|
||||
if os.path.dirname(word) or word in (os.curdir, os.pardir):
|
||||
# Ignore components that are not a simple file/directory name
|
||||
continue
|
||||
path = os.path.join(path, word)
|
||||
if trailing_slash:
|
||||
path += '/'
|
||||
return path
|
||||
|
||||
def do_GET(self):
|
||||
# cut off a query string
|
||||
if '?' in self.path:
|
||||
self.path, _ = self.path.split('?', 1)
|
||||
original_path = self.path.split('?', 1)[0]
|
||||
# try to find file
|
||||
self.path = self.get_path_that_exists(original_path)
|
||||
|
||||
if not self.path:
|
||||
logging.warning("Unable to find `%s` or variations.",
|
||||
original_path)
|
||||
return
|
||||
|
||||
logging.info("Found `%s`.", self.path)
|
||||
srvmod.SimpleHTTPRequestHandler.do_GET(self)
|
||||
|
||||
def get_path_that_exists(self, original_path):
|
||||
# Try to strip trailing slash
|
||||
original_path = original_path.rstrip('/')
|
||||
# Try to detect file by applying various suffixes
|
||||
for suffix in self.SUFFIXES:
|
||||
if not hasattr(self, 'original_path'):
|
||||
self.original_path = self.path
|
||||
|
||||
self.path = self.original_path + suffix
|
||||
path = self.translate_path(self.path)
|
||||
|
||||
if os.path.exists(path):
|
||||
srvmod.SimpleHTTPRequestHandler.do_GET(self)
|
||||
logging.info("Found `%s`." % self.path)
|
||||
break
|
||||
|
||||
logging.info("Tried to find `%s`, but it doesn't exist.",
|
||||
self.path)
|
||||
else:
|
||||
# Fallback if there were no matches
|
||||
logging.warning("Unable to find `%s` or variations.",
|
||||
self.original_path)
|
||||
path = original_path + suffix
|
||||
if os.path.exists(self.translate_path(path)):
|
||||
return path
|
||||
logging.info("Tried to find `%s`, but it doesn't exist.", path)
|
||||
return None
|
||||
|
||||
def guess_type(self, path):
|
||||
"""Guess at the mime type for the specified file.
|
||||
|
|
@ -54,19 +101,36 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler):
|
|||
return mimetype
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
PORT = len(sys.argv) in (2, 3) and int(sys.argv[1]) or 8000
|
||||
SERVER = len(sys.argv) == 3 and sys.argv[2] or ""
|
||||
class RootedHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
def __init__(self, base_path, *args, **kwargs):
|
||||
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
|
||||
self.RequestHandlerClass.base_path = base_path
|
||||
|
||||
socketserver.TCPServer.allow_reuse_address = True
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.warning("'python -m pelican.server' is deprecated. The "
|
||||
"Pelican development server should be run via "
|
||||
"'pelican --listen' or 'pelican -l' (this can be combined "
|
||||
"with regeneration as 'pelican -lr'). Rerun 'pelican-"
|
||||
"quickstart' to get new Makefile and tasks.py files.")
|
||||
args = parse_arguments()
|
||||
RootedHTTPServer.allow_reuse_address = True
|
||||
try:
|
||||
httpd = socketserver.TCPServer(
|
||||
(SERVER, PORT), ComplexHTTPRequestHandler)
|
||||
except OSError as e:
|
||||
logging.error("Could not listen on port %s, server %s.", PORT, SERVER)
|
||||
httpd = RootedHTTPServer(
|
||||
args.path, (args.server, args.port), ComplexHTTPRequestHandler)
|
||||
if args.ssl:
|
||||
httpd.socket = ssl.wrap_socket(
|
||||
httpd.socket, keyfile=args.key,
|
||||
certfile=args.cert, server_side=True)
|
||||
except ssl.SSLError as e:
|
||||
logging.error("Couldn't open certificate file %s or key file %s",
|
||||
args.cert, args.key)
|
||||
logging.error("Could not listen on port %s, server %s.",
|
||||
args.port, args.server)
|
||||
sys.exit(getattr(e, 'exitcode', 1))
|
||||
|
||||
logging.info("Serving at port %s, server %s.", PORT, SERVER)
|
||||
logging.info("Serving at port %s, server %s.",
|
||||
args.port, args.server)
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt as e:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import inspect
|
|||
import locale
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from os.path import isabs
|
||||
from posixpath import join as posix_join
|
||||
|
||||
|
|
@ -80,6 +81,11 @@ DEFAULT_CONFIG = {
|
|||
'PAGE_ORDER_BY': 'basename',
|
||||
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
|
||||
'PAGE_LANG_SAVE_AS': posix_join('pages', '{slug}-{lang}.html'),
|
||||
'DRAFT_PAGE_URL': 'drafts/pages/{slug}.html',
|
||||
'DRAFT_PAGE_SAVE_AS': posix_join('drafts', 'pages', '{slug}.html'),
|
||||
'DRAFT_PAGE_LANG_URL': 'drafts/pages/{slug}-{lang}.html',
|
||||
'DRAFT_PAGE_LANG_SAVE_AS': posix_join('drafts', 'pages',
|
||||
'{slug}-{lang}.html'),
|
||||
'STATIC_URL': '{path}',
|
||||
'STATIC_SAVE_AS': '{path}',
|
||||
'STATIC_CREATE_LINKS': False,
|
||||
|
|
@ -91,16 +97,23 @@ DEFAULT_CONFIG = {
|
|||
'AUTHOR_URL': 'author/{slug}.html',
|
||||
'AUTHOR_SAVE_AS': posix_join('author', '{slug}.html'),
|
||||
'PAGINATION_PATTERNS': [
|
||||
(0, '{name}{number}{extension}', '{name}{number}{extension}'),
|
||||
(1, '{name}{extension}', '{name}{extension}'),
|
||||
(2, '{name}{number}{extension}', '{name}{number}{extension}'),
|
||||
],
|
||||
'YEAR_ARCHIVE_URL': '',
|
||||
'YEAR_ARCHIVE_SAVE_AS': '',
|
||||
'MONTH_ARCHIVE_URL': '',
|
||||
'MONTH_ARCHIVE_SAVE_AS': '',
|
||||
'DAY_ARCHIVE_URL': '',
|
||||
'DAY_ARCHIVE_SAVE_AS': '',
|
||||
'RELATIVE_URLS': False,
|
||||
'DEFAULT_LANG': 'en',
|
||||
'ARTICLE_TRANSLATION_ID': 'slug',
|
||||
'PAGE_TRANSLATION_ID': 'slug',
|
||||
'DIRECT_TEMPLATES': ['index', 'tags', 'categories', 'authors', 'archives'],
|
||||
'EXTRA_TEMPLATES_PATHS': [],
|
||||
'PAGINATED_DIRECT_TEMPLATES': ['index'],
|
||||
'THEME_TEMPLATES_OVERRIDES': [],
|
||||
'PAGINATED_TEMPLATES': {'index': None, 'tag': None, 'category': None,
|
||||
'author': None},
|
||||
'PELICAN_CLASS': 'pelican.Pelican',
|
||||
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
|
||||
'DATE_FORMATS': {},
|
||||
|
|
@ -134,8 +147,14 @@ DEFAULT_CONFIG = {
|
|||
'PLUGINS': [],
|
||||
'PYGMENTS_RST_OPTIONS': {},
|
||||
'TEMPLATE_PAGES': {},
|
||||
'TEMPLATE_EXTENSIONS': ['.html'],
|
||||
'IGNORE_FILES': ['.#*'],
|
||||
'SLUG_SUBSTITUTIONS': (),
|
||||
'SLUG_REGEX_SUBSTITUTIONS': [
|
||||
(r'[^\w\s-]', ''), # remove non-alphabetical/whitespace/'-' chars
|
||||
(r'(?u)\A\s*', ''), # strip leading whitespace
|
||||
(r'(?u)\s*\Z', ''), # strip trailing whitespace
|
||||
(r'[-\s]+', '-'), # reduce multiple whitespace or '-' to single '-'
|
||||
],
|
||||
'INTRASITE_LINK_REGEX': '[{|](?P<what>.*?)[|}]',
|
||||
'SLUGIFY_SOURCE': 'title',
|
||||
'CACHE_CONTENT': False,
|
||||
|
|
@ -146,85 +165,70 @@ DEFAULT_CONFIG = {
|
|||
'LOAD_CONTENT_CACHE': False,
|
||||
'WRITE_SELECTED': [],
|
||||
'FORMATTED_FIELDS': ['summary'],
|
||||
'PORT': 8000,
|
||||
'BIND': '',
|
||||
}
|
||||
|
||||
PYGMENTS_RST_OPTIONS = None
|
||||
|
||||
|
||||
def read_settings(path=None, override=None):
|
||||
settings = override or {}
|
||||
|
||||
if path:
|
||||
local_settings = get_settings_from_file(path)
|
||||
# Make the paths relative to the settings file
|
||||
settings = dict(get_settings_from_file(path), **settings)
|
||||
|
||||
if settings:
|
||||
settings = handle_deprecated_settings(settings)
|
||||
|
||||
if path:
|
||||
# Make relative paths absolute
|
||||
def getabs(maybe_relative, base_path=path):
|
||||
if isabs(maybe_relative):
|
||||
return maybe_relative
|
||||
return os.path.abspath(os.path.normpath(os.path.join(
|
||||
os.path.dirname(base_path), maybe_relative)))
|
||||
|
||||
for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'CACHE_PATH']:
|
||||
if p in local_settings and local_settings[p] is not None \
|
||||
and not isabs(local_settings[p]):
|
||||
absp = os.path.abspath(os.path.normpath(os.path.join(
|
||||
os.path.dirname(path), local_settings[p])))
|
||||
if settings.get(p) is not None:
|
||||
absp = getabs(settings[p])
|
||||
# THEME may be a name rather than a path
|
||||
if p != 'THEME' or os.path.exists(absp):
|
||||
local_settings[p] = absp
|
||||
settings[p] = absp
|
||||
|
||||
if 'PLUGIN_PATH' in local_settings:
|
||||
logger.warning('PLUGIN_PATH setting has been replaced by '
|
||||
'PLUGIN_PATHS, moving it to the new setting name.')
|
||||
local_settings['PLUGIN_PATHS'] = local_settings['PLUGIN_PATH']
|
||||
del local_settings['PLUGIN_PATH']
|
||||
if 'JINJA_EXTENSIONS' in local_settings:
|
||||
logger.warning('JINJA_EXTENSIONS setting has been deprecated, '
|
||||
'moving it to JINJA_ENVIRONMENT setting.')
|
||||
local_settings['JINJA_ENVIRONMENT']['extensions'] = \
|
||||
local_settings['JINJA_EXTENSIONS']
|
||||
del local_settings['JINJA_EXTENSIONS']
|
||||
if isinstance(local_settings['PLUGIN_PATHS'], six.string_types):
|
||||
logger.warning("Defining PLUGIN_PATHS setting as string "
|
||||
"has been deprecated (should be a list)")
|
||||
local_settings['PLUGIN_PATHS'] = [local_settings['PLUGIN_PATHS']]
|
||||
elif local_settings['PLUGIN_PATHS'] is not None:
|
||||
def getabs(path, pluginpath):
|
||||
if isabs(pluginpath):
|
||||
return pluginpath
|
||||
else:
|
||||
path_dirname = os.path.dirname(path)
|
||||
path_joined = os.path.join(path_dirname, pluginpath)
|
||||
path_normed = os.path.normpath(path_joined)
|
||||
path_absolute = os.path.abspath(path_normed)
|
||||
return path_absolute
|
||||
if settings.get('PLUGIN_PATHS') is not None:
|
||||
settings['PLUGIN_PATHS'] = [getabs(pluginpath)
|
||||
for pluginpath
|
||||
in settings['PLUGIN_PATHS']]
|
||||
|
||||
pluginpath_list = [getabs(path, pluginpath)
|
||||
for pluginpath
|
||||
in local_settings['PLUGIN_PATHS']]
|
||||
local_settings['PLUGIN_PATHS'] = pluginpath_list
|
||||
else:
|
||||
local_settings = copy.deepcopy(DEFAULT_CONFIG)
|
||||
settings = dict(copy.deepcopy(DEFAULT_CONFIG), **settings)
|
||||
settings = configure_settings(settings)
|
||||
|
||||
if override:
|
||||
local_settings.update(override)
|
||||
|
||||
parsed_settings = configure_settings(local_settings)
|
||||
# This is because there doesn't seem to be a way to pass extra
|
||||
# parameters to docutils directive handlers, so we have to have a
|
||||
# variable here that we'll import from within Pygments.run (see
|
||||
# rstdirectives.py) to see what the user defaults were.
|
||||
global PYGMENTS_RST_OPTIONS
|
||||
PYGMENTS_RST_OPTIONS = parsed_settings.get('PYGMENTS_RST_OPTIONS', None)
|
||||
return parsed_settings
|
||||
PYGMENTS_RST_OPTIONS = settings.get('PYGMENTS_RST_OPTIONS', None)
|
||||
return settings
|
||||
|
||||
|
||||
def get_settings_from_module(module=None, default_settings=DEFAULT_CONFIG):
|
||||
def get_settings_from_module(module=None):
|
||||
"""Loads settings from a module, returns a dictionary."""
|
||||
|
||||
context = copy.deepcopy(default_settings)
|
||||
context = {}
|
||||
if module is not None:
|
||||
context.update(
|
||||
(k, v) for k, v in inspect.getmembers(module) if k.isupper())
|
||||
return context
|
||||
|
||||
|
||||
def get_settings_from_file(path, default_settings=DEFAULT_CONFIG):
|
||||
def get_settings_from_file(path):
|
||||
"""Loads settings from a file path, returning a dict."""
|
||||
|
||||
name, ext = os.path.splitext(os.path.basename(path))
|
||||
module = load_source(name, path)
|
||||
return get_settings_from_module(module, default_settings=default_settings)
|
||||
return get_settings_from_module(module)
|
||||
|
||||
|
||||
def get_jinja_environment(settings):
|
||||
|
|
@ -241,6 +245,149 @@ def get_jinja_environment(settings):
|
|||
return settings
|
||||
|
||||
|
||||
def handle_deprecated_settings(settings):
|
||||
"""Converts deprecated settings and issues warnings. Issues an exception
|
||||
if both old and new setting is specified.
|
||||
"""
|
||||
|
||||
# PLUGIN_PATH -> PLUGIN_PATHS
|
||||
if 'PLUGIN_PATH' in settings:
|
||||
logger.warning('PLUGIN_PATH setting has been replaced by '
|
||||
'PLUGIN_PATHS, moving it to the new setting name.')
|
||||
settings['PLUGIN_PATHS'] = settings['PLUGIN_PATH']
|
||||
del settings['PLUGIN_PATH']
|
||||
|
||||
# PLUGIN_PATHS: str -> [str]
|
||||
if isinstance(settings.get('PLUGIN_PATHS'), six.string_types):
|
||||
logger.warning("Defining PLUGIN_PATHS setting as string "
|
||||
"has been deprecated (should be a list)")
|
||||
settings['PLUGIN_PATHS'] = [settings['PLUGIN_PATHS']]
|
||||
|
||||
# JINJA_EXTENSIONS -> JINJA_ENVIRONMENT > extensions
|
||||
if 'JINJA_EXTENSIONS' in settings:
|
||||
logger.warning('JINJA_EXTENSIONS setting has been deprecated, '
|
||||
'moving it to JINJA_ENVIRONMENT setting.')
|
||||
settings['JINJA_ENVIRONMENT']['extensions'] = \
|
||||
settings['JINJA_EXTENSIONS']
|
||||
del settings['JINJA_EXTENSIONS']
|
||||
|
||||
# {ARTICLE,PAGE}_DIR -> {ARTICLE,PAGE}_PATHS
|
||||
for key in ['ARTICLE', 'PAGE']:
|
||||
old_key = key + '_DIR'
|
||||
new_key = key + '_PATHS'
|
||||
if old_key in settings:
|
||||
logger.warning(
|
||||
'Deprecated setting %s, moving it to %s list',
|
||||
old_key, new_key)
|
||||
settings[new_key] = [settings[old_key]] # also make a list
|
||||
del settings[old_key]
|
||||
|
||||
# EXTRA_TEMPLATES_PATHS -> THEME_TEMPLATES_OVERRIDES
|
||||
if 'EXTRA_TEMPLATES_PATHS' in settings:
|
||||
logger.warning('EXTRA_TEMPLATES_PATHS is deprecated use '
|
||||
'THEME_TEMPLATES_OVERRIDES instead.')
|
||||
if ('THEME_TEMPLATES_OVERRIDES' in settings and
|
||||
settings['THEME_TEMPLATES_OVERRIDES']):
|
||||
raise Exception(
|
||||
'Setting both EXTRA_TEMPLATES_PATHS and '
|
||||
'THEME_TEMPLATES_OVERRIDES is not permitted. Please move to '
|
||||
'only setting THEME_TEMPLATES_OVERRIDES.')
|
||||
settings['THEME_TEMPLATES_OVERRIDES'] = \
|
||||
settings['EXTRA_TEMPLATES_PATHS']
|
||||
del settings['EXTRA_TEMPLATES_PATHS']
|
||||
|
||||
# MD_EXTENSIONS -> MARKDOWN
|
||||
if 'MD_EXTENSIONS' in settings:
|
||||
logger.warning('MD_EXTENSIONS is deprecated use MARKDOWN '
|
||||
'instead. Falling back to the default.')
|
||||
settings['MARKDOWN'] = DEFAULT_CONFIG['MARKDOWN']
|
||||
|
||||
# LESS_GENERATOR -> Webassets plugin
|
||||
# FILES_TO_COPY -> STATIC_PATHS, EXTRA_PATH_METADATA
|
||||
for old, new, doc in [
|
||||
('LESS_GENERATOR', 'the Webassets plugin', None),
|
||||
('FILES_TO_COPY', 'STATIC_PATHS and EXTRA_PATH_METADATA',
|
||||
'https://github.com/getpelican/pelican/'
|
||||
'blob/master/docs/settings.rst#path-metadata'),
|
||||
]:
|
||||
if old in settings:
|
||||
message = 'The {} setting has been removed in favor of {}'.format(
|
||||
old, new)
|
||||
if doc:
|
||||
message += ', see {} for details'.format(doc)
|
||||
logger.warning(message)
|
||||
|
||||
# PAGINATED_DIRECT_TEMPLATES -> PAGINATED_TEMPLATES
|
||||
if 'PAGINATED_DIRECT_TEMPLATES' in settings:
|
||||
message = 'The {} setting has been removed in favor of {}'.format(
|
||||
'PAGINATED_DIRECT_TEMPLATES', 'PAGINATED_TEMPLATES')
|
||||
logger.warning(message)
|
||||
|
||||
for t in settings['PAGINATED_DIRECT_TEMPLATES']:
|
||||
if t not in settings['PAGINATED_TEMPLATES']:
|
||||
settings['PAGINATED_TEMPLATES'][t] = None
|
||||
del settings['PAGINATED_DIRECT_TEMPLATES']
|
||||
|
||||
# {SLUG,CATEGORY,TAG,AUTHOR}_SUBSTITUTIONS ->
|
||||
# {SLUG,CATEGORY,TAG,AUTHOR}_REGEX_SUBSTITUTIONS
|
||||
url_settings_url = \
|
||||
'http://docs.getpelican.com/en/latest/settings.html#url-settings'
|
||||
flavours = {'SLUG', 'CATEGORY', 'TAG', 'AUTHOR'}
|
||||
old_values = {f: settings[f + '_SUBSTITUTIONS']
|
||||
for f in flavours if f + '_SUBSTITUTIONS' in settings}
|
||||
new_values = {f: settings[f + '_REGEX_SUBSTITUTIONS']
|
||||
for f in flavours if f + '_REGEX_SUBSTITUTIONS' in settings}
|
||||
if old_values and new_values:
|
||||
raise Exception(
|
||||
'Setting both {new_key} and {old_key} (or variants thereof) is '
|
||||
'not permitted. Please move to only setting {new_key}.'
|
||||
.format(old_key='SLUG_SUBSTITUTIONS',
|
||||
new_key='SLUG_REGEX_SUBSTITUTIONS'))
|
||||
if old_values:
|
||||
message = ('{} and variants thereof are deprecated and will be '
|
||||
'removed in the future. Please use {} and variants thereof '
|
||||
'instead. Check {}.'
|
||||
.format('SLUG_SUBSTITUTIONS', 'SLUG_REGEX_SUBSTITUTIONS',
|
||||
url_settings_url))
|
||||
logger.warning(message)
|
||||
if old_values.get('SLUG'):
|
||||
for f in {'CATEGORY', 'TAG'}:
|
||||
if old_values.get(f):
|
||||
old_values[f] = old_values['SLUG'] + old_values[f]
|
||||
old_values['AUTHOR'] = old_values.get('AUTHOR', [])
|
||||
for f in flavours:
|
||||
if old_values.get(f) is not None:
|
||||
regex_subs = []
|
||||
# by default will replace non-alphanum characters
|
||||
replace = True
|
||||
for tpl in old_values[f]:
|
||||
try:
|
||||
src, dst, skip = tpl
|
||||
if skip:
|
||||
replace = False
|
||||
except ValueError:
|
||||
src, dst = tpl
|
||||
regex_subs.append(
|
||||
(re.escape(src), dst.replace('\\', r'\\')))
|
||||
|
||||
if replace:
|
||||
regex_subs += [
|
||||
(r'[^\w\s-]', ''),
|
||||
(r'(?u)\A\s*', ''),
|
||||
(r'(?u)\s*\Z', ''),
|
||||
(r'[-\s]+', '-'),
|
||||
]
|
||||
else:
|
||||
regex_subs += [
|
||||
(r'(?u)\A\s*', ''),
|
||||
(r'(?u)\s*\Z', ''),
|
||||
]
|
||||
settings[f + '_REGEX_SUBSTITUTIONS'] = regex_subs
|
||||
settings.pop(f + '_SUBSTITUTIONS', None)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def configure_settings(settings):
|
||||
"""Provide optimizations, error checking, and warnings for the given
|
||||
settings.
|
||||
|
|
@ -365,23 +512,12 @@ def configure_settings(settings):
|
|||
key=lambda r: r[0],
|
||||
)
|
||||
|
||||
# move {ARTICLE,PAGE}_DIR -> {ARTICLE,PAGE}_PATHS
|
||||
for key in ['ARTICLE', 'PAGE']:
|
||||
old_key = key + '_DIR'
|
||||
new_key = key + '_PATHS'
|
||||
if old_key in settings:
|
||||
logger.warning(
|
||||
'Deprecated setting %s, moving it to %s list',
|
||||
old_key, new_key)
|
||||
settings[new_key] = [settings[old_key]] # also make a list
|
||||
del settings[old_key]
|
||||
|
||||
# Save people from accidentally setting a string rather than a list
|
||||
path_keys = (
|
||||
'ARTICLE_EXCLUDES',
|
||||
'DEFAULT_METADATA',
|
||||
'DIRECT_TEMPLATES',
|
||||
'EXTRA_TEMPLATES_PATHS',
|
||||
'THEME_TEMPLATES_OVERRIDES',
|
||||
'FILES_TO_COPY',
|
||||
'IGNORE_FILES',
|
||||
'PAGINATED_DIRECT_TEMPLATES',
|
||||
|
|
@ -399,12 +535,6 @@ def configure_settings(settings):
|
|||
PATH_KEY)
|
||||
settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY]
|
||||
|
||||
# Deprecated warning of MD_EXTENSIONS
|
||||
if 'MD_EXTENSIONS' in settings:
|
||||
logger.warning('MD_EXTENSIONS is deprecated use MARKDOWN '
|
||||
'instead. Falling back to the default.')
|
||||
settings['MARKDOWN'] = DEFAULT_CONFIG['MARKDOWN']
|
||||
|
||||
# Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES
|
||||
mutually_exclusive = ('ARTICLE', 'PAGE')
|
||||
for type_1, type_2 in [mutually_exclusive, mutually_exclusive[::-1]]:
|
||||
|
|
@ -417,17 +547,4 @@ def configure_settings(settings):
|
|||
except KeyError:
|
||||
continue # setting not specified, nothing to do
|
||||
|
||||
for old, new, doc in [
|
||||
('LESS_GENERATOR', 'the Webassets plugin', None),
|
||||
('FILES_TO_COPY', 'STATIC_PATHS and EXTRA_PATH_METADATA',
|
||||
'https://github.com/getpelican/pelican/'
|
||||
'blob/master/docs/settings.rst#path-metadata'),
|
||||
]:
|
||||
if old in settings:
|
||||
message = 'The {} setting has been removed in favor of {}'.format(
|
||||
old, new)
|
||||
if doc:
|
||||
message += ', see {} for details'.format(doc)
|
||||
logger.warning(message)
|
||||
|
||||
return settings
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ article_writer_finalized = signal('article_writer_finalized')
|
|||
|
||||
page_generator_init = signal('page_generator_init')
|
||||
page_generator_finalized = signal('page_generator_finalized')
|
||||
page_generator_write_page = signal('page_generator_write_page')
|
||||
page_writer_finalized = signal('page_writer_finalized')
|
||||
|
||||
static_generator_init = signal('static_generator_init')
|
||||
|
|
|
|||
8
pelican/tests/TestPages/draft_page.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
This is a test draft page
|
||||
##########################
|
||||
|
||||
:status: draft
|
||||
|
||||
The quick brown fox .
|
||||
|
||||
This page is a draft.
|
||||
12
pelican/tests/TestPages/draft_page_markdown.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
title: This is a markdown test draft page
|
||||
status: draft
|
||||
|
||||
Test Markdown File Header
|
||||
=========================
|
||||
|
||||
Used for pelican test
|
||||
---------------------
|
||||
|
||||
The quick brown fox .
|
||||
|
||||
This page is a draft
|
||||
11
pelican/tests/TestPages/draft_page_with_template.rst
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
This is a test draft page with a custom template
|
||||
#################################################
|
||||
|
||||
:status: draft
|
||||
:template: custom
|
||||
|
||||
The quick brown fox .
|
||||
|
||||
This page is a draft
|
||||
|
||||
This page has a custom template to be called when rendered
|
||||
1067
pelican/tests/content/bloggerexport.xml
vendored
Normal file
6
pelican/tests/content/wordpressexport.xml
vendored
|
|
@ -554,7 +554,11 @@ Pelicans are supposed to eat fish, damn it!
|
|||
|
||||
<iframe width="420" height="315" src="http://www.youtube.com/embed/QNNl_uWmQXE" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
Bottom line: don't mess up with birds]]></content:encoded>
|
||||
Bottom line: don't mess up with birds
|
||||
|
||||
"That's a 'wonderful' shoe."
|
||||
|
||||
“That’s a ‘magic’ sock.”]]></content:encoded>
|
||||
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
|
||||
<wp:post_id>173</wp:post_id>
|
||||
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>A Pelican Blog</title>
|
||||
<title>A Pelican Blog - Categories</title>
|
||||
<link rel="stylesheet" href="/theme/css/main.css" />
|
||||
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
|
||||
</head>
|
||||
|
|
@ -20,12 +20,13 @@
|
|||
<li><a href="/category/yeah.html">yeah</a></li>
|
||||
</ul></nav>
|
||||
</header><!-- /#banner -->
|
||||
<ul>
|
||||
<li><a href="/category/bar.html">bar</a></li>
|
||||
<li><a href="/category/cat1.html">cat1</a></li>
|
||||
<li><a href="/category/misc.html">misc</a></li>
|
||||
<li><a href="/category/yeah.html">yeah</a></li>
|
||||
</ul>
|
||||
<h1>Categories on A Pelican Blog</h1>
|
||||
<ul>
|
||||
<li><a href="/category/bar.html">bar</a> (1)</li>
|
||||
<li><a href="/category/cat1.html">cat1</a> (4)</li>
|
||||
<li><a href="/category/misc.html">misc</a> (4)</li>
|
||||
<li><a href="/category/yeah.html">yeah</a> (1)</li>
|
||||
</ul>
|
||||
<section id="extras" class="body">
|
||||
<div class="social">
|
||||
<h2>social</h2>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Deuxième article</title>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 751 B After Width: | Height: | Size: 411 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 827 B |
|
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 150 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 606 B |
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 487 B After Width: | Height: | Size: 402 B |
|
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 420 B |
|
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 840 B |
|
Before Width: | Height: | Size: 896 B After Width: | Height: | Size: 625 B |
|
Before Width: | Height: | Size: 693 B After Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 879 B After Width: | Height: | Size: 751 B |
|
Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 435 B |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 414 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 416 B |
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 316 B |
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Alexis' log</title>
|
||||
<title>Alexis' log - Categories</title>
|
||||
<link rel="stylesheet" href="./theme/css/main.css" />
|
||||
<link href="http://blog.notmyidea.org/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Alexis' log Atom Feed" />
|
||||
<link href="http://blog.notmyidea.org/feeds/all.rss.xml" type="application/rss+xml" rel="alternate" title="Alexis' log RSS Feed" />
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
@ -24,12 +24,13 @@
|
|||
<li><a href="./category/bar.html">bar</a></li>
|
||||
</ul></nav>
|
||||
</header><!-- /#banner -->
|
||||
<ul>
|
||||
<li><a href="./category/yeah.html">yeah</a></li>
|
||||
<li><a href="./category/misc.html">misc</a></li>
|
||||
<li><a href="./category/cat1.html">cat1</a></li>
|
||||
<li><a href="./category/bar.html">bar</a></li>
|
||||
</ul>
|
||||
<h1>Categories on Alexis' log</h1>
|
||||
<ul>
|
||||
<li><a href="./category/bar.html">bar</a> (1)</li>
|
||||
<li><a href="./category/cat1.html">cat1</a> (4)</li>
|
||||
<li><a href="./category/misc.html">misc</a> (4)</li>
|
||||
<li><a href="./category/yeah.html">yeah</a> (1)</li>
|
||||
</ul>
|
||||
<section id="extras" class="body">
|
||||
<div class="blogroll">
|
||||
<h2>links</h2>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - Alexis Métaireau</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/alexis-metaireau.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - Alexis Métaireau</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/alexis-metaireau.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
</summary><content type="html"><p>Some cool stuff!</p>
|
||||
</content></entry><entry><title>Second article</title><link href="http://blog.notmyidea.org/second-article.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-02-29:/second-article.html</id><summary type="html"><p>This is some article, in english</p>
|
||||
</summary><content type="html"><p>This is some article, in english</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log - Alexis Métaireau</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
<rss version="2.0"><channel><title>Alexis' log - Alexis Métaireau</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Fri, 30 Nov 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</guid></item><item><title>Second article</title><link>http://blog.notmyidea.org/second-article.html</link><description><p>This is some article, in english</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 29 Feb 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-02-29:/second-article.html</guid><category>foo</category><category>bar</category><category>baz</category></item><item><title>A markdown powered article</title><link>http://blog.notmyidea.org/a-markdown-powered-article.html</link><description><p>You're mutually oblivious.</p>
|
||||
<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all-en.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all-en.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
</summary><content type="html"><p>Some cool stuff!</p>
|
||||
</content></entry><entry><title>Second article</title><link href="http://blog.notmyidea.org/second-article.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-02-29:/second-article.html</id><summary type="html"><p>This is some article, in english</p>
|
||||
</summary><content type="html"><p>This is some article, in english</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all-fr.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2012-03-02T14:01:01+01:00</updated><entry><title>Trop bien !</title><link href="http://blog.notmyidea.org/oh-yeah-fr.html" rel="alternate"></link><published>2012-03-02T14:01:01+01:00</published><updated>2012-03-02T14:01:01+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-03-02:/oh-yeah-fr.html</id><summary type="html"><p>Et voila du contenu en français</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all-fr.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2012-03-02T14:01:01+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>Trop bien !</title><link href="http://blog.notmyidea.org/oh-yeah-fr.html" rel="alternate"></link><published>2012-03-02T14:01:01+01:00</published><updated>2012-03-02T14:01:01+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-03-02:/oh-yeah-fr.html</id><summary type="html"><p>Et voila du contenu en français</p>
|
||||
</summary><content type="html"><p>Et voila du contenu en français</p>
|
||||
</content></entry><entry><title>Deuxième article</title><link href="http://blog.notmyidea.org/second-article-fr.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-02-29:/second-article-fr.html</id><summary type="html"><p>Ceci est un article, en français.</p>
|
||||
</summary><content type="html"><p>Ceci est un article, en français.</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/all.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
</summary><content type="html"><p>Some cool stuff!</p>
|
||||
</content></entry><entry><title>Trop bien !</title><link href="http://blog.notmyidea.org/oh-yeah-fr.html" rel="alternate"></link><published>2012-03-02T14:01:01+01:00</published><updated>2012-03-02T14:01:01+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-03-02:/oh-yeah-fr.html</id><summary type="html"><p>Et voila du contenu en français</p>
|
||||
</summary><content type="html"><p>Et voila du contenu en français</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
<rss version="2.0"><channel><title>Alexis' log</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Fri, 30 Nov 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</guid></item><item><title>Trop bien !</title><link>http://blog.notmyidea.org/oh-yeah-fr.html</link><description><p>Et voila du contenu en français</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Fri, 02 Mar 2012 14:01:01 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-03-02:/oh-yeah-fr.html</guid></item><item><title>Second article</title><link>http://blog.notmyidea.org/second-article.html</link><description><p>This is some article, in english</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 29 Feb 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-02-29:/second-article.html</guid><category>foo</category><category>bar</category><category>baz</category></item><item><title>Deuxième article</title><link>http://blog.notmyidea.org/second-article-fr.html</link><description><p>Ceci est un article, en français.</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - bar</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/bar.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2010-10-20T10:14:00+02:00</updated><entry><title>Oh yeah !</title><link href="http://blog.notmyidea.org/oh-yeah.html" rel="alternate"></link><published>2010-10-20T10:14:00+02:00</published><updated>2010-10-20T10:14:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2010-10-20:/oh-yeah.html</id><summary type="html"><div class="section" id="why-not">
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - bar</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/bar.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2010-10-20T10:14:00+02:00</updated><subtitle>A personal blog.</subtitle><entry><title>Oh yeah !</title><link href="http://blog.notmyidea.org/oh-yeah.html" rel="alternate"></link><published>2010-10-20T10:14:00+02:00</published><updated>2010-10-20T10:14:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2010-10-20:/oh-yeah.html</id><summary type="html"><div class="section" id="why-not">
|
||||
<h2>Why not ?</h2>
|
||||
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
|
||||
YEAH !</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log - bar</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Wed, 20 Oct 2010 10:14:00 +0200</lastBuildDate><item><title>Oh yeah !</title><link>http://blog.notmyidea.org/oh-yeah.html</link><description><div class="section" id="why-not">
|
||||
<rss version="2.0"><channel><title>Alexis' log - bar</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Wed, 20 Oct 2010 10:14:00 +0200</lastBuildDate><item><title>Oh yeah !</title><link>http://blog.notmyidea.org/oh-yeah.html</link><description><div class="section" id="why-not">
|
||||
<h2>Why not ?</h2>
|
||||
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
|
||||
YEAH !</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - cat1</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/cat1.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2011-04-20T00:00:00+02:00</updated><entry><title>A markdown powered article</title><link href="http://blog.notmyidea.org/a-markdown-powered-article.html" rel="alternate"></link><published>2011-04-20T00:00:00+02:00</published><updated>2011-04-20T00:00:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2011-04-20:/a-markdown-powered-article.html</id><summary type="html"><p>You're mutually oblivious.</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - cat1</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/cat1.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2011-04-20T00:00:00+02:00</updated><subtitle>A personal blog.</subtitle><entry><title>A markdown powered article</title><link href="http://blog.notmyidea.org/a-markdown-powered-article.html" rel="alternate"></link><published>2011-04-20T00:00:00+02:00</published><updated>2011-04-20T00:00:00+02:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2011-04-20:/a-markdown-powered-article.html</id><summary type="html"><p>You're mutually oblivious.</p>
|
||||
<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a>
|
||||
<a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p></summary><content type="html"><p>You're mutually oblivious.</p>
|
||||
<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log - cat1</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Wed, 20 Apr 2011 00:00:00 +0200</lastBuildDate><item><title>A markdown powered article</title><link>http://blog.notmyidea.org/a-markdown-powered-article.html</link><description><p>You're mutually oblivious.</p>
|
||||
<rss version="2.0"><channel><title>Alexis' log - cat1</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Wed, 20 Apr 2011 00:00:00 +0200</lastBuildDate><item><title>A markdown powered article</title><link>http://blog.notmyidea.org/a-markdown-powered-article.html</link><description><p>You're mutually oblivious.</p>
|
||||
<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a>
|
||||
<a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 20 Apr 2011 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2011-04-20:/a-markdown-powered-article.html</guid></item><item><title>Article 1</title><link>http://blog.notmyidea.org/article-1.html</link><description><p>Article 1</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Thu, 17 Feb 2011 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2011-02-17:/article-1.html</guid></item><item><title>Article 2</title><link>http://blog.notmyidea.org/article-2.html</link><description><p>Article 2</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - misc</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/misc.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2012-11-30T00:00:00+01:00</updated><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - misc</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/misc.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2012-11-30T00:00:00+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>FILENAME_METADATA example</title><link href="http://blog.notmyidea.org/filename_metadata-example.html" rel="alternate"></link><published>2012-11-30T00:00:00+01:00</published><updated>2012-11-30T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</id><summary type="html"><p>Some cool stuff!</p>
|
||||
</summary><content type="html"><p>Some cool stuff!</p>
|
||||
</content></entry><entry><title>Second article</title><link href="http://blog.notmyidea.org/second-article.html" rel="alternate"></link><published>2012-02-29T00:00:00+01:00</published><updated>2012-02-29T00:00:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2012-02-29:/second-article.html</id><summary type="html"><p>This is some article, in english</p>
|
||||
</summary><content type="html"><p>This is some article, in english</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log - misc</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Fri, 30 Nov 2012 00:00:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
<rss version="2.0"><channel><title>Alexis' log - misc</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Fri, 30 Nov 2012 00:00:00 +0100</lastBuildDate><item><title>FILENAME_METADATA example</title><link>http://blog.notmyidea.org/filename_metadata-example.html</link><description><p>Some cool stuff!</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Fri, 30 Nov 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-11-30:/filename_metadata-example.html</guid></item><item><title>Second article</title><link>http://blog.notmyidea.org/second-article.html</link><description><p>This is some article, in english</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 29 Feb 2012 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2012-02-29:/second-article.html</guid><category>foo</category><category>bar</category><category>baz</category></item><item><title>Unbelievable !</title><link>http://blog.notmyidea.org/unbelievable.html</link><description><p>Or completely awesome. Depends the needs.</p>
|
||||
<p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - yeah</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/yeah.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><entry><title>This is a super article !</title><link href="http://blog.notmyidea.org/this-is-a-super-article.html" rel="alternate"></link><published>2010-12-02T10:14:00+01:00</published><updated>2013-11-17T23:29:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2010-12-02:/this-is-a-super-article.html</id><summary type="html"><p class="first last">Multi-line metadata should be supported
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis' log - yeah</title><link href="http://blog.notmyidea.org/" rel="alternate"></link><link href="http://blog.notmyidea.org/feeds/yeah.atom.xml" rel="self"></link><id>http://blog.notmyidea.org/</id><updated>2013-11-17T23:29:00+01:00</updated><subtitle>A personal blog.</subtitle><entry><title>This is a super article !</title><link href="http://blog.notmyidea.org/this-is-a-super-article.html" rel="alternate"></link><published>2010-12-02T10:14:00+01:00</published><updated>2013-11-17T23:29:00+01:00</updated><author><name>Alexis Métaireau</name></author><id>tag:blog.notmyidea.org,2010-12-02:/this-is-a-super-article.html</id><summary type="html"><p class="first last">Multi-line metadata should be supported
|
||||
as well as <strong>inline markup</strong>.</p>
|
||||
</summary><content type="html"><p>Some content here !</p>
|
||||
<div class="section" id="this-is-a-simple-title">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0"><channel><title>Alexis' log - yeah</title><link>http://blog.notmyidea.org/</link><description></description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>This is a super article !</title><link>http://blog.notmyidea.org/this-is-a-super-article.html</link><description><p class="first last">Multi-line metadata should be supported
|
||||
<rss version="2.0"><channel><title>Alexis' log - yeah</title><link>http://blog.notmyidea.org/</link><description>A personal blog.</description><lastBuildDate>Sun, 17 Nov 2013 23:29:00 +0100</lastBuildDate><item><title>This is a super article !</title><link>http://blog.notmyidea.org/this-is-a-super-article.html</link><description><p class="first last">Multi-line metadata should be supported
|
||||
as well as <strong>inline markup</strong>.</p>
|
||||
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Thu, 02 Dec 2010 10:14:00 +0100</pubDate><guid isPermaLink="false">tag:blog.notmyidea.org,2010-12-02:/this-is-a-super-article.html</guid><category>foo</category><category>bar</category><category>foobar</category></item></channel></rss>
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Trop bien !</title>
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li class="active"><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Deuxième article</title>
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li class="active"><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="../">Alexis' log </a></h1>
|
||||
<h1><a href="../">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="../tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="../override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="./">Alexis' log </a></h1>
|
||||
<h1><a href="./">Alexis' log <strong>A personal blog.</strong></a></h1>
|
||||
<nav><ul>
|
||||
<li><a href="./tag/oh.html">Oh Oh Oh</a></li>
|
||||
<li><a href="./override/">Override url/save_as</a></li>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 751 B After Width: | Height: | Size: 411 B |