diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 00000000..2cb24879
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,3 @@
+[report]
+omit = pelican/tests/*
+
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index f1ec6c3d..00000000
--- a/.editorconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-end_of_line = lf
-indent_size = 2
-indent_style = space
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.py]
-max_line_length = 88
-
-[*.{yml,yaml}]
-indent_size = 2
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..dfe07704
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.gitignore b/.gitignore
index c2658d7d..1ae0e9f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,15 @@
-node_modules/
+*.egg-info
+.*.swp
+.*.swo
+*.pyc
+.DS_Store
+docs/_build
+docs/fr/_build
+build
+dist
+tags
+.tox
+.coverage
+htmlcov
+six-*.egg/
+*.orig
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 00000000..8dd40fb6
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,24 @@
+Alexis Métaireau
+Alexis Métaireau
+Alexis Métaireau
+Axel Haustant
+Axel Haustant
+Dave Mankoff
+Feth Arezki
+Guillaume
+Guillaume
+Guillaume B
+Guillermo López
+Guillermo López
+Jomel Imperio
+Justin Mayer
+Justin Mayer
+Marco Milanesi
+Massimo Santini
+Rémy HUBSCHER
+Simon Conseil
+Simon Liedtke
+Skami18
+Stuart Colville
+Stéphane Bunel
+tBunnyMan
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..54dcf0ea
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+language: python
+python:
+ - "2.7"
+ - "3.3"
+ - "3.4"
+before_install:
+ - sudo apt-get update -qq
+ - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8
+install:
+ - pip install .
+ - pip install -r dev_requirements.txt
+ - pip install nose-cov
+script: nosetests -sv --with-coverage --cover-package=pelican pelican
+after_success:
+ # Report coverage results to coveralls.io
+ - pip install coveralls
+ - coveralls
+notifications:
+ irc:
+ channels:
+ - "irc.freenode.org#pelican"
+ on_success: change
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 00000000..85d30c97
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,144 @@
+Filing issues
+-------------
+
+* Before you file an issue, try `asking for help`_ first.
+* If determined to file an issue, first check for `existing issues`_, including
+ closed issues.
+
+.. _`asking for help`: `How to get help`_
+.. _`existing issues`: https://github.com/getpelican/pelican/issues
+
+How to get help
+---------------
+
+Before you ask for help, please make sure you do the following:
+
+1. Read the documentation_ thoroughly. If in a hurry, at least use the search
+ field that is provided at top-right on the documentation_ pages. Make sure
+ you read the docs for the Pelican version you are using.
+2. Use a search engine (e.g., DuckDuckGo, Google) to search for a solution to
+ your problem. Someone may have already found a solution, perhaps in the
+ form of a plugin_ or a specific combination of settings.
+
+3. Try reproducing the issue in a clean environment, ensuring you are using:
+
+* latest Pelican release (or an up-to-date git clone of Pelican master)
+* latest releases of libraries used by Pelican
+* 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
+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)::
+
+ cd ~/projects/your-site
+ git clone https://github.com/getpelican/pelican ~/projects/pelican
+ pelican content -s ~/projects/pelican/samples/pelican.conf.py -t ~/projects/pelican/pelican/themes/notmyidea
+
+If despite the above efforts you still cannot resolve your problem, be sure to
+include in your inquiry the following information, preferably in the form of
+links to content uploaded to a `paste service`_, GitHub repository, or other
+publicly-accessible location:
+
+* Describe what version of Pelican you are running (output of ``pelican --version``
+ or the HEAD commit hash if you cloned the repo) and how exactly you installed
+ it (the full command you used, e.g. ``pip install pelican``).
+* If you are looking for a way to get some end result, prepare a detailed
+ description of what the end result should look like (preferably in the form of
+ an image or a mock-up page) and explain in detail what you have done so far to
+ achieve it.
+* If you are trying to solve some issue, prepare a detailed description of how
+ to reproduce the problem. If the issue cannot be easily reproduced, it cannot
+ be debugged by developers or volunteers. Describe only the **minimum steps**
+ necessary to reproduce it (no extra plugins, etc.).
+* Upload your settings file or any other custom code that would enable people to
+ reproduce the problem or to see what you have already tried to achieve the
+ desired end result.
+* Upload detailed and **complete** output logs and backtraces (remember to add
+ the ``--debug`` flag: ``pelican --debug content [...]``)
+
+.. _documentation: http://docs.getpelican.com/
+.. _`paste service`: https://dpaste.de/
+
+Once the above preparation is ready, you can contact people willing to help via
+(preferably) the ``#pelican`` IRC channel or send a message to ``authors at getpelican dot com``.
+Remember to include all the information you prepared.
+
+The #pelican IRC channel
+........................
+
+* Because of differing time zones, you may not get an immediate response to your
+ question, but please be patient and stay logged into IRC — someone will almost
+ always respond if you wait long enough (it may take a few hours).
+* If you don't have an IRC client handy, use the webchat_ for quick feedback.
+* You can direct your IRC client to the channel using this `IRC link`_ or you
+ 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/
+
+
+Contributing code
+-----------------
+
+Before you submit a contribution, please ask whether it is desired so that you
+don't spend a lot of time working on something that would be rejected for a
+known reason. Consider also whether your new feature might be better suited as
+a plugin_ — you can `ask for help`_ to make that determination.
+
+Using Git and GitHub
+....................
+
+* `Create a new git branch`_ specific to your change (as opposed to making
+ your commits in the master branch).
+* **Don't put multiple unrelated fixes/features in the same branch / pull request.**
+ For example, if you're hacking on a new feature and find a bugfix that
+ doesn't *require* your new feature, **make a new distinct branch and pull
+ request** for the bugfix.
+* Check for unnecessary whitespace via ``git diff --check`` before committing.
+* First line of your commit message should start with present-tense verb, be 50
+ characters or less, and include the relevant issue number(s) if applicable.
+ *Example:* ``Ensure proper PLUGIN_PATH behavior. Refs #428.`` If the commit
+ *completely fixes* an existing bug report, please use ``Fixes #585`` or ``Fix
+ #585`` syntax (so the relevant issue is automatically closed upon PR merge).
+* After the first line of the commit message, add a blank line and then a more
+ detailed explanation (when relevant).
+* `Squash your commits`_ to eliminate merge commits and ensure a clean and
+ readable commit history.
+* If you have previously filed a GitHub issue and want to contribute code that
+ addresses that issue, **please use** ``hub pull-request`` instead of using
+ GitHub's web UI to submit the pull request. This isn't an absolute
+ requirement, but makes the maintainers' lives much easier! Specifically:
+ `install hub `_ and then run
+ `hub pull-request `_ to
+ turn your GitHub issue into a pull request containing your code.
+
+Contribution quality standards
+..............................
+
+* Adhere to `PEP8 coding standards`_ whenever possible. This can be eased via
+ the `pep8 `_ or `flake8
+ `_ tools, the latter of which in
+ particular will give you some useful hints about ways in which the
+ code/formatting can be improved.
+* Make sure your code is compatible with Python 2.7, 3.3, and 3.4 — see our
+ `compatibility cheatsheet`_ for more details.
+* Add docs and tests for your changes. Undocumented and untested features will
+ not be accepted.
+* `Run all the tests`_ **on all versions of Python supported by Pelican** to
+ ensure nothing was accidentally broken.
+
+Check out our `Git Tips`_ page or `ask for help`_ if you
+need assistance or have any questions about these guidelines.
+
+.. _`plugin`: http://docs.getpelican.com/en/latest/plugins.html
+.. _`#pelican IRC channel`: http://webchat.freenode.net/?channels=pelican&uio=d4
+.. _`Create a new git branch`: https://github.com/getpelican/pelican/wiki/Git-Tips#making-your-changes
+.. _`Squash your commits`: https://github.com/getpelican/pelican/wiki/Git-Tips#squashing-commits
+.. _`Run all the tests`: http://docs.getpelican.com/en/latest/contribute.html#running-the-test-suite
+.. _`Git Tips`: https://github.com/getpelican/pelican/wiki/Git-Tips
+.. _`PEP8 coding standards`: http://www.python.org/dev/peps/pep-0008/
+.. _`ask for help`: `How to get help`_
+.. _`compatibility cheatsheet`: http://docs.getpelican.com/en/latest/contribute.html#python-3-development-tips
diff --git a/LICENSE b/LICENSE
index 789896df..dba13ed2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,20 +1,661 @@
-MIT License
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
-Copyright (c) 2024 Oliver Ladner
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
+ Preamble
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+ .
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..dcf9ea45
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include *.rst
+recursive-include pelican *.html *.css *png *.in *.rst *.md *.mkd *.xml *.py
+include LICENSE THANKS docs/changelog.rst
diff --git a/README.md b/README.md
deleted file mode 100644
index f3fe9563..00000000
--- a/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# lugh Pelican theme
-
-This theme is based on the [simple theme](https://github.com/getpelican/pelican/tree/main/pelican/themes/simple/templates).
-It's heavily customized to what I need here, so no efforts have been made to
-keep it useful for others. Amongst other things, I:
-
-- removed translations
-- changed the structure (HTML `` etc.)
-
-## Docs
-
-- [Pelican: how to create your own theme](https://docs.getpelican.com/en/stable/themes.html)
-- [Tailwind CSS quick start](https://tailwindcss.com/docs/installation)
-
-## Doing
-
-### Prepare Pelican development server config
-
-Adapt Pelican's `publishconf.py` for local development.
-E.g. `RELATIVE_URLS = False`
-
-### Install Tailwind CSS Typography plugin
-
-Typography enables sane defaults for longer texts. In this case, we use it for
-the body content only, which is always Markdown. Typography is a bit of a beast
-to configure/align to standard Tailwind.
-
-```shell
-npm install -D @tailwindcss/typography
-```
-
-### Run the Tailwind build process
-
-```shell
-npx tailwindcss -i static/css/in.css -o static/css/out.css --watch
-```
-
-### Run Pelican dev server
-
-```shell
-conda activate pelican
-./devserver.sh
-```
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..58896f1d
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,64 @@
+Pelican |build-status| |coverage-status|
+========================================
+
+Pelican is a static site generator, written in Python_.
+
+* Write your weblog entries directly with your editor of choice (vim!)
+ in reStructuredText_ or Markdown_
+* Includes a simple CLI tool to (re)generate the weblog
+* Easy to interface with DVCSes and web hooks
+* Completely static output is easy to host anywhere
+
+Features
+--------
+
+Pelican currently supports:
+
+* Blog articles and pages
+* Comments, via an external service (Disqus). (Please note that while
+ useful, Disqus is an external service, and thus the comment data will be
+ somewhat outside of your control and potentially subject to data loss.)
+* Theming support (themes are created using Jinja2_ templates)
+* PDF generation of the articles/pages (optional)
+* Publication of articles in multiple languages
+* Atom/RSS feeds
+* Code syntax highlighting
+* Import from WordPress, Dotclear, or RSS feeds
+* Integration with external tools: Twitter, Google Analytics, etc. (optional)
+* Fast rebuild times thanks to content caching and selective output writing.
+
+Have a look at the `Pelican documentation`_ for more information.
+
+Why the name "Pelican"?
+-----------------------
+
+"Pelican" is an anagram for *calepin*, which means "notebook" in French. ;)
+
+Source code
+-----------
+
+You can access the source code at: https://github.com/getpelican/pelican
+
+If you feel hackish, have a look at the explanation of `Pelican's internals`_.
+
+How to get help, contribute, or provide feedback
+------------------------------------------------
+
+See our `contribution submission and feedback guidelines `_.
+
+.. Links
+
+.. _Python: http://www.python.org/
+.. _reStructuredText: http://docutils.sourceforge.net/rst.html
+.. _Markdown: http://daringfireball.net/projects/markdown/
+.. _Jinja2: http://jinja.pocoo.org/
+.. _`Pelican documentation`: http://docs.getpelican.com/
+.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
+
+.. |build-status| image:: https://travis-ci.org/getpelican/pelican.svg?branch=master
+ :target: https://travis-ci.org/getpelican/pelican
+ :alt: Travis CI: continuous integration status
+.. |coverage-status| image:: https://img.shields.io/coveralls/getpelican/pelican.svg
+ :target: https://coveralls.io/r/getpelican/pelican
+ :alt: Coveralls: code coverage status
+
diff --git a/THANKS b/THANKS
new file mode 100644
index 00000000..1d867471
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,158 @@
+Pelican is a project originally created by Alexis Métaireau
+ , but there are a large number of people that have
+contributed or implemented key features over time. We do our best to keep this
+list up-to-date, but you can also have a look at the nice contributor graphs
+produced by GitHub: https://github.com/getpelican/pelican/graphs/contributors
+
+If you want to contibute, check the documentation section about how to do so:
+
+
+Aaron Kavlie
+Abhishek L
+Albrecht Mühlenschulte
+Aldiantoro Nugroho
+Alen Mujezinovic
+Alessandro Martin
+Alexander Artemenko
+Alexandre RODIERE
+Alexis Daboville
+Alexis Métaireau
+Allan Whatmough
+Andrea Crotti
+Andrew Laski
+Andrew Spiers
+Arnaud BOS
+asselinpaul
+Axel Haustant
+Benoît HERVIER
+Borgar
+Brandon W Maister
+Brendan Wholihan
+Brian C. Lane
+Brian Hsu
+Brian St. Pierre
+Bruno Binet
+BunnyMan
+Chenguang Wang
+Chris Elston
+Chris McDonald (Wraithan)
+Chris Streeter
+Christophe Chauvet
+Clint Howarth
+Colin Dunklau
+Dafydd Crosby
+Dana Woodman
+Dave King
+Dave Mankoff
+David Beitey
+David Marble
+Deniz Turgut (Avaris)
+derdon
+Dirkjan Ochtman
+Dirk Makowski
+draftcode
+Edward Delaporte
+Emily Strickland
+epatters
+Eric Case
+Erik Hetzner
+FELD Boris
+Feth Arezki
+Florian Jacob
+Florian Preinstorfer
+Félix Delval
+Freeculture
+George V. Reilly
+Guillaume
+Guillaume B
+Guillermo López
+guillermooo
+Ian Cordasco
+Igor Kalnitsky
+Irfan Ahmad
+Iuri de Silvio
+Ivan Dyedov
+James King
+James Rowe
+jawher
+Jered Boxman
+Jerome
+Jiachen Yang
+Jochen Breuer
+joe di castro
+John Kristensen
+John Mastro
+Jökull Sólberg Auðunsson
+Jomel Imperio
+Joseph Reagle
+Joshua Adelman
+Julian Berman
+Justin Mayer
+Kevin Deldycke
+Kyle Fuller
+Laureline Guerin
+Leonard Huang
+Leroy Jiang
+Marcel Hellkamp
+Marco Milanesi
+Marcus Fredriksson
+Mario Rodas
+Mark Caudill
+Martin Brochhaus
+Massimo Santini
+Matt Bowcock
+Matt Layman
+Meir Kriheli
+Michael Guntsche
+Michael Reneer
+Michael Yanovich
+Mike Yumatov
+Mikhail Korobov
+m-r-r
+mviera
+Nico Di Rocco
+Nicolas Duhamel
+Nicolas Perriault
+Nicolas Steinmetz
+Paul Asselin
+Pavel Puchkin
+Perry Roper
+Peter Desmet
+Philippe Pepiot
+Rachid Belaid
+Randall Degges
+Ranjhith Kalisamy
+Remi Rampin
+Rémy HUBSCHER
+renhbo
+Richard Duivenvoorde
+Rogdham
+Roman Skvazh
+Ronny Pfannschmidt
+Rory McCann
+Rıdvan Örsvuran
+saghul
+sam
+Samrat Man Singh
+Simon Conseil
+Simon Liedtke
+Skami18
+solsTiCe d'Hiver
+Steve Schwarz
+Stéphane Bunel
+Stéphane Raimbault
+Stuart Colville
+Talha Mansoor
+Tarek Ziade
+Thanos Lefteris
+Thomas Thurman
+Tobias
+Tomi Pieviläinen
+Trae Blain
+Tshepang Lekhonkhobe
+Valentin-Costel Hăloiu
+Vlad Niculae
+William Light
+Wladislaw Merezhko
+W. Trevor King
+Zoresvit
diff --git a/bumpr.rc b/bumpr.rc
new file mode 100644
index 00000000..97dbad22
--- /dev/null
+++ b/bumpr.rc
@@ -0,0 +1,30 @@
+[bumpr]
+file = pelican/__init__.py
+vcs = git
+clean =
+ python setup.py clean
+ rm -rf *egg-info build dist
+tests = python -m unittest discover
+publish = python setup.py sdist bdist_wheel register upload
+files = README.rst
+
+[bump]
+unsuffix = true
+message = Bump version {version}
+
+[prepare]
+part = patch
+suffix = dev
+message = Prepare version {version} for next development cycle
+
+[changelog]
+file = docs/changelog.rst
+separator = =
+bump = {version} ({date:%Y-%m-%d})
+prepare = Next release
+
+[readthedoc]
+url = http://docs.getpelican.com/{tag}
+
+[commands]
+bump = sed -i "" "s/last_stable\s*=.*/last_stable = '{version}'/" docs/conf.py
diff --git a/dev_requirements.txt b/dev_requirements.txt
new file mode 100644
index 00000000..a7c10719
--- /dev/null
+++ b/dev_requirements.txt
@@ -0,0 +1,15 @@
+# Tests
+mock
+
+# Optional Packages
+Markdown
+BeautifulSoup4
+lxml
+typogrify
+
+# To perform release
+bumpr==0.2.0
+wheel
+
+# For docs theme
+sphinx_rtd_theme
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..bf49b542
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Raclette.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Raclette.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Raclette"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Raclette"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/_static/overall.png b/docs/_static/overall.png
new file mode 100644
index 00000000..1240a148
Binary files /dev/null and b/docs/_static/overall.png differ
diff --git a/docs/_static/theme-basic.zip b/docs/_static/theme-basic.zip
new file mode 100644
index 00000000..d1e4754a
Binary files /dev/null and b/docs/_static/theme-basic.zip differ
diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css
new file mode 100644
index 00000000..83afc78e
--- /dev/null
+++ b/docs/_static/theme_overrides.css
@@ -0,0 +1,12 @@
+
+/* override table width restrictions */
+.wy-table-responsive table td, .wy-table-responsive table th {
+ /* !important prevents the common CSS stylesheets from
+ overriding this as on RTD they are loaded after this stylesheet */
+ white-space: normal !important;
+}
+
+.wy-table-responsive {
+ overflow: visible !important;
+}
+
diff --git a/docs/_static/uml.jpg b/docs/_static/uml.jpg
new file mode 100644
index 00000000..03655d7e
Binary files /dev/null and b/docs/_static/uml.jpg differ
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 00000000..d8c33cb5
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1,231 @@
+Release history
+###############
+
+3.4.0 (2014-07-01)
+==================
+
+* Speed up content generation via new caching mechanism
+* Add selective post generation (instead of always building entire site)
+* Many documentation improvements, including switching to prettier RtD theme
+* Add support for multiple content and plugin paths
+* Add ``:modified:`` metadata field to complement ``:date:``.
+ Used to specify the last date and time an article was updated independently
+ from the date and time it was published.
+* Add support for multiple authors via new ``:authors:`` metadata field
+* Watch for changes in static directories when in auto-regeneration mode
+* Add filters to limit log output when desired
+* Add language support to drafts
+* Add ``SLUGIFY_SOURCE`` setting to control how post slugs are generated
+* Fix many issues relating to locale and encoding
+* Apply Typogrify filter to post summary
+* Preserve file metadata (e.g. time stamps) when copying static files to output
+* Move AsciiDoc support from Pelican core into separate plugin
+* Produce inline links instead of reference-style links when importing content
+* Improve handling of ``IGNORE_FILES`` setting behavior
+* Properly escape symbol characters in tag names (e.g., ``C++``)
+* Minor tweaks for Python 3.4 compatibility
+* Add several new signals
+
+3.3.0 (2013-09-24)
+==================
+
+* Drop Python 3.2 support in favor of Python 3.3
+* Add ``Fabfile`` so Fabric can be used for workflow automation instead of Make
+* ``OUTPUT_RETENTION`` setting can be used to preserve metadata (e.g., VCS
+ data such as ``.hg`` and ``.git``) from being removed from output directory
+* Tumblr import
+* Improve logic and consistency when cleaning output folder
+* Improve documentation versioning and release automation
+* Improve pagination flexibility
+* Rename signals for better consistency (some plugins may need to be updated)
+* Move metadata extraction from generators to readers; metadata extraction no
+ longer article-specific
+* Deprecate ``FILES_TO_COPY`` in favor of ``STATIC_PATHS`` and
+ ``EXTRA_PATH_METADATA``
+* Summaries in Markdown posts no longer include footnotes
+* Remove unnecessary whitespace in output via ``lstrip_blocks`` Jinja parameter
+* Move PDF generation from core to plugin
+* Replace ``MARKUP`` setting with ``READERS``
+* Add warning if img tag is missing ``alt`` attribute
+* Add support for ``{}`` in relative links syntax, besides ``||``
+* Add support for ``{tag}`` and ``{category}`` relative links
+* Add a ``content_written`` signal
+
+3.2.1 and 3.2.2
+===============
+
+* Facilitate inclusion in FreeBSD Ports Collection
+
+3.2 (2013-04-24)
+================
+
+* Support for Python 3!
+* Override page save-to location from meta-data (enables using a static page as
+ the site's home page, for example)
+* Time period archives (per-year, per-month, and per-day archives of posts)
+* Posterous blog import
+* Improve WordPress blog import
+* Migrate plugins to separate repository
+* Improve HTML parser
+* Provide ability to show or hide categories from menu using
+ ``DISPLAY_CATEGORIES_ON_MENU`` option
+* Auto-regeneration can be told to ignore files via ``IGNORE_FILES`` setting
+* Improve post-generation feedback to user
+* For multilingual posts, use meta-data to designate which is the original
+ and which is the translation
+* Add ``.mdown`` to list of supported Markdown file extensions
+* Document-relative URL generation (``RELATIVE_URLS``) is now off by default
+
+3.1 (2012-12-04)
+================
+
+* Importer now stores slugs within files by default. This can be disabled with
+ the ``--disable-slugs`` option.
+* Improve handling of links to intra-site resources
+* Ensure WordPress import adds paragraphs for all types of line endings
+ in post content
+* Decode HTML entities within WordPress post titles on import
+* Improve appearance of LinkedIn icon in default theme
+* Add GitHub and Google+ social icons support in default theme
+* Optimize social icons
+* Add ``FEED_ALL_ATOM`` and ``FEED_ALL_RSS`` to generate feeds containing all posts regardless of their language
+* Split ``TRANSLATION_FEED`` into ``TRANSLATION_FEED_ATOM`` and ``TRANSLATION_FEED_RSS``
+* Different feeds can now be enabled/disabled individually
+* Allow for blank author: if ``AUTHOR`` setting is not set, author won't
+ default to ``${USER}`` anymore, and a post won't contain any author
+ information if the post author is empty
+* Move LESS and Webassets support from Pelican core to plugin
+* The ``DEFAULT_DATE`` setting now defaults to ``None``, which means that
+ articles won't be generated unless date metadata is specified
+* Add ``FILENAME_METADATA`` setting to support metadata extraction from filename
+* Add ``gzip_cache`` plugin to compress common text files into a ``.gz``
+ file within the same directory as the original file, preventing the server
+ (e.g. Nginx) from having to compress files during an HTTP call
+* Add support for AsciiDoc-formatted content
+* Add ``USE_FOLDER_AS_CATEGORY`` setting so that feature can be toggled on/off
+* Support arbitrary Jinja template files
+* Restore basic functional tests
+* New signals: ``generator_init``, ``get_generators``, and
+ ``article_generate_preread``
+
+3.0 (2012-08-08)
+================
+
+* Refactored the way URLs are handled
+* Improved the English documentation
+* Fixed packaging using ``setuptools`` entrypoints
+* Added ``typogrify`` support
+* Added a way to disable feed generation
+* Added support for ``DIRECT_TEMPLATES``
+* Allow multiple extensions for content files
+* Added LESS support
+* Improved the import script
+* Added functional tests
+* Rsync support in the generated Makefile
+* Improved feed support (easily pluggable with Feedburner for instance)
+* Added support for ``abbr`` in reST
+* Fixed a bunch of bugs :-)
+
+2.8 (2012-02-28)
+==================
+
+* Dotclear importer
+* Allow the usage of Markdown extensions
+* Themes are now easily extensible
+* Don't output pagination information if there is only one page
+* Add a page per author, with all their articles
+* Improved the test suite
+* Made the themes easier to extend
+* Removed Skribit support
+* Added a ``pelican-quickstart`` script
+* Fixed timezone-related issues
+* Added some scripts for Windows support
+* Date can be specified in seconds
+* Never fail when generating posts (skip and continue)
+* Allow the use of future dates
+* Support having different timezones per language
+* Enhanced the documentation
+
+2.7 (2011-06-11)
+==================
+
+* Use ``logging`` rather than echoing to stdout
+* Support custom Jinja filters
+* Compatibility with Python 2.5
+* Added a theme manager
+* Packaged for Debian
+* Added draft support
+
+2.6 (2011-03-08)
+==================
+
+* Changes in the output directory structure
+* Makes templates easier to work with / create
+* Added RSS support (was Atom-only)
+* Added tag support for the feeds
+* Enhance the documentation
+* Added another theme (brownstone)
+* Added translations
+* Added a way to use cleaner URLs with a rewrite url module (or equivalent)
+* Added a tag cloud
+* Added an autoreloading feature: the blog is automatically regenerated each time a modification is detected
+* Translate the documentation into French
+* Import a blog from an RSS feed
+* Pagination support
+* Added Skribit support
+
+2.5 (2010-11-20)
+==================
+
+* Import from Wordpress
+* Added some new themes (martyalchin / wide-notmyidea)
+* First bug report!
+* Linkedin support
+* Added a FAQ
+* Google Analytics support
+* Twitter support
+* Use relative URLs, not static ones
+
+2.4 (2010-11-06)
+================
+
+* Minor themes changes
+* Add Disqus support (so we have comments)
+* Another code refactoring
+* Added config settings about pages
+* Blog entries can also be generated in PDF
+
+2.3 (2010-10-31)
+================
+
+* Markdown support
+
+2.2 (2010-10-30)
+================
+
+* Prettify output
+* Manages static pages as well
+
+2.1 (2010-10-30)
+================
+
+* Make notmyidea the default theme
+
+2.0 (2010-10-30)
+================
+
+* Refactoring to be more extensible
+* Change into the setting variables
+
+1.2 (2010-09-28)
+================
+
+* Added a debug option
+* Added per-category feeds
+* Use filesystem to get dates if no metadata is provided
+* Add Pygments support
+
+1.1 (2010-08-19)
+================
+
+* First working version
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..d4efcb38
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+import sys, os
+
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+sys.path.append(os.path.abspath(os.pardir))
+
+from pelican import __version__
+
+# -- General configuration -----------------------------------------------------
+templates_path = ['_templates']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.extlinks']
+source_suffix = '.rst'
+master_doc = 'index'
+project = 'Pelican'
+copyright = '2014, Alexis Metaireau and contributors'
+exclude_patterns = ['_build']
+release = __version__
+version = '.'.join(release.split('.')[:1])
+last_stable = '3.3.0'
+rst_prolog = '''
+.. |last_stable| replace:: :pelican-doc:`{0}`
+'''.format(last_stable)
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+extlinks = {
+ 'pelican-doc': ('http://docs.getpelican.com/%s/', '')
+}
+
+# -- Options for HTML output ---------------------------------------------------
+
+html_theme = 'default'
+if not on_rtd:
+ try:
+ import sphinx_rtd_theme
+ html_theme = 'sphinx_rtd_theme'
+ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+ except ImportError:
+ pass
+
+html_static_path = ['_static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Pelicandoc'
+
+html_use_smartypants = True
+
+# If false, no module index is generated.
+html_use_modindex = False
+
+# If false, no index is generated.
+html_use_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+
+def setup(app):
+ # overrides for wide tables in RTD theme
+ app.add_stylesheet('theme_overrides.css') # path relative to _static
+
+
+# -- Options for LaTeX output --------------------------------------------------
+latex_documents = [
+ ('index', 'Pelican.tex', 'Pelican Documentation',
+ 'Alexis Métaireau', 'manual'),
+]
+
+# -- Options for manual page output --------------------------------------------
+man_pages = [
+ ('index', 'pelican', 'pelican documentation',
+ ['Alexis Métaireau'], 1),
+ ('pelican-themes', 'pelican-themes', 'A theme manager for Pelican',
+ ['Mickaël Raybaud'], 1),
+ ('themes', 'pelican-theming', 'How to create themes for Pelican',
+ ['The Pelican contributors'], 1)
+]
diff --git a/docs/content.rst b/docs/content.rst
new file mode 100644
index 00000000..ad81bed1
--- /dev/null
+++ b/docs/content.rst
@@ -0,0 +1,372 @@
+Writing content
+###############
+
+Articles and pages
+==================
+
+Pelican considers "articles" to be chronological content, such as posts on a
+blog, and thus associated with a date.
+
+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).
+
+.. _internal_metadata:
+
+File metadata
+=============
+
+Pelican tries to be smart enough to get the information it needs from the
+file system (for instance, about the category of your articles), but some
+information you need to provide in the form of metadata inside your files.
+
+If you are writing your content in reStructuredText format, you can provide
+this metadata in text files via the following syntax (give your file the
+``.rst`` extension)::
+
+ My super title
+ ##############
+
+ :date: 2010-10-03 10:20
+ :modified: 2010-10-04 18:40
+ :tags: thats, awesome
+ :category: yeah
+ :slug: my-super-post
+ :authors: Alexis Metaireau, Conan Doyle
+ :summary: Short version for index and feeds
+
+Pelican implements an extension to reStructuredText to enable support for the
+``abbr`` HTML tag. To use it, write something like this in your post::
+
+ This will be turned into :abbr:`HTML (HyperText Markup Language)`.
+
+You can also use Markdown syntax (with a file ending in ``.md``,
+``.markdown``, ``.mkd``, or ``.mdown``). Markdown generation requires that you
+first explicitly install the ``Markdown`` package, which can be done via ``pip
+install Markdown``. Metadata syntax for Markdown posts should follow this
+pattern::
+
+ Title: My super title
+ Date: 2010-12-03 10:20
+ Modified: 2010-12-05 19:30
+ Category: Python
+ Tags: pelican, publishing
+ Slug: my-super-post
+ Authors: Alexis Metaireau, Conan Doyle
+ Summary: Short version for index and feeds
+
+ This is the content of my super blog post.
+
+Readers for additional formats (such as AsciiDoc_) are available via plugins.
+Refer to `pelican-plugins`_ repository for those.
+
+Pelican can also process HTML files ending in ``.html`` and ``.htm``. Pelican
+interprets the HTML in a very straightforward manner, reading metadata from
+``meta`` tags, the title from the ``title`` tag, and the body out from the
+``body`` tag::
+
+
+
+ My super title
+
+
+
+
+
+
+
+
+ This is the content of my super blog post.
+
+
+
+With HTML, there is one simple exception to the standard metadata: ``tags`` can
+be specified either via the ``tags`` metadata, as is standard in Pelican, or
+via the ``keywords`` metadata, as is standard in HTML. The two can be used
+interchangeably.
+
+Note that, aside from the title, none of this article metadata is mandatory:
+if the date is not specified and ``DEFAULT_DATE`` is set to ``fs``, Pelican
+will rely on the file's "mtime" timestamp, and the category can be determined
+by the directory in which the file resides. For example, a file located at
+``python/foobar/myfoobar.rst`` will have a category of ``foobar``. If you would
+like to organize your files in other ways where the name of the subfolder would
+not be a good category name, you can set the setting ``USE_FOLDER_AS_CATEGORY``
+to ``False``. When parsing dates given in the page metadata, Pelican supports
+the W3C's `suggested subset ISO 8601`__.
+
+__ `W3C ISO 8601`_
+
+``modified`` should be last time you updated the article, and defaults to ``date`` if not specified.
+Besides you can show ``modified`` in the templates, feed entries in feed readers will be updated automatically
+when you set ``modified`` to the current date after you modified your article.
+
+``authors`` is a comma-separated list of article authors. If there's only one author you
+can use ``author`` field.
+
+If you do not explicitly specify summary metadata for a given post, the
+``SUMMARY_MAX_LENGTH`` setting can be used to specify how many words from the
+beginning of an article are used as the summary.
+
+You can also extract any metadata from the filename through a regular
+expression to be set in the ``FILENAME_METADATA`` setting. All named groups
+that are matched will be set in the metadata object. The default value for the
+``FILENAME_METADATA`` setting will only extract the date from the filename. For
+example, if you would like to extract both the date and the slug, you could set
+something like: ``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``
+
+Please note that the metadata available inside your files takes precedence over
+the metadata extracted from the filename.
+
+Pages
+=====
+
+If you create a folder named ``pages`` inside the content folder, all the
+files in it will be used to generate static pages, such as **About** or
+**Contact** pages. (See example filesystem layout below.)
+
+You can use the ``DISPLAY_PAGES_ON_MENU`` setting to control whether all those
+pages are displayed in the primary navigation menu. (Default is ``True``.)
+
+If you want to exclude any pages from being linked to or listed in the menu
+then add a ``status: hidden`` attribute to its metadata. This is useful for
+things like making error pages that fit the generated theme of your site.
+
+.. _ref-linking-to-internal-content:
+
+Linking to internal content
+===========================
+
+From Pelican 3.1 onwards, it is now possible to specify intra-site links to
+files in the *source content* hierarchy instead of files in the *generated*
+hierarchy. This makes it easier to link from the current post to other posts
+and images that may be sitting alongside the current post (instead of having
+to determine where those resources will be placed after site generation).
+
+To link to internal content (files in the ``content`` directory), use the
+following syntax: ``{filename}path/to/file``::
+
+
+ website/
+ ├── content
+ │ ├── article1.rst
+ │ ├── cat/
+ │ │ └── article2.md
+ │ └── pages
+ │ └── about.md
+ └── pelican.conf.py
+
+In this example, ``article1.rst`` could look like::
+
+ The first article
+ #################
+
+ :date: 2012-12-01 10:02
+
+ See below intra-site link examples in reStructuredText format.
+
+ `a link relative to content root <{filename}/cat/article2.rst>`_
+ `a link relative to current file <{filename}cat/article2.rst>`_
+
+and ``article2.md``::
+
+ Title: The second article
+ Date: 2012-12-01 10:02
+
+ See below intra-site link examples in Markdown format.
+
+ [a link relative to content root]({filename}/article1.md)
+ [a link relative to current file]({filename}../article1.md)
+
+Embedding non-article or non-page content is slightly different in that the
+directories need to be specified in ``pelicanconf.py`` file. The ``images``
+directory is configured for this by default but others will need to be added
+manually::
+
+ content
+ ├── images
+ │ └── han.jpg
+ └── misc
+ └── image-test.md
+
+And ``image-test.md`` would include::
+
+ 
+
+Any content can be linked in this way. What happens is that the ``images``
+directory gets copied to ``output/`` during site generation because Pelican
+includes ``images`` in the ``STATIC_PATHS`` setting's list by default. If
+you want to have another directory, say ``pdfs``, copied from your content to
+your output during site generation, you would need to add the following to
+your settings file::
+
+ STATIC_PATHS = ['images', 'pdfs']
+
+After the above line has been added, subsequent site generation should copy the
+``content/pdfs/`` directory to ``output/pdfs/``.
+
+You can also link to categories or tags, using the ``{tag}tagname`` and
+``{category}foobar`` syntax.
+
+For backward compatibility, Pelican also supports bars (``||``) in addition to
+curly braces (``{}``). For example: ``|filename|an_article.rst``,
+``|tag|tagname``, ``|category|foobar``. The syntax was changed from ``||`` to
+``{}`` to avoid collision with Markdown extensions or reST directives.
+
+Importing an existing site
+==========================
+
+It is possible to import your site from WordPress, Tumblr, Dotclear, and RSS
+feeds using a simple script. See :ref:`import`.
+
+Translations
+============
+
+It is possible to translate articles. To do so, you need to add a ``lang`` meta
+attribute to your articles/pages and set a ``DEFAULT_LANG`` setting (which is
+English [en] by default). With those settings in place, only articles with the
+default language will be listed, and each article will be accompanied by a list
+of available translations for that article.
+
+Pelican uses the article's URL "slug" to determine if two or more articles are
+translations of one another. The slug can be set manually in the file's
+metadata; if not set explicitly, Pelican will auto-generate the slug from the
+title of the article.
+
+Here is an example of two articles, one in English and the other in French.
+
+The English article::
+
+ Foobar is not dead
+ ##################
+
+ :slug: foobar-is-not-dead
+ :lang: en
+
+ That's true, foobar is still alive!
+
+And the French version::
+
+ Foobar n'est pas mort !
+ #######################
+
+ :slug: foobar-is-not-dead
+ :lang: fr
+
+ Oui oui, foobar est toujours vivant !
+
+Post content quality notwithstanding, you can see that only item in common
+between the two articles is the slug, which is functioning here as an
+identifier. If you'd rather not explicitly define the slug this way, you must
+then instead ensure that the translated article titles are identical, since the
+slug will be auto-generated from the article title.
+
+If you do not want the original version of one specific article to be detected
+by the ``DEFAULT_LANG`` setting, use the ``translation`` metadata to specify
+which posts are translations::
+
+ Foobar is not dead
+ ##################
+
+ :slug: foobar-is-not-dead
+ :lang: en
+ :translation: true
+
+ That's true, foobar is still alive!
+
+
+.. _internal_pygments_options:
+
+Syntax highlighting
+===================
+
+Pelican is able to provide colorized syntax highlighting for your code blocks.
+To do so, you have to use the following conventions inside your content files.
+
+For reStructuredText, use the code-block directive::
+
+ .. code-block:: identifier
+
+
+
+For Markdown, include the language identifier just above the code block,
+indenting both the identifier and code::
+
+ A block of text.
+
+ :::identifier
+
+
+The specified identifier (e.g. ``python``, ``ruby``) should be one that
+appears on the `list of available lexers `_.
+
+When using reStructuredText the following options are available in the
+code-block directive:
+
+============= ============ =========================================
+Option Valid values Description
+============= ============ =========================================
+anchorlinenos N/A If present wrap line numbers in tags.
+classprefix string String to prepend to token class names
+hl_lines numbers List of lines to be highlighted.
+lineanchors string Wrap each line in an anchor using this
+ string and -linenumber.
+linenos string If present or set to "table" output line
+ numbers in a table, if set to
+ "inline" output them inline. "none" means
+ do not output the line numbers for this
+ table.
+linenospecial number If set every nth line will be given the
+ 'special' css class.
+linenostart number Line number for the first line.
+linenostep number Print every nth line number.
+lineseparator string String to print between lines of code,
+ '\n' by default.
+linespans string Wrap each line in a span using this and
+ -linenumber.
+nobackground N/A If set do not output background color for
+ the wrapping element
+nowrap N/A If set do not wrap the tokens at all.
+tagsfile string ctags file to use for name definitions.
+tagurlformat string format for the ctag links.
+============= ============ =========================================
+
+Note that, depending on the version, your Pygments module might not have
+all of these options available. Refer to the *HtmlFormatter* section of the
+`Pygments documentation `_ for more
+details on each of the options.
+
+For example, the following code block enables line numbers, starting at 153,
+and prefixes the Pygments CSS classes with *pgcss* to make the names
+more unique and avoid possible CSS conflicts::
+
+ .. code-block:: identifier
+ :classprefix: pgcss
+ :linenos: table
+ :linenostart: 153
+
+
+
+It is also possible to specify the ``PYGMENTS_RST_OPTIONS`` variable in your
+Pelican settings file to include options that will be automatically applied to
+every code block.
+
+For example, if you want to have line numbers displayed for every code block
+and a CSS prefix you would set this variable to::
+
+ PYGMENTS_RST_OPTIONS = {'classprefix': 'pgcss', 'linenos': 'table'}
+
+If specified, settings for individual code blocks will override the defaults in
+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
+listed on the index page nor on any category or tag page.
+
+.. _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
diff --git a/docs/contribute.rst b/docs/contribute.rst
new file mode 100644
index 00000000..7c117d26
--- /dev/null
+++ b/docs/contribute.rst
@@ -0,0 +1,206 @@
+Contributing and feedback guidelines
+####################################
+
+There are many ways to contribute to Pelican. You can improve the
+documentation, add missing features, and fix bugs (or just report them). You
+can also help out by reviewing and commenting on
+`existing issues `_.
+
+Don't hesitate to fork Pelican and submit an issue or pull request on GitHub.
+When doing so, please adhere to the following guidelines.
+
+.. include:: ../CONTRIBUTING.rst
+
+Setting up the development environment
+======================================
+
+While there are many ways to set up one's development environment, following
+is a method that uses `virtualenv `_. 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::
+
+ $ virtualenv ~/virtualenvs/pelican
+ $ cd ~/virtualenvs/pelican
+ $ . bin/activate
+
+To clone the Pelican source::
+
+ $ git clone https://github.com/getpelican/pelican.git src/pelican
+
+To install the development dependencies::
+
+ $ cd src/pelican
+ $ pip install -r dev_requirements.txt
+
+To install Pelican and its dependencies::
+
+ $ python setup.py develop
+
+Or using ``pip``::
+
+ $ pip install -e .
+
+Building the docs
+=================
+
+If you make changes to the documentation, you should preview your changes
+before committing them::
+
+ $ pip install sphinx
+ $ cd src/pelican/docs
+ $ make html
+
+Open ``_build/html/index.html`` in your browser to preview the documentation.
+
+Running the test suite
+======================
+
+Each time you add a feature, there are two things to do regarding tests:
+check that the existing tests pass, and add tests for the new feature
+or bugfix.
+
+The tests live in ``pelican/tests`` and you can run them using the
+"discover" feature of ``unittest``::
+
+ $ python -m unittest discover
+
+After making your changes and running the tests, you may see a test failure
+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, you can use the following two commands::
+
+ $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \
+ -s samples/pelican.conf.py samples/content/
+ $ LC_ALL=fr_FR.utf8 pelican -o pelican/tests/output/custom_locale/ \
+ -s samples/pelican.conf_FR.py samples/content/
+ $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \
+ samples/content/
+
+Testing on Python 2 and 3
+-------------------------
+
+Testing on Python 3 currently requires some extra steps: installing
+Python 3-compatible versions of dependent packages and plugins.
+
+Tox_ is a useful tool to run tests on both versions. It will install the
+Python 3-compatible version of dependent packages.
+
+.. _Tox: http://testrun.org/tox/latest/
+
+Python 3 development tips
+=========================
+
+Here are some tips that may be useful when doing some code for both Python 2.7
+and Python 3 at the same time:
+
+- Assume every string and literal is unicode (import unicode_literals):
+
+ - 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 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.:
+
+ - ``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,
+ 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.
+
+
+Logging tips
+============
+
+Try to use logging with appropriate levels.
+
+For logging messages that are not repeated, use the usual Python way::
+
+ # at top of file
+ import logging
+ logger = logging.getLogger(__name__)
+
+ # when needed
+ logger.warning("A warning with %s formatting", arg_to_be_formatted)
+
+Do not format log messages yourself. Use ``%s`` formatting in messages and pass
+arguments to logger. This is important, because Pelican logger will preprocess
+some arguments (like Exceptions) for Py2/Py3 compatibility.
+
+Limiting extraneous log messages
+--------------------------------
+
+If the log message can occur several times, you may want to limit the log to
+prevent flooding. In order to do that, use the ``extra`` keyword argument for
+the logging message in the following format::
+
+ logger.warning("A warning with %s formatting", arg_to_be_formatted,
+ extra={'limit_msg': 'A generic message for too many warnings'})
+
+Optionally, you can also set ``'limit_args'`` as a tuple of arguments in
+``extra`` dict if your generic message needs formatting.
+
+Limit is set to ``5``, i.e, first four logs with the same ``'limit_msg'`` are
+outputted normally but the fifth one will be logged using
+``'limit_msg'`` (and ``'limit_args'`` if present). After the fifth,
+corresponding log messages will be ignored.
+
+For example, if you want to log missing resources, use the following code::
+
+ for resource in resources:
+ if resource.is_missing:
+ logger.warning(
+ 'The resource %s is missing', resource.name,
+ extra={'limit_msg': 'Other resources were missing'})
+
+The log messages will be displayed as follows::
+
+ WARNING: The resource prettiest_cat.jpg is missing
+ WARNING: The resource best_cat_ever.jpg is missing
+ WARNING: The resource cutest_cat.jpg is missing
+ WARNING: The resource lolcat.jpg is missing
+ WARNING: Other resources were missing
+
+
+Outputting traceback in the logs
+--------------------------------
+
+If you're logging inside an ``except`` block, you may want to provide the
+traceback information as well. You can do that by setting ``exc_info`` keyword
+argument to ``True`` during logging. However, doing so by default can be
+undesired because tracebacks are long and can be confusing to regular users.
+Try to limit them to ``--debug`` mode like the following::
+
+ try:
+ some_action()
+ except Exception as e:
+ logger.error('Exception occured: %s', e,
+ exc_info=settings.get('DEBUG', False))
diff --git a/docs/faq.rst b/docs/faq.rst
new file mode 100644
index 00000000..630dd3c1
--- /dev/null
+++ b/docs/faq.rst
@@ -0,0 +1,226 @@
+Frequently Asked Questions (FAQ)
+################################
+
+Here are some frequently asked questions about Pelican.
+
+What's the best way to communicate a problem, question, or suggestion?
+======================================================================
+
+Please read our :doc:`feedback guidelines `.
+
+How can I help?
+================
+
+There are several ways to help out. First, you can report any Pelican
+suggestions or problems you might have via IRC or the `issue tracker
+`_. If submitting an issue
+report, please first check the existing issue list (both open and closed) in
+order to avoid submitting a duplicate issue.
+
+If you want to contribute, please fork `the git repository
+ `_, create a new feature branch, make
+your changes, and issue a pull request. Someone will review your changes as
+soon as possible. Please refer to the :doc:`How to Contribute `
+section for more details.
+
+You can also contribute by creating themes and improving the documentation.
+
+Is it mandatory to have a configuration file?
+=============================================
+
+Configuration files are optional and are just an easy way to configure Pelican.
+For basic operations, it's possible to specify options while invoking Pelican
+via the command line. See ``pelican --help`` for more information.
+
+I'm creating my own theme. How do I use Pygments for syntax highlighting?
+=========================================================================
+
+Pygments adds some classes to the generated content. These classes are used by
+themes to style code syntax highlighting via CSS. Specifically, you can
+customize the appearance of your syntax highlighting via the ``.highlight pre``
+class in your theme's CSS file. To see how various styles can be used to render
+Django code, for example, use the style selector drop-down at top-right on the
+`Pygments project demo site `_.
+
+You can use the following example commands to generate a starting CSS file from
+a Pygments built-in style (in this case, "monokai") and then copy the generated
+CSS file to your new theme::
+
+ pygmentize -S monokai -f html -a .highlight > pygment.css
+ cp pygment.css path/to/theme/static/css/
+
+Don't forget to import your ``pygment.css`` file from your main CSS file.
+
+How do I create my own theme?
+==============================
+
+Please refer to :ref:`theming-pelican`.
+
+I want to use Markdown, but I got an error.
+==========================================================================
+
+If you try to generate Markdown content without first installing the Markdown
+library, may see a message that says ``No valid files found in content``.
+Markdown is not a hard dependency for Pelican, so if you have content in
+Markdown format, you will need to explicitly install the Markdown library.
+You can do so by typing the following command, prepending ``sudo`` if
+permissions require it::
+
+ pip install markdown
+
+Can I use arbitrary metadata in my templates?
+==============================================
+
+Yes. For example, to include a modified date in a Markdown post, one could
+include the following at the top of the article::
+
+ Modified: 2012-08-08
+
+For reStructuredText, this metadata should of course be prefixed with a colon::
+
+ :Modified: 2012-08-08
+
+This metadata can then be accessed in templates such as ``article.html`` via::
+
+ {% if article.modified %}
+ Last modified: {{ article.modified }}
+ {% endif %}
+
+If you want to include metadata in templates outside the article context (e.g.,
+``base.html``), the ``if`` statement should instead be::
+
+ {% if article and article.modified %}
+
+How do I assign custom templates on a per-page basis?
+=====================================================
+
+It's as simple as adding an extra line of metadata to any page or article that
+you want to have its own template. For example, this is how it would be handled
+for content in reST format::
+
+ :template: template_name
+
+For content in Markdown format::
+
+ Template: template_name
+
+Then just make sure your theme contains the relevant template file (e.g.
+``template_name.html``).
+
+How can I override the generated URL of a specific page or article?
+===================================================================
+
+Include ``url`` and ``save_as`` metadata in any pages or articles that you want
+to override the generated URL. Here is an example page in reST format::
+
+ Override url/save_as page
+ #########################
+
+ :url: override/url/
+ :save_as: override/url/index.html
+
+With this metadata, the page will be written to ``override/url/index.html``
+and Pelican will use url ``override/url/`` to link to this page.
+
+How can I use a static page as my home page?
+============================================
+
+The override feature mentioned above can be used to specify a static page as
+your home page. The following Markdown example could be stored in
+``content/pages/home.md``::
+
+ Title: Welcome to My Site
+ URL:
+ save_as: index.html
+
+ Thank you for visiting. Welcome!
+
+If the original blog index is still wanted, it can then be saved in a
+different location by setting ``INDEX_SAVE_AS = 'blog_index.html`` for
+the ``''index'`` direct template.
+
+What if I want to disable feed generation?
+==========================================
+
+To disable feed generation, all feed settings should be set to ``None``.
+All but three feed settings already default to ``None``, so if you want to
+disable all feed generation, you only need to specify the following settings::
+
+ FEED_ALL_ATOM = None
+ CATEGORY_FEED_ATOM = None
+ TRANSLATION_FEED_ATOM = None
+ AUTHOR_FEED_ATOM = None
+ AUTHOR_FEED_RSS = None
+
+The word ``None`` should not be surrounded by quotes. Please note that ``None``
+and ``''`` are not the same thing.
+
+I'm getting a warning about feeds generated without SITEURL being set properly
+==============================================================================
+
+`RSS and Atom feeds require all URL links to be absolute
+`_.
+In order to properly generate links in Pelican you will need to set ``SITEURL``
+to the full path of your site.
+
+Feeds are still generated when this warning is displayed, but links within may
+be malformed and thus the feed may not validate.
+
+My feeds are broken since I upgraded to Pelican 3.x
+===================================================
+
+Starting in 3.0, some of the FEED setting names were changed to more explicitly
+refer to the Atom feeds they inherently represent (much like the FEED_RSS
+setting names). Here is an exact list of the renamed settings::
+
+ FEED -> FEED_ATOM
+ TAG_FEED -> TAG_FEED_ATOM
+ CATEGORY_FEED -> CATEGORY_FEED_ATOM
+
+Starting in 3.1, the new feed ``FEED_ALL_ATOM`` has been introduced: this
+feed will aggregate all posts regardless of their language. This setting
+generates ``'feeds/all.atom.xml'`` by default and ``FEED_ATOM`` now defaults to
+``None``. The following feed setting has also been renamed::
+
+ TRANSLATION_FEED -> TRANSLATION_FEED_ATOM
+
+Older themes that referenced the old setting names may not link properly.
+In order to rectify this, please update your theme for compatibility by changing
+the relevant values in your template files. For an example of complete feed
+headers and usage please check out the ``simple`` theme.
+
+Is Pelican only suitable for blogs?
+===================================
+
+No. Pelican can be easily configured to create and maintain any type of static site.
+This may require a little customization of your theme and Pelican configuration.
+For example, if you are building a launch site for your product and do not need
+tags on your site, you could remove the relevant HTML code from your theme.
+You can also disable generation of tag-related pages via::
+
+ TAGS_SAVE_AS = ''
+ TAG_SAVE_AS = ''
+
+Why does Pelican always write all HTML files even with content caching enabled?
+===============================================================================
+
+In order to reliably determine whether the HTML output is different
+before writing it, a large part of the generation environment
+including the template contexts, imported plugins, etc. would have to
+be saved and compared, at least in the form of a hash (which would
+require special handling of unhashable types), because of all the
+possible combinations of plugins, pagination, etc. which may change in
+many different ways. This would require a lot more processing time
+and memory and storage space. Simply writing the files each time is a
+lot faster and a lot more reliable.
+
+However, this means that the modification time of the files changes
+every time, so a ``rsync`` based upload will transfer them even if
+their content hasn't changed. A simple solution is to make ``rsync``
+use the ``--checksum`` option, which will make it compare the file
+checksums in a much faster way than Pelican would.
+
+When only several specific output files are of interest (e.g. when
+working on some specific page or the theme templates), the
+`WRITE_SELECTED` option may help, see
+:ref:`writing_only_selected_content`.
diff --git a/docs/importer.rst b/docs/importer.rst
new file mode 100644
index 00000000..309ca144
--- /dev/null
+++ b/docs/importer.rst
@@ -0,0 +1,114 @@
+.. _import:
+
+Importing an existing site
+##########################
+
+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
+- Dotclear export
+- Posterous API
+- Tumblr API
+- RSS/Atom feed
+
+The conversion from HTML to reStructuredText or Markdown relies on `Pandoc`_.
+For Dotclear, if the source posts are written with Markdown syntax, they will
+not be converted (as Pelican also supports Markdown).
+
+
+Dependencies
+============
+
+``pelican-import`` has some dependencies not required by the rest of Pelican:
+
+- *BeautifulSoup4* and *lxml*, for WordPress and Dotclear import. Can be installed like
+ any other Python package (``pip install BeautifulSoup4 lxml``).
+- *Feedparser*, for feed import (``pip install feedparser``).
+- *Pandoc*, see the `Pandoc site`_ for installation instructions on your
+ operating system.
+
+.. _Pandoc: http://johnmacfarlane.net/pandoc/
+.. _Pandoc site: http://johnmacfarlane.net/pandoc/installing.html
+
+
+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]
+ input|api_token|api_key
+
+Positional arguments
+--------------------
+
+ input The input file to read
+ api_token [Posterous only] api_token can be obtained from http://posterous.com/api/
+ api_key [Tumblr only] api_key can be obtained from http://www.tumblr.com/oauth/apps
+
+Optional arguments
+------------------
+
+ -h, --help Show this help message and exit
+ --wpfile WordPress XML export (default: False)
+ --dotclear Dotclear export (default: False)
+ --posterous Posterous API (default: False)
+ --tumblr Tumblr API (default: False)
+ --feed Feed to parse (default: False)
+ -o OUTPUT, --output OUTPUT
+ Output path (default: output)
+ -m MARKUP, --markup MARKUP
+ Output markup format (supports rst & markdown)
+ (default: rst)
+ --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.
+ --strip-raw Strip raw HTML code that can't be converted to markup
+ such as flash embeds or iframes (wordpress import
+ only) (default: False)
+ --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:
+ False)
+ -e EMAIL, --email=EMAIL
+ Email used to authenticate Posterous API
+ -p PASSWORD, --password=PASSWORD
+ Password used to authenticate Posterous API
+ -b BLOGNAME, --blogname=BLOGNAME
+ Blog name used in Tumblr API
+
+
+Examples
+========
+
+For WordPress::
+
+ $ pelican-import --wpfile -o ~/output ~/posts.xml
+
+For Dotclear::
+
+ $ pelican-import --dotclear -o ~/output ~/backup.txt
+
+for Posterous::
+
+ $ pelican-import --posterous -o ~/output --email= --password=
+
+For Tumblr::
+
+ $ pelican-import --tumblr -o ~/output --blogname=
+
+Tests
+=====
+
+To test the module, one can use sample files:
+
+- for WordPress: http://wpcandy.com/made/the-sample-post-collection
+- for Dotclear: http://themes.dotaddict.org/files/public/downloads/lorem-backup.txt
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..ccfb9982
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,85 @@
+Pelican |release|
+=================
+
+
+.. ifconfig:: release.endswith('.dev')
+
+ .. warning::
+
+ This documentation is for the version of Pelican currently under development.
+ Were you looking for version |last_stable| documentation?
+
+
+Pelican is a static site generator, written in Python_. Highlights include:
+
+* Write your content directly with your editor of choice
+ in reStructuredText_ or Markdown_ formats
+* Includes a simple CLI tool to (re)generate your site
+* Easy to interface with distributed version control systems and web hooks
+* Completely static output is easy to host anywhere
+
+Ready to get started? Check out the :doc:`Quickstart` guide.
+
+Features
+--------
+
+Pelican |version| currently supports:
+
+* Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact")
+* Comments, via an external service (Disqus). If you prefer to have more
+ control over your comment data, self-hosted comments are another option.
+ Check out the `Pelican Plugins`_ repository for more details.
+* Theming support (themes are created using Jinja2_ templates)
+* Publication of articles in multiple languages
+* Atom/RSS feeds
+* Code syntax highlighting
+* Import from WordPress, Dotclear, or RSS feeds
+* Integration with external tools: Twitter, Google Analytics, etc. (optional)
+* Fast rebuild times thanks to content caching and selective output writing
+
+Why the name "Pelican"?
+-----------------------
+
+"Pelican" is an anagram for *calepin*, which means "notebook" in French. ;)
+
+Source code
+-----------
+
+You can access the source code at: https://github.com/getpelican/pelican
+
+How to get help, contribute, or provide feedback
+------------------------------------------------
+
+See our :doc:`feedback and contribution submission guidelines `.
+
+Documentation
+-------------
+
+.. toctree::
+ :maxdepth: 2
+
+ quickstart
+ install
+ content
+ publish
+ settings
+ themes
+ plugins
+ pelican-themes
+ importer
+ faq
+ tips
+ contribute
+ internals
+ report
+ changelog
+
+.. Links
+
+.. _Python: http://www.python.org/
+.. _reStructuredText: http://docutils.sourceforge.net/rst.html
+.. _Markdown: http://daringfireball.net/projects/markdown/
+.. _Jinja2: http://jinja.pocoo.org/
+.. _`Pelican documentation`: http://docs.getpelican.com/latest/
+.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
+.. _`Pelican plugins`: https://github.com/getpelican/pelican-plugins
diff --git a/docs/install.rst b/docs/install.rst
new file mode 100644
index 00000000..418c8ca6
--- /dev/null
+++ b/docs/install.rst
@@ -0,0 +1,117 @@
+Installing Pelican
+##################
+
+Pelican currently runs best on Python 2.7.x; earlier versions of Python are
+not supported. There is provisional support for Python 3.3+, although there may
+be rough edges, particularly with regards to optional 3rd-party components.
+
+You can install Pelican via several different methods. The simplest is via
+`pip `_::
+
+ pip install pelican
+
+(Keep in mind that operating systems will often require you to prefix the above
+command with ``sudo`` in order to install Pelican system-wide.)
+
+While the above is the simplest method, the recommended approach is to create
+a virtual environment for Pelican via virtualenv_ before installing Pelican.
+Assuming you have virtualenv_ installed, you can then open a new terminal
+session and create a new virtual environment for Pelican::
+
+ virtualenv ~/virtualenvs/pelican
+ cd ~/virtualenvs/pelican
+ source bin/activate
+
+Once the virtual environment has been created and activated, Pelican can be
+be installed via ``pip install pelican`` as noted above. Alternatively, if
+you have the project source, you can install Pelican using the distutils
+method::
+
+ cd path-to-Pelican-source
+ python setup.py install
+
+If you have Git installed and prefer to install the latest bleeding-edge
+version of Pelican rather than a stable release, use the following command::
+
+ pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican"
+
+Once Pelican is installed, you can run ``pelican --help`` to see basic usage
+options. For more detail, refer to the :doc:`Publish` section.
+
+Optional packages
+-----------------
+
+If you plan on using `Markdown `_ as a
+markup format, you'll need to install the Markdown library::
+
+ pip install Markdown
+
+Typographical enhancements can be enabled in your settings file, but first the
+requisite `Typogrify `_ library must be
+installed::
+
+ pip install typogrify
+
+Dependencies
+------------
+
+When Pelican is installed, the following dependent Python packages should be
+automatically installed without any action on your part:
+
+* `feedgenerator `_, to generate the
+ Atom feeds
+* `jinja2 `_, for templating support
+* `pygments `_, for syntax highlighting
+* `docutils `_, for supporting
+ reStructuredText as an input format
+* `pytz `_, for timezone definitions
+* `blinker `_, an object-to-object and
+ broadcast signaling system
+* `unidecode `_, for ASCII
+ transliterations of Unicode text
+* `six `_, for Python 2 and 3 compatibility
+ utilities
+* `MarkupSafe `_, for a markup safe
+ string implementation
+* `python-dateutil `_, to read
+ the date metadata
+
+Upgrading
+---------
+
+If you installed a stable Pelican release via ``pip`` or ``easy_install`` and
+wish to upgrade to the latest stable release, you can do so by adding
+``--upgrade`` to the relevant command. For pip, that would be::
+
+ pip install --upgrade pelican
+
+If you installed Pelican via distutils or the bleeding-edge method, simply
+perform the same step to install the most recent version.
+
+Kickstart your site
+===================
+
+Once Pelican has been installed, you can create a skeleton project via the
+``pelican-quickstart`` command, which begins by asking some questions about
+your site::
+
+ pelican-quickstart
+
+Once you finish answering all the questions, your project will consist of the
+following hierarchy (except for *pages* — shown in parentheses below — which you
+can optionally add yourself if you plan to create non-chronological content)::
+
+ yourproject/
+ ├── content
+ │ └── (pages)
+ ├── output
+ ├── develop_server.sh
+ ├── fabfile.py
+ ├── Makefile
+ ├── pelicanconf.py # Main settings file
+ └── publishconf.py # Settings to use when ready to publish
+
+The next step is to begin to adding content to the *content* folder that has
+been created for you.
+
+.. _virtualenv: http://www.virtualenv.org/
diff --git a/docs/internals.rst b/docs/internals.rst
new file mode 100644
index 00000000..303a327f
--- /dev/null
+++ b/docs/internals.rst
@@ -0,0 +1,90 @@
+Pelican internals
+#################
+
+This section describe how Pelican works internally. As you'll see, it's
+quite simple, but a bit of documentation doesn't hurt. :)
+
+You can also find in the :doc:`report` section an excerpt of a report the
+original author wrote with some software design information.
+
+.. _report: :doc:`report`
+
+Overall structure
+=================
+
+What Pelican does is take a list of files and process them into some sort of
+output. Usually, the input files are reStructuredText and Markdown
+files, and the output is a blog, but both input and output can be anything you
+want.
+
+The logic is separated into different classes and concepts:
+
+* **Writers** are responsible for writing files: .html files, RSS feeds, and so
+ on. Since those operations are commonly used, the object is created once and
+ then passed to the generators.
+
+* **Readers** are used to read from various formats (HTML, Markdown and
+ reStructuredText for now, but the system is extensible). Given a file, they
+ return metadata (author, tags, category, etc.) and content (HTML-formatted).
+
+* **Generators** generate the different outputs. For instance, Pelican comes with
+ ``ArticlesGenerator`` and ``PageGenerator``. Given a configuration, they can do
+ whatever they want. Most of the time, it's generating files from inputs.
+
+* Pelican also uses templates, so it's easy to write your own theme. The
+ syntax is `Jinja2 `_ and is very easy to learn, so
+ don't hesitate to jump in and build your own theme.
+
+How to implement a new reader?
+==============================
+
+Is there an awesome markup language you want to add to Pelican?
+Well, the only thing you have to do is to create a class with a ``read``
+method that returns HTML content and some metadata.
+
+Take a look at the Markdown reader::
+
+ class MarkdownReader(BaseReader):
+ enabled = bool(Markdown)
+
+ def read(self, source_path):
+ """Parse content and metadata of markdown files"""
+ text = pelican_open(source_path)
+ md = Markdown(extensions = ['meta', 'codehilite'])
+ content = md.convert(text)
+
+ metadata = {}
+ for name, value in md.Meta.items():
+ name = name.lower()
+ meta = self.process_metadata(name, value[0])
+ metadata[name] = meta
+ return content, metadata
+
+Simple, isn't it?
+
+If your new reader requires additional Python dependencies, then you should wrap
+their ``import`` statements in a ``try...except`` block. Then inside the reader's
+class, set the ``enabled`` class attribute to mark import success or failure.
+This makes it possible for users to continue using their favourite markup method
+without needing to install modules for formats they don't use.
+
+How to implement a new generator?
+=================================
+
+Generators have two important methods. You're not forced to create
+both; only the existing ones will be called.
+
+* ``generate_context``, that is called first, for all the generators.
+ Do whatever you have to do, and update the global context if needed. This
+ context is shared between all generators, and will be passed to the
+ templates. For instance, the ``PageGenerator`` ``generate_context`` method
+ finds all the pages, transforms them into objects, and populates the context
+ with them. Be careful *not* to output anything using this context at this
+ stage, as it is likely to change by the effect of other generators.
+
+* ``generate_output`` is then called. And guess what is it made for? Oh,
+ generating the output. :) It's here that you may want to look at the context
+ and call the methods of the ``writer`` object that is passed as the first
+ argument of this function. In the ``PageGenerator`` example, this method will
+ look at all the pages recorded in the global context and output a file on
+ the disk (using the writer method ``write_file``) for each page encountered.
diff --git a/docs/pelican-themes.rst b/docs/pelican-themes.rst
new file mode 100644
index 00000000..7090c648
--- /dev/null
+++ b/docs/pelican-themes.rst
@@ -0,0 +1,155 @@
+pelican-themes
+##############
+
+
+
+Description
+===========
+
+``pelican-themes`` is a command line tool for managing themes for Pelican.
+
+
+Usage
+"""""
+
+| pelican-themes [-h] [-l] [-i theme path [theme path ...]]
+| [-r theme name [theme name ...]]
+| [-s theme path [theme path ...]] [-v] [--version]
+
+Optional arguments:
+"""""""""""""""""""
+
+
+-h, --help Show the help an exit
+
+-l, --list Show the themes already installed
+
+-i theme_path, --install theme_path One or more themes to install
+
+-r theme_name, --remove theme_name One or more themes to remove
+
+-s theme_path, --symlink theme_path Same as "--install", but create a symbolic link instead of copying the theme.
+ Useful for theme development
+
+-v, --verbose Verbose output
+
+--version Print the version of this script
+
+
+
+Examples
+========
+
+
+Listing the installed themes
+""""""""""""""""""""""""""""
+
+With ``pelican-themes``, you can see the available themes by using the ``-l`` or ``--list`` option:
+
+.. code-block:: console
+
+ $ pelican-themes -l
+ notmyidea
+ two-column@
+ simple
+ $ pelican-themes --list
+ notmyidea
+ two-column@
+ simple
+
+In this example, we can see there are three themes available: ``notmyidea``, ``simple``, and ``two-column``.
+
+``two-column`` is prefixed with an ``@`` because this theme is not copied to the Pelican theme path, but is instead just linked to it (see `Creating symbolic links`_ for details about creating symbolic links).
+
+Note that you can combine the ``--list`` option with the ``-v`` or ``--verbose`` option to get more verbose output, like this:
+
+.. code-block:: console
+
+ $ pelican-themes -v -l
+ /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/notmyidea
+ /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/two-column (symbolic link to `/home/skami/Dev/Python/pelican-themes/two-column')
+ /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/simple
+
+
+Installing themes
+"""""""""""""""""
+
+You can install one or more themes using the ``-i`` or ``--install`` option.
+This option takes as argument the path(s) of the theme(s) you want to install, and can be combined with the verbose option:
+
+.. code-block:: console
+
+ # pelican-themes --install ~/Dev/Python/pelican-themes/notmyidea-cms --verbose
+
+.. code-block:: console
+
+ # pelican-themes --install ~/Dev/Python/pelican-themes/notmyidea-cms\
+ ~/Dev/Python/pelican-themes/martyalchin \
+ --verbose
+
+.. code-block:: console
+
+ # pelican-themes -vi ~/Dev/Python/pelican-themes/two-column
+
+
+Removing themes
+"""""""""""""""
+
+The ``pelican-themes`` command can also remove themes from the Pelican themes path.
+The ``-r`` or ``--remove`` option takes as argument the name(s) of the theme(s) you want to remove, and can be combined with the ``--verbose`` option.
+
+.. code-block:: console
+
+ # pelican-themes --remove two-column
+
+.. code-block:: console
+
+ # pelican-themes -r martyachin notmyidea-cmd -v
+
+
+
+
+
+Creating symbolic links
+"""""""""""""""""""""""
+
+``pelican-themes`` can also install themes by creating symbolic links instead of copying entire themes into the Pelican themes path.
+
+To symbolically link a theme, you can use the ``-s`` or ``--symlink``, which works exactly as the ``--install`` option:
+
+.. code-block:: console
+
+ # pelican-themes --symlink ~/Dev/Python/pelican-themes/two-column
+
+In this example, the ``two-column`` theme is now symbolically linked to the Pelican themes path, so we can use it, but we can also modify it without having to reinstall it after each modification.
+
+This is useful for theme development:
+
+.. code-block:: console
+
+ $ sudo pelican-themes -s ~/Dev/Python/pelican-themes/two-column
+ $ pelican ~/Blog/content -o /tmp/out -t two-column
+ $ firefox /tmp/out/index.html
+ $ vim ~/Dev/Pelican/pelican-themes/two-coumn/static/css/main.css
+ $ pelican ~/Blog/content -o /tmp/out -t two-column
+ $ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-coumn/static/img/bg.png
+ $ pelican ~/Blog/content -o /tmp/out -t two-column
+ $ vim ~/Dev/Pelican/pelican-themes/two-coumn/templates/index.html
+ $ pelican ~/Blog/content -o /tmp/out -t two-column
+
+
+
+Doing several things at once
+""""""""""""""""""""""""""""
+
+The ``--install``, ``--remove`` and ``--symlink`` option are not mutually exclusive, so you can combine them in the same command line to do more than one operation at time, like this:
+
+
+.. code-block:: console
+
+ # pelican-themes --remove notmyidea-cms two-column \
+ --install ~/Dev/Python/pelican-themes/notmyidea-cms-fr \
+ --symlink ~/Dev/Python/pelican-themes/two-column \
+ --verbose
+
+In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr``
diff --git a/docs/plugins.rst b/docs/plugins.rst
new file mode 100644
index 00000000..27299e83
--- /dev/null
+++ b/docs/plugins.rst
@@ -0,0 +1,210 @@
+.. _plugins:
+
+Plugins
+#######
+
+Beginning with version 3.0, Pelican supports plugins. Plugins are a way to add
+features to Pelican without having to directly modify the Pelican core.
+
+How to use plugins
+==================
+
+To load plugins, you have to specify them in your settings file. There are two
+ways to do so. The first method is to specify strings with the path to the
+callables::
+
+ PLUGINS = ['package.myplugin',]
+
+Alternatively, another method is to import them and add them to the list::
+
+ from package import myplugin
+ PLUGINS = [myplugin,]
+
+If your plugins are not in an importable path, you can specify a list of paths
+via the ``PLUGIN_PATHS`` setting. As shown in the following example, paths in
+the ``PLUGIN_PATHS`` list can be absolute or relative to the settings file::
+
+ PLUGIN_PATHS = ["plugins", "/srv/pelican/plugins"]
+ PLUGINS = ["assets", "liquid_tags", "sitemap"]
+
+Where to find plugins
+=====================
+
+We maintain a separate repository of plugins for people to share and use.
+Please visit the `pelican-plugins`_ repository for a list of available plugins.
+
+.. _pelican-plugins: https://github.com/getpelican/pelican-plugins
+
+Please note that while we do our best to review and maintain these plugins,
+they are submitted by the Pelican community and thus may have varying levels of
+support and interoperability.
+
+How to create plugins
+=====================
+
+Plugins are based on the concept of signals. Pelican sends signals, and plugins
+subscribe to those signals. The list of signals are defined in a subsequent
+section.
+
+The only rule to follow for plugins is to define a ``register`` callable, in
+which you map the signals to your plugin logic. Let's take a simple example::
+
+ from pelican import signals
+
+ def test(sender):
+ print "%s initialized !!" % sender
+
+ def register():
+ signals.initialized.connect(test)
+
+List of signals
+===============
+
+Here is the list of currently implemented signals:
+
+================================= ============================ ===========================================================================
+Signal Arguments Description
+================================= ============================ ===========================================================================
+initialized pelican object
+finalized pelican object invoked after all the generators are executed and just before pelican exits
+ useful for custom post processing actions, such as:
+ - minifying js/css assets.
+ - notify/ping search engines with an updated sitemap.
+generator_init generator invoked in the Generator.__init__
+readers_init readers invoked in the Readers.__init__
+article_generator_context article_generator, metadata
+article_generator_preread article_generator invoked before a article is read in ArticlesGenerator.generate_context;
+ use if code needs to do something before every article is parsed
+article_generator_init article_generator invoked in the ArticlesGenerator.__init__
+article_generator_pretaxonomy article_generator invoked before categories and tags lists are created
+ useful when e.g. modifying the list of articles to be generated
+ so that removed articles are not leaked in categories or tags
+article_generator_finalized article_generator invoked at the end of ArticlesGenerator.generate_context
+article_generator_write_article article_generator, content invoked before writing each article, the article is passed as content
+article_writer_finalized article_generator, writer invoked after all articles and related pages have been written, but before
+ the article generator is closed.
+get_generators pelican object invoked in Pelican.get_generator_classes,
+ can return a Generator, or several
+ generators in a tuple or in a list.
+get_writer pelican object invoked in Pelican.get_writer,
+ can return a custom Writer.
+page_generator_context page_generator, metadata
+page_generator_preread page_generator invoked before a page is read in PageGenerator.generate_context;
+ 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
+static_generator_context static_generator, metadata
+static_generator_preread static_generator invoked before a static file is read in StaticGenerator.generate_context;
+ use if code needs to do something before every static file is added to the
+ staticfiles list.
+static_generator_init static_generator invoked in the StaticGenerator.__init__
+static_generator_finalized static_generator invoked at the end of StaticGenerator.generate_context
+content_object_init content_object invoked at the end of Content.__init__ (see note below)
+content_written path, context invoked each time a content file is written.
+feed_written path, context, feed invoked each time a feed file is written.
+================================= ============================ ===========================================================================
+
+The list is currently small, so don't hesitate to add signals and make a pull
+request if you need them!
+
+.. note::
+
+ The signal ``content_object_init`` can send a different type of object as
+ the argument. If you want to register only one type of object then you will
+ need to specify the sender when you are connecting to the signal.
+
+ ::
+
+ from pelican import signals
+ from pelican import contents
+
+ def test(sender, instance):
+ print "%s : %s content initialized !!" % (sender, instance)
+
+ def register():
+ signals.content_object_init.connect(test, sender=contents.Article)
+
+.. note::
+
+ After Pelican 3.2, signal names were standardized. Older plugins
+ may need to be updated to use the new names:
+
+ ========================== ===========================
+ Old name New name
+ ========================== ===========================
+ article_generate_context article_generator_context
+ article_generate_finalized article_generator_finalized
+ article_generate_preread article_generator_preread
+ pages_generate_context page_generator_context
+ pages_generate_preread page_generator_preread
+ pages_generator_finalized page_generator_finalized
+ pages_generator_init page_generator_init
+ static_generate_context static_generator_context
+ static_generate_preread static_generator_preread
+ ========================== ===========================
+
+Recipes
+=======
+
+We eventually realised some of the recipes to create plugins would be best
+shared in the documentation somewhere, so here they are!
+
+How to create a new reader
+--------------------------
+
+One thing you might want is to add support for your very own input format.
+While it might make sense to add this feature in Pelican core, we
+wisely chose to avoid this situation and instead have the different readers
+defined via plugins.
+
+The rationale behind this choice is mainly that plugins are really easy to
+write and don't slow down Pelican itself when they're not active.
+
+No more talking — here is an example::
+
+ from pelican import signals
+ from pelican.readers import BaseReader
+
+ # Create a new reader class, inheriting from the pelican.reader.BaseReader
+ class NewReader(BaseReader):
+ enabled = True # Yeah, you probably want that :-)
+
+ # The list of file extensions you want this reader to match with.
+ # If multiple readers were to use the same extension, the latest will
+ # win (so the one you're defining here, most probably).
+ file_extensions = ['yeah']
+
+ # You need to have a read method, which takes a filename and returns
+ # some content and the associated metadata.
+ def read(self, filename):
+ metadata = {'title': 'Oh yeah',
+ 'category': 'Foo',
+ 'date': '2012-12-01'}
+
+ parsed = {}
+ for key, value in metadata.items():
+ parsed[key] = self.process_metadata(key, value)
+
+ return "Some content", parsed
+
+ def add_reader(readers):
+ readers.reader_classes['yeah'] = NewReader
+
+ # This is how pelican works.
+ def register():
+ signals.readers_init.connect(add_reader)
+
+
+Adding a new generator
+----------------------
+
+Adding a new generator is also really easy. You might want to have a look at
+:doc:`internals` for more information on how to create your own generator.
+
+::
+
+ def get_generators(pelican_object):
+ # define a new generator here if you need to
+ return MyGenerator
+
+ signals.get_generators.connect(get_generators)
diff --git a/docs/publish.rst b/docs/publish.rst
new file mode 100644
index 00000000..266009e4
--- /dev/null
+++ b/docs/publish.rst
@@ -0,0 +1,174 @@
+Publish your site
+#################
+
+Site generation
+===============
+
+Once Pelican is installed and you have some content (e.g., in Markdown or reST
+format), you can convert your content into HTML via the ``pelican`` command,
+specifying the path to your content and (optionally) the path to your
+:doc:`settings` file::
+
+ pelican /path/to/your/content/ [-s path/to/your/settings.py]
+
+The above command will generate your site and save it in the ``output/``
+folder, using the default theme to produce a simple site. The default theme
+consists of very simple HTML without styling and is provided so folks may use
+it as a basis for creating their own themes.
+
+You can also tell Pelican to watch for your modifications, instead of
+manually re-running it every time you want to see your changes. To enable this,
+run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
+
+Pelican has other command-line switches available. Have a look at the help to
+see all the options you can use::
+
+ pelican --help
+
+Viewing the generated files
+---------------------------
+
+The files generated by Pelican are static files, so you don't actually need
+anything special to view them. You can use your browser to open the generated
+HTML files directly::
+
+ firefox output/index.html
+
+Because the above method may have trouble locating your CSS and other linked
+assets, running a simple web server using Python will often provide a more
+reliable previewing experience::
+
+ cd output
+ python -m SimpleHTTPServer
+
+Once the ``SimpleHTTPServer`` has been started, you can preview your site at
+http://localhost:8000/
+
+Deployment
+==========
+
+After you have generated your site, previewed it in your local development
+environment, and are ready to deploy it to production, you might first
+re-generate your site with any production-specific settings (e.g., analytics
+feeds, etc.) that you may have defined::
+
+ pelican content -s publishconf.py
+
+The steps for deploying your site will depend on where it will be hosted.
+If you have SSH access to a server running Nginx or Apache, you might use the
+``rsync`` tool to transmit your site files::
+
+ rsync --avc --delete output/ host.example.com:/var/www/your-site/
+
+There are many other deployment options, some of which can be configured when
+first setting up your site via the ``pelican-quickstart`` command. See the
+:doc:`Tips` page for detail on publishing via GitHub Pages.
+
+Automation
+==========
+
+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
+``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
+should be customized to fit your particular needs and usage patterns. If you
+find one or both of these automation tools to be of limited utility, these
+files can deleted at any time and will not affect usage of the canonical
+``pelican`` command.
+
+Following are automation tools that "wrap" the ``pelican`` command and can
+simplify the process of generating, previewing, and uploading your site.
+
+Fabric
+------
+
+The advantage of Fabric_ 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
+``sudo`` if your environment requires it::
+
+ pip install Fabric
+
+Take a moment to open the ``fabfile.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
+
+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
+
+To serve the generated site so it can be previewed in your browser at
+http://localhost:8000/::
+
+ fab 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
+
+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
+needs and preferences.
+
+Make
+----
+
+A ``Makefile`` is also automatically created for you when you say "yes" to
+the relevant question during the ``pelican-quickstart`` process. The advantage
+of this method is that the ``make`` command is built into most POSIX systems
+and thus doesn't require installing anything else in order to use it. The
+downside is that non-POSIX systems (e.g., Windows) do not include ``make``,
+and installing it on those systems can be a non-trivial task.
+
+If you want to use ``make`` to generate your site, run::
+
+ make html
+
+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::
+
+ make regenerate
+
+To serve the generated site so it can be previewed in your browser at
+http://localhost:8000/::
+
+ make serve
+
+Normally you would need to run ``make regenerate`` and ``make serve`` in two
+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
+
+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
+use rsync over ssh::
+
+ make rsync_upload
+
+That's it! Your site should now be live.
+
+(The default ``Makefile`` and ``devserver.sh`` scripts use the ``python`` and
+``pelican`` executables to complete its tasks. If you want to use different
+executables, such as ``python3``, you can set the ``PY`` and ``PELICAN``
+environment variables, respectively, to override the default executable names.)
+
+.. _Fabric: http://fabfile.org/
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 00000000..fb90b002
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,73 @@
+Quickstart
+##########
+
+Reading through all the documentation is highly recommended, but for the truly
+impatient, following are some quick steps to get started.
+
+Installation
+------------
+
+Install Pelican on Python 2.7.x or Python 3.3+ by running the following command
+in your preferred terminal, prefixing with ``sudo`` if permissions warrant::
+
+ pip install pelican markdown
+
+Create a project
+----------------
+
+First, choose a name for your project, create an appropriately-named directory
+for your site, and switch to that directory::
+
+ mkdir -p ~/projects/yoursite
+ cd ~/projects/yoursite
+
+Create a skeleton project via the ``pelican-quickstart`` command, which begins
+by asking some questions about your site::
+
+ pelican-quickstart
+
+For questions that have default values denoted in brackets, feel free to use
+the Return key to accept those default values. When asked for your URL prefix,
+enter your domain name as indicated (e.g., ``http://example.com``).
+
+Create an article
+-----------------
+
+You cannot run Pelican until you have created some content. Use your preferred
+text editor to create your first article with the following content::
+
+ Title: My First Review
+ Date: 2010-12-03 10:20
+ Category: Review
+
+ Following is a review of my favorite mechanical keyboard.
+
+Given that this example article is in Markdown format, save it as
+``~/projects/yoursite/content/keyboard-review.md``.
+
+Generate your site
+------------------
+
+From your project directory, run the ``pelican`` command to generate your site::
+
+ pelican content
+
+Your site has now been generated inside the ``output`` directory. (You may see a
+warning related to feeds, but that is normal when developing locally and can be
+ignored for now.)
+
+Preview your site
+-----------------
+
+Open a new terminal session and run the following commands to switch to your
+``output`` directory and launch Python's built-in web server::
+
+ cd ~/projects/yoursite/output
+ python -m SimpleHTTPServer
+
+Preview your site by navigating to http://localhost:8000/ in your browser.
+
+Continue reading the other documentation sections for more detail, and check out
+the Pelican wiki's Tutorials_ page for links to community-published tutorials.
+
+.. _Tutorials: https://github.com/getpelican/pelican/wiki/Tutorials
diff --git a/docs/report.rst b/docs/report.rst
new file mode 100644
index 00000000..f3ddff31
--- /dev/null
+++ b/docs/report.rst
@@ -0,0 +1,121 @@
+Some history about Pelican
+##########################
+
+.. warning::
+
+ This page comes from a report the original author (Alexis Métaireau) wrote
+ right after writing Pelican, in December 2010. The information may not be
+ up-to-date.
+
+Pelican is a simple static blog generator. It parses markup files
+(Markdown or reStructuredText for now) and generates an HTML folder
+with all the files in it.
+I've chosen to use Python to implement Pelican because it seemed to
+be simple and to fit to my needs. I did not wanted to define a class for
+each thing, but still wanted to keep my things loosely coupled.
+It turns out that it was exactly what I wanted. From time to time,
+thanks to the feedback of some users, it took me a very few time to
+provide fixes on it. So far, I've re-factored the Pelican code by two
+times; each time took less than 30 minutes.
+
+Use case
+========
+
+I was previously using WordPress, a solution you can host on a web
+server to manage your blog. Most of the time, I prefer using markup
+languages such as Markdown or reStructuredText to type my articles.
+To do so, I use vim. I think it is important to let the people choose the
+tool they want to write the articles. In my opinion, a blog manager
+should just allow you to take any kind of input and transform it to a
+weblog. That's what Pelican does.
+You can write your articles using the tool you want, and the markup
+language you want, and then generate a static HTML weblog.
+
+.. image:: _static/overall.png
+
+To be flexible enough, Pelican has template support, so you can easily write
+your own themes if you want to.
+
+Design process
+==============
+
+Pelican came from a need I have. I started by creating a single file
+application, and I have make it grow to support what it does by now.
+To start, I wrote a piece of documentation about what I wanted to do.
+Then, I created the content I wanted to parse (the reStructuredText files)
+and started experimenting with the code. Pelican was 200 lines long and
+contained almost ten functions and one class when it was first usable.
+
+I have been facing different problems all over the time and wanted to
+add features to Pelican while using it. The first change I have done was
+to add the support of a settings file. It is possible to pass the options to
+the command line, but can be tedious if there is a lot of them.
+In the same way, I have added the support of different things over
+time: Atom feeds, multiple themes, multiple markup support, etc.
+At some point, it appears that the "only one file" mantra was not good
+enough for Pelican, so I decided to rework a bit all that, and split this in
+multiple different files.
+
+I’ve separated the logic in different classes and concepts:
+
+* *writers* are responsible of all the writing process of the files.
+ They are responsible of writing .html files, RSS feeds and so on.
+ Since those operations are commonly used, the object is created
+ once, and then passed to the generators.
+
+* *readers* are used to read from various formats (Markdown and
+ reStructuredText for now, but the system is extensible). Given a
+ file, they return metadata (author, tags, category, etc) and
+ content (HTML formatted).
+
+* *generators* generate the different outputs. For instance, Pelican
+ comes with an ArticlesGenerator and PagesGenerator, into
+ others. Given a configuration, they can do whatever you want
+ them to do. Most of the time it's generating files from inputs
+ (user inputs and files).
+
+I also deal with contents objects. They can be ``Articles``, ``Pages``,
+``Quotes``, or whatever you want. They are defined in the ``contents.py``
+module and represent some content to be used by the program.
+
+In more detail
+==============
+
+Here is an overview of the classes involved in Pelican.
+
+.. image:: _static/uml.jpg
+
+The interface does not really exist, and I have added it only to clarify the
+whole picture. I do use duck typing and not interfaces.
+
+Internally, the following process is followed:
+
+* First of all, the command line is parsed, and some content from
+ the user is used to initialize the different generator objects.
+
+* A ``context`` is created. It contains the settings from the command
+ line and a settings file if provided.
+* The ``generate_context`` method of each generator is called, updating
+ the context.
+
+* The writer is created and given to the ``generate_output`` method of
+ each generator.
+
+I make two calls because it is important that when the output is
+generated by the generators, the context will not change. In other
+words, the first method ``generate_context`` should modify the context,
+whereas the second ``generate_output`` method should not.
+
+Then, it is up to the generators to do what the want, in the
+``generate_context`` and ``generate_content`` method.
+Taking the ``ArticlesGenerator`` class will help to understand some others
+concepts. Here is what happens when calling the ``generate_context``
+method:
+
+* Read the folder “path”, looking for restructured text files, load
+ each of them, and construct a content object (``Article``) with it. To do so,
+ use ``Reader`` objects.
+* Update the ``context`` with all those articles.
+
+Then, the ``generate_content`` method uses the ``context`` and the ``writer`` to
+generate the wanted output.
diff --git a/docs/settings.rst b/docs/settings.rst
new file mode 100644
index 00000000..7192a0a6
--- /dev/null
+++ b/docs/settings.rst
@@ -0,0 +1,860 @@
+Settings
+########
+
+Pelican is configurable thanks to a settings file you can pass to
+the command line::
+
+ pelican content -s path/to/your/settingsfile.py
+
+(If you used the ``pelican-quickstart`` command, your primary settings file will
+be named ``pelicanconf.py`` by default.)
+
+Settings are configured in the form of a Python module (a file). There is an
+`example settings file
+`_
+available for reference.
+
+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
+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.
+
+Here is a list of settings for Pelican:
+
+Basic settings
+==============
+
+=============================================================================== =====================================================================
+Setting name (followed by default value, if any) What does it do?
+=============================================================================== =====================================================================
+``AUTHOR`` Default author (put your name)
+``DATE_FORMATS = {}`` If you manage multiple languages, you can set the date formatting
+ here. See the "Date format and locale" section below for details.
+``USE_FOLDER_AS_CATEGORY = True`` When you don't specify a category in your post metadata, set this
+ setting to ``True``, and organize your articles in subfolders, the
+ subfolder will become the category of your post. If set to ``False``,
+ ``DEFAULT_CATEGORY`` will be used as a fallback.
+``DEFAULT_CATEGORY = 'misc'`` The default category to fall back on.
+``DEFAULT_DATE_FORMAT = '%a %d %B %Y'`` The default date format you want to use.
+``DISPLAY_PAGES_ON_MENU = True`` Whether to display pages on the menu of the
+ template. Templates may or may not honor this
+ setting.
+``DISPLAY_CATEGORIES_ON_MENU = True`` Whether to display categories on the menu of the
+ template. Templates may or not honor this
+ setting.
+``DEFAULT_DATE = None`` The default date you want to use.
+ If ``fs``, Pelican will use the file system
+ timestamp information (mtime) if it can't get
+ date information from the metadata.
+ If set to a tuple object, the default datetime object will instead
+ be generated by passing the tuple to the
+ ``datetime.datetime`` constructor.
+``DEFAULT_METADATA = ()`` The default metadata you want to use for all articles
+ and pages.
+``DOCUTILS_SETTINGS = {}`` Extra configuration settings for the docutils publisher
+ (applicable only to reStructuredText). See `Docutils
+ Configuration`_ settings for more details.
+
+``FILENAME_METADATA =`` ``'(?P\d{4}-\d{2}-\d{2}).*'`` The regexp that will be used to extract any metadata
+ from the filename. All named groups that are matched
+ will be set in the metadata object.
+ The default value will only extract the date from
+ the filename.
+ For example, if you would like to extract both the
+ date and the slug, you could set something like:
+ ``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``.
+ See :ref:`path_metadata` and ``SLUGIFY_SOURCE``.
+``PATH_METADATA = ''`` Like ``FILENAME_METADATA``, but parsed from a page's
+ full path relative to the content source directory.
+ See :ref:`path_metadata`.
+``EXTRA_PATH_METADATA = {}`` Extra metadata dictionaries keyed by relative path. Relative paths
+ require correct OS-specific directory separators (i.e. / in UNIX and
+ \\ in Windows) unlike some other Pelican file settings.
+ See :ref:`path_metadata`.
+``DELETE_OUTPUT_DIRECTORY = False`` Delete the output directory, and **all** of its contents, before
+ generating new files. This can be useful in preventing older,
+ unnecessary files from persisting in your output. However, **this is
+ a destructive setting and should be handled with extreme care.**
+``OUTPUT_RETENTION = ()`` A tuple of filenames that should be retained and not deleted from the
+ output directory. One use case would be the preservation of version
+ control data. For example: ``(".hg", ".git", ".bzr")``
+``JINJA_EXTENSIONS = []`` A list of any Jinja2 extensions you want to use.
+``JINJA_FILTERS = {}`` A list of custom Jinja2 filters you want to use.
+ The dictionary should map the filtername to the filter function.
+ For example: ``{'urlencode': urlencode_filter}``
+ See `Jinja custom filters documentation`_.
+``LOCALE`` [#]_ Change the locale. A list of locales can be provided
+ here or a single string representing one locale.
+ When providing a list, all the locales will be tried
+ until one works.
+``LOG_FILTER = []`` A list of tuples containing the logging level (up to ``warning``)
+ and the message to be ignored.
+ For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]``
+``READERS = {}`` A dictionary of file extensions / Reader classes for Pelican to
+ process or ignore. For example, to avoid processing .html files,
+ set: ``READERS = {'html': None}``. To add a custom reader for the
+ ``foo`` extension, set: ``READERS = {'foo': FooReader}``
+``IGNORE_FILES = ['.#*']`` A list of file globbing patterns to match against the
+ source files to be ignored by the processor. For example,
+ the default ``['.#*']`` will ignore emacs lock files.
+``MD_EXTENSIONS =`` ``['codehilite(css_class=highlight)','extra']`` A list of the extensions that the Markdown processor
+ will use. Refer to the Python Markdown documentation's
+ `Extensions section `_
+ for a complete list of supported extensions. (Note that
+ defining this in your settings file will override and
+ replace the default values. If your goal is to *add*
+ to the default values for this setting, you'll need to
+ include them explicitly and enumerate the full list of
+ desired Markdown extensions.)
+``OUTPUT_PATH = 'output/'`` Where to output the generated files.
+``PATH`` Path to content directory to be processed by Pelican. If undefined,
+ and content path is not specified via an argument to the ``pelican``
+ command, Pelican will use the current working directory.
+``PAGE_PATHS = ['pages']`` A list of directories to look at for pages, relative to ``PATH``.
+``PAGE_EXCLUDES = []`` A list of directories to exclude when looking for pages in addition
+ to ``ARTICLE_PATHS``.
+``ARTICLE_PATHS = ['']`` A list of directories to look at for articles, relative to ``PATH``.
+``ARTICLE_EXCLUDES = []`` A list of directories to exclude when looking for articles in addition
+ to ``PAGE_PATHS``.
+``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their
+ original format (e.g. Markdown or reStructuredText) to the
+ specified ``OUTPUT_PATH``.
+``OUTPUT_SOURCES_EXTENSION = '.text'`` Controls the extension that will be used by the SourcesGenerator.
+ Defaults to ``.text``. If not a valid string the default value
+ will be used.
+``RELATIVE_URLS = False`` Defines whether Pelican should use document-relative URLs or
+ not. Only set this to ``True`` when developing/testing and only
+ if you fully understand the effect it can have on links/feeds.
+``PLUGINS = []`` The list of plugins to load. See :ref:`plugins`.
+``PLUGIN_PATHS = []`` A list of directories where to look for plugins. See :ref:`plugins`.
+``SITENAME = 'A Pelican Blog'`` Your site name
+``SITEURL`` Base URL of your website. Not defined by default,
+ so it is best to specify your SITEURL; if you do not, feeds
+ will not be generated with properly-formed URLs. You should
+ include ``http://`` and your domain, with no trailing
+ slash at the end. Example: ``SITEURL = 'http://mydomain.com'``
+``TEMPLATE_PAGES = None`` A mapping containing template pages that will be rendered with
+ the blog entries. See :ref:`template_pages`.
+``STATIC_PATHS = ['images']`` A list of directories (relative to ``PATH``) in which to look for
+ static files. Such files will be copied to the output directory
+ without modification. Articles, pages, and other content source
+ files will normally be skipped, so it is safe for a directory to
+ appear both here and in ``PAGE_PATHS`` or ``ARTICLE_PATHS``.
+ Pelican's default settings include the "images" directory here.
+``STATIC_EXCLUDES = []`` A list of directories to exclude when looking for static files.
+``STATIC_EXCLUDE_SOURCES = True`` If set to False, content source files will not be skipped when
+ copying files found in ``STATIC_PATHS``.
+``TIMEZONE`` The timezone used in the date information, to
+ generate Atom and RSS feeds. See the *Timezone*
+ section below for more info.
+``TYPOGRIFY = False`` If set to True, several typographical improvements will be
+ incorporated into the generated HTML via the `Typogrify
+ `_ library,
+ which can be installed via: ``pip install typogrify``
+``TYPOGRIFY_IGNORE_TAGS = []`` A list of tags for Typogrify to ignore. By default
+ Typogrify will ignore ``pre`` and ``code`` tags. This
+ requires that Typogrify version 2.0.4 or later is installed
+``DIRECT_TEMPLATES =`` ``('index', 'categories', 'authors', 'archives')`` List of templates that are used directly to render
+ content. Typically direct templates are used to generate
+ index pages for collections of content (e.g., tags and
+ category index pages). If the tag and category collections
+ are not needed, set ``DIRECT_TEMPLATES = ('index', 'archives')``
+``PAGINATED_DIRECT_TEMPLATES = ('index',)`` Provides the direct templates that should be paginated.
+``SUMMARY_MAX_LENGTH = 50`` When creating a short summary of an article, this will
+ be the default length (measured in words) of the text created.
+ This only applies if your content does not otherwise
+ specify a summary. Setting to ``None`` will cause the summary
+ to be a copy of the original content.
+``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.
+``WITH_FUTURE_DATES = True`` If disabled, content with dates in the future will get a default
+ status of ``draft``. See :ref:`reading_only_modified_content`
+ for caveats.
+``INTRASITE_LINK_REGEX = '[{|](?P.*?)[|}]'`` Regular expression that is used to parse internal links. Default
+ syntax when linking to internal files, tags, etc., is to enclose
+ the identifier, say ``filename``, in ``{}`` or ``||``. Identifier
+ between ``{`` and ``}`` goes into the ``what`` capturing group.
+ For details see :ref:`ref-linking-to-internal-content`.
+``PYGMENTS_RST_OPTIONS = []`` A list of default Pygments settings for your reStructuredText
+ code blocks. See :ref:`internal_pygments_options` for a list of
+ supported options.
+``SLUGIFY_SOURCE = 'title'`` Specifies where you want the slug to be automatically generated
+ from. Can be set to ``title`` to use the 'Title:' metadata tag or
+ ``basename`` to use the article's file name when creating the slug.
+``CACHE_CONTENT = True`` If ``True``, save content in a cache file.
+ See :ref:`reading_only_modified_content` for details about caching.
+``CONTENT_CACHING_LAYER = 'reader'`` If set to ``'reader'``, save only the raw content and metadata
+ returned by readers. If set to ``'generator'``, save processed
+ content objects.
+``CACHE_PATH = 'cache'`` Directory in which to store cache files.
+``GZIP_CACHE = True`` If ``True``, use gzip to (de)compress the cache files.
+``CHECK_MODIFIED_METHOD = 'mtime'`` Controls how files are checked for modifications.
+``LOAD_CONTENT_CACHE = True`` If ``True``, load unmodified content from cache.
+``AUTORELOAD_IGNORE_CACHE = False`` If ``True``, do not load content cache in autoreload mode
+ when the settings file changes.
+``WRITE_SELECTED = []`` If this list is not empty, **only** output files with their paths
+ in this list are written. Paths should be either absolute or relative
+ to the current Pelican working directory. For possible use cases see
+ :ref:`writing_only_selected_content`.
+=============================================================================== =====================================================================
+
+.. [#] Default is the system locale.
+
+
+URL settings
+============
+
+The first thing to understand is that there are currently two supported methods
+for URL formation: *relative* and *absolute*. Relative URLs are useful
+when testing locally, and absolute URLs are reliable and most useful when
+publishing. One method of supporting both is to have one Pelican configuration
+file for local development and another for publishing. To see an example of this
+type of setup, use the ``pelican-quickstart`` script as described in the
+:doc:`Installation ` section, which will produce two separate
+configuration files for local development and publishing, respectively.
+
+You can customize the URLs and locations where files will be saved. The
+``*_URL`` and ``*_SAVE_AS`` variables use Python's format strings. These
+variables allow you to place your articles in a location such as
+``{slug}/index.html`` and link to them as ``{slug}`` for clean URLs. These
+settings give you the flexibility to place your articles and pages anywhere you
+want.
+
+.. note::
+ If you specify a ``datetime`` directive, it will be substituted using the
+ input files' date metadata attribute. If the date is not specified for a
+ particular file, Pelican will rely on the file's ``mtime`` timestamp.
+ Check the `Python datetime documentation`_ for more information.
+
+.. _Python datetime documentation:
+ http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
+
+Also, you can use other file metadata attributes as well:
+
+* slug
+* date
+* lang
+* author
+* category
+
+Example usage:
+
+* ``ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'``
+* ``ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'``
+
+This would save your articles in something like ``/posts/2011/Aug/07/sample-post/index.html``,
+and the URL to this would be ``/posts/2011/Aug/07/sample-post/``.
+
+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.
+Period archives fit intuitively with the hierarchical model of web URLs and can
+make it easier for readers to navigate through the posts you've written over time.
+
+Example usage:
+
+* ``YEAR_ARCHIVE_SAVE_AS = 'posts/{date:%Y}/index.html'``
+* ``MONTH_ARCHIVE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/index.html'``
+
+With these settings, Pelican will create an archive of all your posts for the
+year at (for instance) ``posts/2011/index.html`` and an archive of all your
+posts for the month at ``posts/2011/Aug/index.html``.
+
+.. note::
+ Period archives work best when the final path segment is ``index.html``.
+ This way a reader can remove a portion of your URL and automatically
+ arrive at an appropriate archive of posts, without having to specify
+ a page name.
+
+====================================================== ==============================================================
+Setting name (followed by default value, if any) What does it do?
+====================================================== ==============================================================
+``ARTICLE_URL = '{slug}.html'`` The URL to refer to an article.
+``ARTICLE_SAVE_AS = '{slug}.html'`` The place where we will save an article.
+``ARTICLE_ORDER_BY = 'slug'`` The metadata attribute used to sort articles. By default,
+ the ``articles_page.object_list`` template variable is
+ ordered by slug. If you modify this, make sure all
+ articles contain the attribute you specify. You can also
+ specify a "sorting" function of one argument that is used
+ to extract a comparison key from each article. For example,
+ sorting by title without using the built-in functionality
+ would use the function ``operator.attrgetter('title')``.
+``ARTICLE_LANG_URL = '{slug}-{lang}.html'`` The URL to refer to an article which doesn't use the
+ default language.
+``ARTICLE_LANG_SAVE_AS = '{slug}-{lang}.html'`` The place where we will save an article which
+ doesn't use the default language.
+``DRAFT_URL = 'drafts/{slug}.html'`` The URL to refer to an article draft.
+``DRAFT_SAVE_AS = 'drafts/{slug}.html'`` The place where we will save an article draft.
+``DRAFT_LANG_URL = 'drafts/{slug}-{lang}.html'`` The URL to refer to an article draft which doesn't
+ use the default language.
+``DRAFT_LANG_SAVE_AS = 'drafts/{slug}-{lang}.html'`` The place where we will save an article draft which
+ doesn't use the default language.
+``PAGE_URL = 'pages/{slug}.html'`` The URL we will use to link to a page.
+``PAGE_SAVE_AS = 'pages/{slug}.html'`` The location we will save the page. This value has to be
+ the same as PAGE_URL or you need to use a rewrite in
+ your server config.
+
+``PAGE_ORDER_BY = 'basename'`` The metadata attribute used to sort pages. By default
+ the ``PAGES`` template variable is ordered by basename
+ (i.e., path not included). Note that the option ``'basename'``
+ is a special option supported in the source code. If
+ you modify this setting, make sure all pages contain
+ the attribute you specify. You can also specify a "sorting"
+ function of one argument that is used to extract a comparison
+ key from each page. For example, the basename function looks
+ similar to
+ ``lambda x: os.path.basename(getattr(x, 'source_path', ''))``.
+``PAGE_LANG_URL = 'pages/{slug}-{lang}.html'`` The URL we will use to link to a page which doesn't
+ use the default language.
+``PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}.html'`` The location we will save the page which doesn't
+ use the default language.
+``CATEGORY_URL = 'category/{slug}.html'`` The URL to use for a category.
+``CATEGORY_SAVE_AS = 'category/{slug}.html'`` The location to save a category.
+``TAG_URL = 'tag/{slug}.html'`` The URL to use for a tag.
+``TAG_SAVE_AS = 'tag/{slug}.html'`` The location to save the tag page.
+``AUTHOR_URL = 'author/{slug}.html'`` The URL to use for an author.
+``AUTHOR_SAVE_AS = 'author/{slug}.html'`` The location to save an author.
+``YEAR_ARCHIVE_SAVE_AS = ''`` The location to save per-year archives of your posts.
+``MONTH_ARCHIVE_SAVE_AS = ''`` The location to save per-month archives of your posts.
+``DAY_ARCHIVE_SAVE_AS = ''`` The location to save per-day archives of your posts.
+``SLUG_SUBSTITUTIONS = ()`` Substitutions to make prior to stripping out
+ non-alphanumerics when generating slugs. Specified
+ as a list of 2-tuples of ``(from, to)`` which are
+ applied in order.
+====================================================== ==============================================================
+
+.. note::
+
+ If you do not want one or more of the default pages to be created (e.g.,
+ you are the only author on your site and thus do not need an Authors page),
+ set the corresponding ``*_SAVE_AS`` setting to ``''`` to prevent the
+ relevant page from being generated.
+
+``DIRECT_TEMPLATES``, which are ``('index', 'tags', 'categories', 'archives')``
+by default, work a bit differently than noted above. Only the ``_SAVE_AS``
+settings are available, but it is available for any direct template:
+
+============================================= ===============================================
+Setting name (followed by default value) What does it do?
+============================================= ===============================================
+``ARCHIVES_SAVE_AS = 'archives.html'`` The location to save the article archives page.
+``AUTHORS_SAVE_AS = 'authors.html'`` The location to save the author list.
+``CATEGORIES_SAVE_AS = 'categories.html'`` The location to save the category list.
+``TAGS_SAVE_AS = 'tags.html'`` The location to save the tag list.
+``INDEX_SAVE_AS = 'index.html'`` The location to save the list of articles.
+============================================= ===============================================
+
+URLs for direct template pages are theme-dependent. Some themes hard-code them:
+``'archives.html'``, ``'authors.html'``, ``'categories.html'``, ``'tags.html'``.
+
+Timezone
+--------
+
+If no timezone is defined, UTC is assumed. This means that the generated Atom
+and RSS feeds will contain incorrect date information if your locale is not UTC.
+
+Pelican issues a warning in case this setting is not defined, as it was not
+mandatory in previous versions.
+
+Have a look at `the wikipedia page`_ to get a list of valid timezone values.
+
+.. _the wikipedia page: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+
+
+Date format and locale
+----------------------
+
+If no ``DATE_FORMATS`` are set, Pelican will fall back to
+``DEFAULT_DATE_FORMAT``. If you need to maintain multiple languages with
+different date formats, you can set the ``DATE_FORMATS`` dictionary using the
+language name (``lang`` metadata in your post content) as the key.
+
+In addition to the standard C89 strftime format codes that are listed in
+`Python strftime documentation`_, you can use ``-`` character between ``%`` and
+the format character to remove any leading zeros. For example, ``%d/%m/%Y`` will
+output ``01/01/2014`` whereas ``%-d/%-m/%Y`` will result in ``1/1/2014``.
+
+.. parsed-literal::
+
+ DATE_FORMATS = {
+ 'en': '%a, %d %b %Y',
+ 'jp': '%Y-%m-%d(%a)',
+ }
+
+You can set locale to further control date format:
+
+.. parsed-literal::
+
+ LOCALE = ('usa', 'jpn', # On Windows
+ 'en_US', 'ja_JP' # On Unix/Linux
+ )
+
+Also, it is possible to set different locale settings for each language. If you
+put (locale, format) tuples in the dict, this will override the ``LOCALE``
+setting above:
+
+.. parsed-literal::
+ # On Unix/Linux
+ DATE_FORMATS = {
+ 'en': ('en_US','%a, %d %b %Y'),
+ 'jp': ('ja_JP','%Y-%m-%d(%a)'),
+ }
+
+ # On Windows
+ DATE_FORMATS = {
+ 'en': ('usa','%a, %d %b %Y'),
+ 'jp': ('jpn','%Y-%m-%d(%a)'),
+ }
+
+This is a list of available `locales on Windows`_ . On Unix/Linux, usually you
+can get a list of available locales via the ``locale -a`` command; see manpage
+`locale(1)`_ for more information.
+
+
+.. _Python strftime documentation: http://docs.python.org/library/datetime.html#strftime-strptime-behavior
+
+.. _locales on Windows: http://msdn.microsoft.com/en-us/library/cdax410z%28VS.71%29.aspx
+
+.. _locale(1): http://linux.die.net/man/1/locale
+
+
+.. _template_pages:
+
+Template pages
+==============
+
+If you want to generate custom pages besides your blog entries, you can point
+any Jinja2 template file with a path pointing to the file and the destination
+path for the generated file.
+
+For instance, if you have a blog with three static pages — a list of books,
+your resume, and a contact page — you could have::
+
+ TEMPLATE_PAGES = {'src/books.html': 'dest/books.html',
+ 'src/resume.html': 'dest/resume.html',
+ 'src/contact.html': 'dest/contact.html'}
+
+
+.. _path_metadata:
+
+Path metadata
+=============
+
+Not all metadata needs to be `embedded in source file itself`__. For
+example, blog posts are often named following a ``YYYY-MM-DD-SLUG.rst``
+pattern, or nested into ``YYYY/MM/DD-SLUG`` directories. To extract
+metadata from the filename or path, set ``FILENAME_METADATA`` or
+``PATH_METADATA`` to regular expressions that use Python's `group name
+notation`_ ``(?P…)``. If you want to attach additional metadata
+but don't want to encode it in the path, you can set
+``EXTRA_PATH_METADATA``:
+
+.. parsed-literal::
+
+ EXTRA_PATH_METADATA = {
+ 'relative/path/to/file-1': {
+ 'key-1a': 'value-1a',
+ 'key-1b': 'value-1b',
+ },
+ 'relative/path/to/file-2': {
+ 'key-2': 'value-2',
+ },
+ }
+
+This can be a convenient way to shift the installed location of a
+particular file:
+
+.. parsed-literal::
+
+ # Take advantage of the following defaults
+ # STATIC_SAVE_AS = '{path}'
+ # STATIC_URL = '{path}'
+ STATIC_PATHS = [
+ 'extra/robots.txt',
+ ]
+ EXTRA_PATH_METADATA = {
+ 'extra/robots.txt': {'path': 'robots.txt'},
+ }
+
+__ internal_metadata__
+.. _group name notation:
+ http://docs.python.org/3/library/re.html#regular-expression-syntax
+
+Feed settings
+=============
+
+By default, Pelican uses Atom feeds. However, it is also possible to use RSS
+feeds if you prefer.
+
+Pelican generates category feeds as well as feeds for all your articles. It does
+not generate feeds for tags by default, but it is possible to do so using
+the ``TAG_FEED_ATOM`` and ``TAG_FEED_RSS`` settings:
+
+================================================= =====================================================
+Setting name (followed by default value, if any) What does it do?
+================================================= =====================================================
+``FEED_DOMAIN = None``, i.e. base URL is "/" The domain prepended to feed URLs. Since feed URLs
+ should always be absolute, it is highly recommended
+ to define this (e.g., "http://feeds.example.com"). If
+ you have already explicitly defined SITEURL (see
+ above) and want to use the same domain for your
+ feeds, you can just set: ``FEED_DOMAIN = SITEURL``.
+``FEED_ATOM = None``, i.e. no Atom feed Relative URL to output the Atom feed.
+``FEED_RSS = None``, i.e. no RSS Relative URL to output the RSS feed.
+``FEED_ALL_ATOM = 'feeds/all.atom.xml'`` Relative URL to output the all-posts Atom feed:
+ this feed will contain all posts regardless of their
+ language.
+``FEED_ALL_RSS = None``, i.e. no all-posts RSS Relative URL to output the all-posts RSS feed:
+ this feed will contain all posts regardless of their
+ language.
+``CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'`` [2]_ Where to put the category Atom feeds.
+``CATEGORY_FEED_RSS = None``, i.e. no RSS Where to put the category RSS feeds.
+``AUTHOR_FEED_ATOM = 'feeds/%s.atom.xml'`` [2]_ Where to put the author Atom feeds.
+``AUTHOR_FEED_RSS = 'feeds/%s.rss.xml'`` [2]_ Where to put the author RSS feeds.
+``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.
+``TAG_FEED_RSS = None``, i.e. no RSS tag feed Relative URL to output the tag RSS feed
+``FEED_MAX_ITEMS`` Maximum number of items allowed in a feed. Feed item
+ quantity is unrestricted by default.
+================================================= =====================================================
+
+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.
+
+FeedBurner
+----------
+
+If you want to use FeedBurner for your feed, you will likely need to decide
+upon a unique identifier. For example, if your site were called "Thyme" and
+hosted on the www.example.com domain, you might use "thymefeeds" as your
+unique identifier, which we'll use throughout this section for illustrative
+purposes. In your Pelican settings, set the ``FEED_ATOM`` attribute to
+``thymefeeds/main.xml`` to create an Atom feed with an original address of
+``http://www.example.com/thymefeeds/main.xml``. Set the ``FEED_DOMAIN``
+attribute to ``http://feeds.feedburner.com``, or ``http://feeds.example.com`` if
+you are using a CNAME on your own domain (i.e., FeedBurner's "MyBrand" feature).
+
+There are two fields to configure in the `FeedBurner
+`_ interface: "Original Feed" and "Feed
+Address". In this example, the "Original Feed" would be
+``http://www.example.com/thymefeeds/main.xml`` and the "Feed Address" suffix
+would be ``thymefeeds/main.xml``.
+
+Pagination
+==========
+
+The default behaviour of Pelican is to list all the article titles along
+with a short description on the index page. While this works well for
+small-to-medium sites, sites with a large quantity of articles will probably
+benefit from paginating this list.
+
+You can use the following settings to configure the pagination.
+
+================================================ =====================================================
+Setting name (followed by default value, if any) What does it do?
+================================================ =====================================================
+``DEFAULT_ORPHANS = 0`` The minimum number of articles allowed on the
+ last page. Use this when you don't want the last page
+ to only contain a handful of articles.
+``DEFAULT_PAGINATION = False`` The maximum number of articles to include on a
+ page, not including orphans. False to disable
+ pagination.
+``PAGINATION_PATTERNS`` A set of patterns that are used to determine advanced
+ pagination output.
+================================================ =====================================================
+
+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::
+
+ (minimum page, URL setting, SAVE_AS setting,)
+
+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::
+
+ PAGINATION_PATTERNS = (
+ (1, '{base_name}/', '{base_name}/index.html'),
+ (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.
+
+Tag cloud
+=========
+
+If you want to generate a tag cloud with all your tags, you can do so using the
+following settings.
+
+================================================ =====================================================
+Setting name (followed by default value) What does it do?
+================================================ =====================================================
+``TAG_CLOUD_STEPS = 4`` Count of different font sizes in the tag
+ cloud.
+``TAG_CLOUD_MAX_ITEMS = 100`` Maximum number of tags in the cloud.
+================================================ =====================================================
+
+The default theme does not include a tag cloud, but it is pretty easy to add one::
+
+
+ {% for tag in tag_cloud %}
+ {{ tag.0 }}
+ {% endfor %}
+
+
+You should then also define CSS styles with appropriate classes (tag-1 to tag-N,
+where N matches ``TAG_CLOUD_STEPS``), tag-1 being the most frequent, and
+define a ``ul.tagcloud`` class with appropriate list-style to create the cloud.
+For example::
+
+ ul.tagcloud {
+ list-style: none;
+ padding: 0;
+ }
+
+ ul.tagcloud li {
+ display: inline-block;
+ }
+
+ li.tag-1 {
+ font-size: 150%;
+ }
+
+ li.tag-2 {
+ font-size: 120%;
+ }
+
+ ...
+
+Translations
+============
+
+Pelican offers a way to translate articles. See the :doc:`Content ` section for
+more information.
+
+======================================================== =====================================================
+Setting name (followed by default value, if any) What does it do?
+======================================================== =====================================================
+``DEFAULT_LANG = 'en'`` The default language to use.
+``TRANSLATION_FEED_ATOM = 'feeds/all-%s.atom.xml'`` [3]_ Where to put the Atom feed for translations.
+``TRANSLATION_FEED_RSS = None``, i.e. no RSS Where to put the RSS feed for translations.
+======================================================== =====================================================
+
+.. [3] %s is the language
+
+Ordering content
+================
+
+================================================ =====================================================
+Setting name (followed by default value) What does it do?
+================================================ =====================================================
+``NEWEST_FIRST_ARCHIVES = True`` Order archives by newest first by date. (False:
+ orders by date with older articles first.)
+``REVERSE_CATEGORY_ORDER = False`` Reverse the category order. (True: lists by reverse
+ alphabetical order; default lists alphabetically.)
+================================================ =====================================================
+
+Themes
+======
+
+Creating Pelican themes is addressed in a dedicated section (see :ref:`theming-pelican`).
+However, here are the settings that are related to themes.
+
+================================================ =====================================================
+Setting name (followed by default value, if any) What does it do?
+================================================ =====================================================
+``THEME`` Theme to use to produce the output. Can be a relative
+ or absolute path to a theme folder, or the name of a
+ default theme or a theme installed via
+ ``pelican-themes`` (see below).
+``THEME_STATIC_DIR = 'theme'`` Destination directory in the output path where
+ Pelican will place the files collected from
+ `THEME_STATIC_PATHS`. Default is `theme`.
+``THEME_STATIC_PATHS = ['static']`` Static theme paths you want to copy. Default
+ value is `static`, but if your theme has
+ other static paths, you can put them here. If files
+ or directories with the same names are included in
+ the paths defined in this settings, they will be
+ progressively overwritten.
+``CSS_FILE = 'main.css'`` Specify the CSS file you want to load.
+================================================ =====================================================
+
+
+By default, two themes are available. You can specify them using the ``THEME``
+setting or by passing the ``-t`` option to the ``pelican`` command:
+
+* notmyidea
+* simple (a synonym for "plain text" :)
+
+There are a number of other themes available at https://github.com/getpelican/pelican-themes.
+Pelican comes with :doc:`pelican-themes`, a small script for managing themes.
+
+You can define your own theme, either by starting from scratch or by duplicating
+and modifying a pre-existing theme. Here is :doc:`a guide on how to create your theme `.
+
+Following are example ways to specify your preferred theme::
+
+ # Specify name of a built-in theme
+ THEME = "notmyidea"
+ # Specify name of a theme installed via the pelican-themes tool
+ THEME = "chunk"
+ # Specify a customized theme, via path relative to the settings file
+ THEME = "themes/mycustomtheme"
+ # Specify a customized theme, via absolute path
+ THEME = "/home/myuser/projects/mysite/themes/mycustomtheme"
+
+The built-in ``notmyidea`` theme can make good use of the following settings. Feel
+free to use them in your themes as well.
+
+======================= =======================================================
+Setting name What does it do?
+======================= =======================================================
+``SITESUBTITLE`` A subtitle to appear in the header.
+``DISQUS_SITENAME`` Pelican can handle Disqus comments. Specify the
+ Disqus sitename identifier here.
+``GITHUB_URL`` Your GitHub URL (if you have one). It will then
+ use this information to create a GitHub ribbon.
+``GOOGLE_ANALYTICS`` Set to 'UA-XXXX-YYYY' to activate Google Analytics.
+``GOSQUARED_SITENAME`` Set to 'XXX-YYYYYY-X' to activate GoSquared.
+``MENUITEMS`` A list of tuples (Title, URL) for additional menu
+ items to appear at the beginning of the main menu.
+``PIWIK_URL`` URL to your Piwik server - without 'http://' at the
+ beginning.
+``PIWIK_SSL_URL`` If the SSL-URL differs from the normal Piwik-URL
+ you have to include this setting too. (optional)
+``PIWIK_SITE_ID`` ID for the monitored website. You can find the ID
+ in the Piwik admin interface > Settings > Websites.
+``LINKS`` A list of tuples (Title, URL) for links to appear on
+ the header.
+``SOCIAL`` A list of tuples (Title, URL) to appear in the
+ "social" section.
+``TWITTER_USERNAME`` Allows for adding a button to articles to encourage
+ others to tweet about them. Add your Twitter username
+ if you want this button to appear.
+======================= =======================================================
+
+In addition, you can use the "wide" version of the ``notmyidea`` theme by
+adding the following to your configuration::
+
+ CSS_FILE = "wide.css"
+
+Logging
+=======
+
+Sometimes, a long list of warnings may appear during site generation. Finding
+the **meaningful** error message in the middle of tons of annoying log output
+can be quite tricky. In order to filter out redundant log messages, Pelican
+comes with the ``LOG_FILTER`` setting.
+
+``LOG_FILTER`` should be a list of tuples ``(level, msg)``, each of them being
+composed of the logging level (up to ``warning``) and the message to be ignored.
+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')]``
+
+.. _reading_only_modified_content:
+
+Reading only modified content
+=============================
+
+To speed up the build process, Pelican can optionally read only articles
+and pages with modified content.
+
+When Pelican is about to read some content source file:
+
+1. The hash or modification time information for the file from a
+ previous build are loaded from a cache file if ``LOAD_CONTENT_CACHE``
+ is ``True``. These files are stored in the ``CACHE_PATH``
+ directory. If the file has no record in the cache file, it is read
+ as usual.
+2. The file is checked according to ``CHECK_MODIFIED_METHOD``:
+
+ - If set to ``'mtime'``, the modification time of the file is
+ checked.
+ - If set to a name of a function provided by the ``hashlib``
+ module, e.g. ``'md5'``, the file hash is checked.
+ - If set to anything else or the necessary information about the
+ file cannot be found in the cache file, the content is read as
+ usual.
+
+3. If the file is considered unchanged, the content data saved in a
+ previous build corresponding to the file is loaded from the cache,
+ and the file is not read.
+4. If the file is considered changed, the file is read and the new
+ modification information and the content data are saved to the
+ cache if ``CACHE_CONTENT`` is ``True``.
+
+If ``CONTENT_CACHING_LAYER`` is set to ``'reader'`` (the default),
+the raw content and metadata returned by a reader are cached. If this
+setting is instead set to ``'generator'``, the processed content
+object is cached. Caching the processed content object may conflict
+with plugins (as some reading related signals may be skipped) and the
+``WITH_FUTURE_DATES`` functionality (as the ``draft`` status of the
+cached content objects would not change automatically over time).
+
+Checking modification times is faster than comparing file hashes,
+but it is not as reliable because ``mtime`` information can be lost,
+e.g., when copying content source files using the ``cp`` or ``rsync``
+commands without the ``mtime`` preservation mode (which for ``rsync``
+can be invoked by passing the ``--archive`` flag).
+
+The cache files are Python pickles, so they may not be readable by
+different versions of Python as the pickle format often changes. If
+such an error is encountered, the cache files have to be rebuilt by
+removing them and re-running Pelican, or by using the Pelican
+command-line option ``--ignore-cache``. The cache files also have to
+be rebuilt when changing the ``GZIP_CACHE`` setting for cache file
+reading to work properly.
+
+The ``--ignore-cache`` command-line option is also useful when the
+whole cache needs to be regenerated, such as when making modifications
+to the settings file that will affect the cached content, or just for
+debugging purposes. When Pelican runs in autoreload mode, modification
+of the settings file will make it ignore the cache automatically if
+``AUTORELOAD_IGNORE_CACHE`` is ``True``.
+
+Note that even when using cached content, all output is always
+written, so the modification times of the generated ``*.html`` files
+will always change. Therefore, ``rsync``-based uploading may benefit
+from the ``--checksum`` option.
+
+.. _writing_only_selected_content:
+
+Writing only selected content
+=============================
+
+When only working on a single article or page, or making tweaks to
+your theme, it is often desirable to generate and review your work
+as quickly as possible. In such cases, generating and writing the
+entire site output is often unnecessary. By specifying only the
+desired files as output paths in the ``WRITE_SELECTED`` list,
+**only** those files will be written. This list can be also specified
+on the command line using the ``--write-selected`` option, which
+accepts a comma-separated list of output file paths. By default this
+list is empty, so all output is written.
+
+Example settings
+================
+
+.. literalinclude:: ../samples/pelican.conf.py
+ :language: python
+
+
+.. _Jinja custom filters documentation: http://jinja.pocoo.org/docs/api/#custom-filters
+.. _Docutils Configuration: http://docutils.sourceforge.net/docs/user/config.html
diff --git a/docs/themes.rst b/docs/themes.rst
new file mode 100644
index 00000000..4be9a8e5
--- /dev/null
+++ b/docs/themes.rst
@@ -0,0 +1,439 @@
+.. _theming-pelican:
+
+Creating themes
+###############
+
+To generate its HTML output, Pelican uses the `Jinja `_
+templating engine due to its flexibility and straightforward syntax. If you want
+to create your own theme, feel free to take inspiration from the `"simple" theme
+`_.
+
+To generate your site using a theme you have created (or downloaded manually and
+then modified), you can specify that theme via the ``-t`` flag::
+
+ pelican content -s pelicanconf.py -t /projects/your-site/themes/your-theme
+
+If you'd rather not specify the theme on every invocation, you can define
+``THEME`` in your settings to point to the location of your preferred theme.
+
+Structure
+=========
+
+To make your own theme, you must follow the following structure::
+
+ ├── static
+ │ ├── css
+ │ └── images
+ └── templates
+ ├── archives.html // to display archives
+ ├── period_archives.html // to display time-period archives
+ ├── article.html // processed for each article
+ ├── author.html // processed for each author
+ ├── authors.html // must list all the authors
+ ├── categories.html // must list all the categories
+ ├── category.html // processed for each category
+ ├── index.html // the index. List all the articles
+ ├── page.html // processed for each page
+ ├── tag.html // processed for each tag
+ └── tags.html // must list all the tags. Can be a tag cloud.
+
+* `static` contains all the static assets, which will be copied to the output
+ `theme` folder. The above filesystem layout includes CSS and image folders,
+ but those are just examples. Put what you need here.
+
+* `templates` contains all the templates that will be used to generate the content.
+ The template files listed above are mandatory; you can add your own templates
+ if it helps you keep things organized while creating your theme.
+
+Templates and variables
+=======================
+
+The idea is to use a simple syntax that you can embed into your HTML pages.
+This document describes which templates should exist in a theme, and which
+variables will be passed to each template at generation time.
+
+All templates will receive the variables defined in your settings file, as long
+as they are in all-caps. You can access them directly.
+
+Common variables
+----------------
+
+All of these settings will be available to all templates.
+
+============= ===================================================
+Variable Description
+============= ===================================================
+output_file The name of the file currently being generated. For
+ instance, when Pelican is rendering the homepage,
+ output_file will be "index.html".
+articles The list of articles, ordered descending by date.
+ All the elements are `Article` objects, so you can
+ access their attributes (e.g. title, summary, author
+ etc.). Sometimes this is shadowed (for instance in
+ the tags page). You will then find info about it
+ in the `all_articles` variable.
+dates The same list of articles, but ordered by date,
+ ascending.
+tags A list of (tag, articles) tuples, containing all
+ the tags.
+categories A list of (category, articles) tuples, containing
+ all the categories and corresponding articles (values)
+pages The list of pages
+============= ===================================================
+
+Sorting
+-------
+
+URL wrappers (currently categories, tags, and authors), have
+comparison methods that allow them to be easily sorted by name::
+
+ {% for tag, articles in tags|sort %}
+
+If you want to sort based on different criteria, `Jinja's sort
+command`__ has a number of options.
+
+__ http://jinja.pocoo.org/docs/templates/#sort
+
+
+Date Formatting
+---------------
+
+Pelican formats the date according to your settings and locale
+(``DATE_FORMATS``/``DEFAULT_DATE_FORMAT``) and provides a
+``locale_date`` attribute. On the other hand, the ``date`` attribute will
+be a `datetime`_ object. If you need custom formatting for a date
+different than your settings, use the Jinja filter ``strftime``
+that comes with Pelican. Usage is same as Python `strftime`_ format,
+but the filter will do the right thing and format your date according
+to the locale given in your settings::
+
+ {{ article.date|strftime('%d %B %Y') }}
+
+.. _datetime: http://docs.python.org/2/library/datetime.html#datetime-objects
+.. _strftime: http://docs.python.org/2/library/datetime.html#strftime-strptime-behavior
+
+index.html
+----------
+
+This is the home page of your blog, generated at output/index.html.
+
+If pagination is active, subsequent pages will reside in output/index`n`.html.
+
+====================== ===================================================
+Variable Description
+====================== ===================================================
+articles_paginator A paginator object for the list of articles
+articles_page The current page of articles
+articles_previous_page The previous page of articles (``None`` if page does
+ not exist)
+articles_next_page The next page of articles (``None`` if page does
+ not exist)
+dates_paginator A paginator object for the article list, ordered by
+ date, ascending.
+dates_page The current page of articles, ordered by date,
+ ascending.
+dates_previous_page The previous page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+dates_next_page The next page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+page_name 'index' -- useful for pagination links
+====================== ===================================================
+
+author.html
+-------------
+
+This template will be processed for each of the existing authors, with
+output generated at output/author/`author_name`.html.
+
+If pagination is active, subsequent pages will reside as defined by setting
+AUTHOR_SAVE_AS (`Default:` output/author/`author_name'n'`.html).
+
+====================== ===================================================
+Variable Description
+====================== ===================================================
+author The name of the author being processed
+articles Articles by this author
+dates Articles by this author, but ordered by date,
+ ascending
+articles_paginator A paginator object for the list of articles
+articles_page The current page of articles
+articles_previous_page The previous page of articles (``None`` if page does
+ not exist)
+articles_next_page The next page of articles (``None`` if page does
+ not exist)
+dates_paginator A paginator object for the article list, ordered by
+ date, ascending.
+dates_page The current page of articles, ordered by date,
+ ascending.
+dates_previous_page The previous page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+dates_next_page The next page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+page_name AUTHOR_URL where everything after `{slug}` is
+ removed -- useful for pagination links
+====================== ===================================================
+
+category.html
+-------------
+
+This template will be processed for each of the existing categories, with
+output generated at output/category/`category_name`.html.
+
+If pagination is active, subsequent pages will reside as defined by setting
+CATEGORY_SAVE_AS (`Default:` output/category/`category_name'n'`.html).
+
+====================== ===================================================
+Variable Description
+====================== ===================================================
+category The name of the category being processed
+articles Articles for this category
+dates Articles for this category, but ordered by date,
+ ascending
+articles_paginator A paginator object for the list of articles
+articles_page The current page of articles
+articles_previous_page The previous page of articles (``None`` if page does
+ not exist)
+articles_next_page The next page of articles (``None`` if page does
+ not exist)
+dates_paginator A paginator object for the list of articles,
+ ordered by date, ascending
+dates_page The current page of articles, ordered by date,
+ ascending
+dates_previous_page The previous page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+dates_next_page The next page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+page_name CATEGORY_URL where everything after `{slug}` is
+ removed -- useful for pagination links
+====================== ===================================================
+
+article.html
+-------------
+
+This template will be processed for each article, with .html files saved
+as output/`article_name`.html. Here are the specific variables it gets.
+
+============= ===================================================
+Variable Description
+============= ===================================================
+article The article object to be displayed
+category The name of the category for the current article
+============= ===================================================
+
+Any metadata that you put in the header of the article source file
+will be available as fields on the ``article`` object. The field name will be
+the same as the name of the metadata field, except in all-lowercase characters.
+
+For example, you could add a field called `FacebookImage` to your article
+metadata, as shown below:
+
+.. code-block:: markdown
+
+ Title: I love Python more than music
+ Date: 2013-11-06 10:06
+ Tags: personal, python
+ Category: Tech
+ Slug: python-je-l-aime-a-mourir
+ Author: Francis Cabrel
+ FacebookImage: http://franciscabrel.com/images/pythonlove.png
+
+This new metadata will be made available as `article.facebookimage` in your
+`article.html` template. This would allow you, for example, to specify an
+image for the Facebook open graph tags that will change for each article:
+
+.. code-block:: html+jinja
+
+
+
+
+page.html
+---------
+
+This template will be processed for each page, with corresponding .html files
+saved as output/`page_name`.html.
+
+============= ===================================================
+Variable Description
+============= ===================================================
+page The page object to be displayed. You can access its
+ title, slug, and content.
+============= ===================================================
+
+tag.html
+--------
+
+This template will be processed for each tag, with corresponding .html files
+saved as output/tag/`tag_name`.html.
+
+If pagination is active, subsequent pages will reside as defined in setting
+TAG_SAVE_AS (`Default:` output/tag/`tag_name'n'`.html).
+
+====================== ===================================================
+Variable Description
+====================== ===================================================
+tag The name of the tag being processed
+articles Articles related to this tag
+dates Articles related to this tag, but ordered by date,
+ ascending
+articles_paginator A paginator object for the list of articles
+articles_page The current page of articles
+articles_previous_page The previous page of articles (``None`` if page does
+ not exist)
+articles_next_page The next page of articles (``None`` if page does
+ not exist)
+dates_paginator A paginator object for the list of articles,
+ ordered by date, ascending
+dates_page The current page of articles, ordered by date,
+ ascending
+dates_previous_page The previous page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+dates_next_page The next page of articles, ordered by date,
+ ascending (``None`` if page does not exist)
+page_name TAG_URL where everything after `{slug}` is removed
+ -- useful for pagination links
+====================== ===================================================
+
+period_archives.html
+--------------------
+
+This template will be processed for each year of your posts if a path
+for YEAR_ARCHIVE_SAVE_AS is defined, each month if MONTH_ARCHIVE_SAVE_AS
+is defined and each day if DAY_ARCHIVE_SAVE_AS is defined.
+
+=================== ===================================================
+Variable Description
+=================== ===================================================
+period A tuple of the form (`year`, `month`, `day`) that
+ indicates the current time period. `year` and `day`
+ are numbers while `month` is a string. This tuple
+ only contains `year` if the time period is a
+ given year. It contains both `year` and `month`
+ if the time period is over years and months and
+ so on.
+
+=================== ===================================================
+
+You can see an example of how to use `period` in the ``simple`` theme's
+period_archives.html
+
+Feeds
+=====
+
+The feed variables changed in 3.0. Each variable now explicitly lists ATOM or
+RSS in the name. ATOM is still the default. Old themes will need to be updated.
+Here is a complete list of the feed variables::
+
+ FEED_ATOM
+ FEED_RSS
+ FEED_ALL_ATOM
+ FEED_ALL_RSS
+ CATEGORY_FEED_ATOM
+ CATEGORY_FEED_RSS
+ TAG_FEED_ATOM
+ TAG_FEED_RSS
+ TRANSLATION_FEED_ATOM
+ TRANSLATION_FEED_RSS
+
+
+Inheritance
+===========
+
+Since version 3.0, Pelican supports inheritance from the ``simple`` theme, so
+you can re-use the ``simple`` theme templates in your own themes.
+
+If one of the mandatory files in the ``templates/`` directory of your theme is
+missing, it will be replaced by the matching template from the ``simple`` theme.
+So if the HTML structure of a template in the ``simple`` theme is right for you,
+you don't have to write a new template from scratch.
+
+You can also extend templates from the ``simple`` themes in your own themes by
+using the ``{% extends %}`` directive as in the following example:
+
+.. code-block:: html+jinja
+
+ {% extends "!simple/index.html" %}
+
+ {% extends "index.html" %}
+
+
+Example
+-------
+
+With this system, it is possible to create a theme with just two files.
+
+base.html
+"""""""""
+
+The first file is the ``templates/base.html`` template:
+
+.. code-block:: html+jinja
+
+ {% extends "!simple/base.html" %}
+
+ {% block head %}
+ {{ super() }}
+
+ {% endblock %}
+
+1. On the first line, we extend the ``base.html`` template from the ``simple``
+ theme, so we don't have to rewrite the entire file.
+2. On the third line, we open the ``head`` block which has already been defined
+ in the ``simple`` theme.
+3. On the fourth line, the function ``super()`` keeps the content previously
+ inserted in the ``head`` block.
+4. On the fifth line, we append a stylesheet to the page.
+5. On the last line, we close the ``head`` block.
+
+This file will be extended by all the other templates, so the stylesheet will
+be linked from all pages.
+
+style.css
+"""""""""
+
+The second file is the ``static/css/style.css`` CSS stylesheet:
+
+.. code-block:: css
+
+ body {
+ font-family : monospace ;
+ font-size : 100% ;
+ background-color : white ;
+ color : #111 ;
+ width : 80% ;
+ min-width : 400px ;
+ min-height : 200px ;
+ padding : 1em ;
+ margin : 5% 10% ;
+ border : thin solid gray ;
+ border-radius : 5px ;
+ display : block ;
+ }
+
+ a:link { color : blue ; text-decoration : none ; }
+ a:hover { color : blue ; text-decoration : underline ; }
+ a:visited { color : blue ; }
+
+ h1 a { color : inherit !important }
+ h2 a { color : inherit !important }
+ h3 a { color : inherit !important }
+ h4 a { color : inherit !important }
+ h5 a { color : inherit !important }
+ h6 a { color : inherit !important }
+
+ pre {
+ margin : 2em 1em 2em 4em ;
+ }
+
+ #menu li {
+ display : inline ;
+ }
+
+ #post-list {
+ margin-bottom : 1em ;
+ margin-top : 1em ;
+ }
+
+Download
+""""""""
+
+You can download this example theme :download:`here <_static/theme-basic.zip>`.
diff --git a/docs/tips.rst b/docs/tips.rst
new file mode 100644
index 00000000..eb400124
--- /dev/null
+++ b/docs/tips.rst
@@ -0,0 +1,101 @@
+Tips
+####
+
+Here are some tips about Pelican that you might find useful.
+
+Publishing to GitHub
+====================
+
+`GitHub Pages `_ offer an easy
+and convenient way to publish Pelican sites. There are `two types of GitHub
+Pages `_:
+*Project Pages* and *User Pages*. Pelican sites can be published as both
+Project Pages and User Pages.
+
+Project Pages
+-------------
+
+To publish a Pelican site as a Project Page you need to *push* the content of
+the ``output`` dir generated by Pelican to a repository's ``gh-pages`` branch
+on GitHub.
+
+The excellent `ghp-import `_, which can
+be installed with ``easy_install`` or ``pip``, makes this process really easy.
+
+For example, if the source of your Pelican site is contained in a GitHub
+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
+ $ git push origin gh-pages
+
+The ``ghp-import output`` command updates the local ``gh-pages`` branch with
+the content of the ``output`` directory (creating the branch if it doesn't
+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 created by the ``pelican-quickstart``
+ command publishes the Pelican site as Project Pages, as described above.
+
+User Pages
+----------
+
+To publish a Pelican site in the form of User Pages, you need to *push* the
+content of the ``output`` dir generated by Pelican to the ``master`` branch of
+your ``.github.io`` repository on GitHub.
+
+Again, you can take advantage of ``ghp-import``::
+
+ $ pelican content -o output -s pelicanconf.py
+ $ ghp-import output
+ $ 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
+by the ``ghp-import`` command) to the ``elemoine.github.io`` repository's
+``master`` branch on GitHub.
+
+.. note::
+
+ To publish your Pelican site as User Pages, feel free to adjust the
+ ``github`` target of the Makefile.
+
+Extra Tips
+----------
+
+Tip #1:
+
+To automatically update your Pelican site on each commit, you can create
+a post-commit hook. For example, you can add the following to
+``.git/hooks/post-commit``::
+
+ pelican content -o output -s pelicanconf.py && ghp-import output && git push origin gh-pages
+
+Tip #2:
+
+To use a `custom domain
+`_ with
+GitHub Pages, you need to put the domain of your site (e.g.,
+``blog.example.com``) inside a ``CNAME`` file at the root of your site. To do
+this, create the ``content/extra/`` directory and add a ``CNAME`` file to it.
+Then use the ``STATIC_PATHS`` setting to tell Pelican to copy this file to your
+output directory. For example::
+
+ STATIC_PATHS = ['images', 'extra/CNAME']
+ EXTRA_PATH_METADATA = {'extra/CNAME': {'path': 'CNAME'},}
+
+How to add YouTube or Vimeo Videos
+==================================
+
+The easiest way is to paste the embed code of the video from these sites
+directly into your source content.
+
+Alternatively, you can also use Pelican plugins like ``liquid_tags``,
+``pelican_youtube``, or ``pelican_vimeo`` to embed videos in your content.
+
+Moreover, markup languages like reST and Markdown have plugins that let you
+embed videos in the markup. You can use `reST video directive
+`_ for reST or `mdx_video plugin
+`_ for Markdown.
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 6f5d9f44..00000000
--- a/package-lock.json
+++ /dev/null
@@ -1,1556 +0,0 @@
-{
- "name": "pelican-theme",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "devDependencies": {
- "@tailwindcss/typography": "^0.5.15",
- "tailwindcss": "^3.4.17"
- }
- },
- "node_modules/@alloc/quick-lru": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
- "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
- "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@tailwindcss/typography": {
- "version": "0.5.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz",
- "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "lodash.castarray": "^4.4.0",
- "lodash.isplainobject": "^4.0.6",
- "lodash.merge": "^4.6.2",
- "postcss-selector-parser": "6.0.10"
- },
- "peerDependencies": {
- "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/any-promise": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/arg": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/camelcase-css": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
- "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/chokidar/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/commander": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "cssesc": "bin/cssesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/didyoumean": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/dlv": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fast-glob": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
- "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.4"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fastq": {
- "version": "1.17.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
- "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/foreground-child": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
- "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-core-module": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz",
- "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
- }
- },
- "node_modules/jiti": {
- "version": "1.21.7",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
- "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "jiti": "bin/jiti.js"
- }
- },
- "node_modules/lilconfig": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/antonk52"
- }
- },
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.castarray": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
- "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/mz": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
- "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "any-promise": "^1.0.0",
- "object-assign": "^4.0.1",
- "thenify-all": "^1.0.0"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-hash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/package-json-from-dist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
- "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
- "dev": true,
- "license": "BlueOak-1.0.0"
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path-scurry": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
- "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^10.2.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
- },
- "engines": {
- "node": ">=16 || 14 >=14.18"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pirates": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
- "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/postcss": {
- "version": "8.4.49",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
- "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-import": {
- "version": "15.1.0",
- "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
- "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.0.0",
- "read-cache": "^1.0.0",
- "resolve": "^1.1.7"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "postcss": "^8.0.0"
- }
- },
- "node_modules/postcss-js": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
- "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "camelcase-css": "^2.0.1"
- },
- "engines": {
- "node": "^12 || ^14 || >= 16"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- "peerDependencies": {
- "postcss": "^8.4.21"
- }
- },
- "node_modules/postcss-load-config": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
- "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "lilconfig": "^3.0.0",
- "yaml": "^2.3.4"
- },
- "engines": {
- "node": ">= 14"
- },
- "peerDependencies": {
- "postcss": ">=8.0.9",
- "ts-node": ">=9.0.0"
- },
- "peerDependenciesMeta": {
- "postcss": {
- "optional": true
- },
- "ts-node": {
- "optional": true
- }
- }
- },
- "node_modules/postcss-nested": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
- "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "postcss-selector-parser": "^6.1.1"
- },
- "engines": {
- "node": ">=12.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.14"
- }
- },
- "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-selector-parser": {
- "version": "6.0.10",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
- "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/read-cache": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
- "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pify": "^2.3.0"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/resolve": {
- "version": "1.22.9",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz",
- "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/string-width-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/sucrase": {
- "version": "3.35.0",
- "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
- "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.2",
- "commander": "^4.0.0",
- "glob": "^10.3.10",
- "lines-and-columns": "^1.1.6",
- "mz": "^2.7.0",
- "pirates": "^4.0.1",
- "ts-interface-checker": "^0.1.9"
- },
- "bin": {
- "sucrase": "bin/sucrase",
- "sucrase-node": "bin/sucrase-node"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/tailwindcss": {
- "version": "3.4.17",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
- "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@alloc/quick-lru": "^5.2.0",
- "arg": "^5.0.2",
- "chokidar": "^3.6.0",
- "didyoumean": "^1.2.2",
- "dlv": "^1.1.3",
- "fast-glob": "^3.3.2",
- "glob-parent": "^6.0.2",
- "is-glob": "^4.0.3",
- "jiti": "^1.21.6",
- "lilconfig": "^3.1.3",
- "micromatch": "^4.0.8",
- "normalize-path": "^3.0.0",
- "object-hash": "^3.0.0",
- "picocolors": "^1.1.1",
- "postcss": "^8.4.47",
- "postcss-import": "^15.1.0",
- "postcss-js": "^4.0.1",
- "postcss-load-config": "^4.0.2",
- "postcss-nested": "^6.2.0",
- "postcss-selector-parser": "^6.1.2",
- "resolve": "^1.22.8",
- "sucrase": "^3.35.0"
- },
- "bin": {
- "tailwind": "lib/cli.js",
- "tailwindcss": "lib/cli.js"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tailwindcss/node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/thenify": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
- "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "any-promise": "^1.0.0"
- }
- },
- "node_modules/thenify-all": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
- "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "thenify": ">= 3.1.0 < 4"
- },
- "engines": {
- "node": ">=0.8"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/ts-interface-checker": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/wrap-ansi-cjs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yaml": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
- "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14"
- }
- }
- }
-}
diff --git a/package.json b/package.json
deleted file mode 100644
index 969943e4..00000000
--- a/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "devDependencies": {
- "@tailwindcss/typography": "^0.5.15",
- "tailwindcss": "^3.4.17"
- }
-}
diff --git a/pelican/__init__.py b/pelican/__init__.py
new file mode 100644
index 00000000..455201be
--- /dev/null
+++ b/pelican/__init__.py
@@ -0,0 +1,431 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+
+import os
+import re
+import sys
+import time
+import logging
+import argparse
+import locale
+import collections
+
+# 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 import signals
+
+from pelican.generators import (ArticlesGenerator, PagesGenerator,
+ StaticGenerator, SourceFileGenerator,
+ TemplatePagesGenerator)
+from pelican.readers import Readers
+from pelican.settings import read_settings
+from pelican.utils import clean_output_dir, folder_watcher, file_watcher
+from pelican.writers import Writer
+
+__version__ = "3.5.dev"
+
+DEFAULT_CONFIG_NAME = 'pelicanconf.py'
+
+
+logger = logging.getLogger(__name__)
+
+
+class Pelican(object):
+
+ def __init__(self, settings):
+ """
+ Pelican initialisation, performs some checks on the environment before
+ doing anything else.
+ """
+
+ # define the default settings
+ self.settings = settings
+ self._handle_deprecation()
+
+ self.path = settings['PATH']
+ self.theme = settings['THEME']
+ self.output_path = settings['OUTPUT_PATH']
+ self.ignore_files = settings['IGNORE_FILES']
+ self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
+ self.output_retention = settings['OUTPUT_RETENTION']
+
+ self.init_path()
+ self.init_plugins()
+ signals.initialized.send(self)
+
+ def init_path(self):
+ if not any(p in sys.path for p in ['', os.curdir]):
+ logger.debug("Adding current directory to system path")
+ sys.path.insert(0, '')
+
+ def init_plugins(self):
+ self.plugins = []
+ logger.debug('Temporarily adding PLUGIN_PATHS to system path')
+ _sys_path = sys.path[:]
+ for pluginpath in self.settings['PLUGIN_PATHS']:
+ sys.path.insert(0, pluginpath)
+ for plugin in self.settings['PLUGINS']:
+ # if it's a string, then import it
+ if isinstance(plugin, six.string_types):
+ logger.debug("Loading plugin `%s`", plugin)
+ try:
+ plugin = __import__(plugin, globals(), locals(),
+ str('module'))
+ except ImportError as e:
+ logger.error(
+ "Cannot load plugin `%s`\n%s", plugin, e)
+ continue
+
+ logger.debug("Registering plugin `%s`", plugin.__name__)
+ plugin.register()
+ self.plugins.append(plugin)
+ logger.debug('Restoring system path')
+ sys.path = _sys_path
+
+ def _handle_deprecation(self):
+
+ if self.settings.get('CLEAN_URLS', False):
+ logger.warning('Found deprecated `CLEAN_URLS` in settings.'
+ ' Modifying the following settings for the'
+ ' same behaviour.')
+
+ self.settings['ARTICLE_URL'] = '{slug}/'
+ self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
+ self.settings['PAGE_URL'] = 'pages/{slug}/'
+ self.settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/'
+
+ for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
+ 'PAGE_LANG_URL'):
+ logger.warning("%s = '%s'", setting, self.settings[setting])
+
+ if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
+ logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
+ ' settings. Modifying the following settings for'
+ ' the same behaviour.')
+
+ structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
+
+ # Convert %(variable) into {variable}.
+ structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure)
+
+ # Convert %x into {date:%x} for strftime
+ structure = re.sub('(%[A-z])', '{date:\g<1>}', structure)
+
+ # Strip a / prefix
+ structure = re.sub('^/', '', structure)
+
+ for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
+ 'PAGE_LANG_URL', 'DRAFT_URL', 'DRAFT_LANG_URL',
+ 'ARTICLE_SAVE_AS', 'ARTICLE_LANG_SAVE_AS',
+ 'DRAFT_SAVE_AS', 'DRAFT_LANG_SAVE_AS',
+ 'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'):
+ self.settings[setting] = os.path.join(structure,
+ self.settings[setting])
+ logger.warning("%s = '%s'", setting, self.settings[setting])
+
+ for new, old in [('FEED', 'FEED_ATOM'), ('TAG_FEED', 'TAG_FEED_ATOM'),
+ ('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'),
+ ('TRANSLATION_FEED', 'TRANSLATION_FEED_ATOM')]:
+ if self.settings.get(new, False):
+ logger.warning(
+ 'Found deprecated `%(new)s` in settings. Modify %(new)s '
+ 'to %(old)s in your settings and theme for the same '
+ 'behavior. Temporarily setting %(old)s for backwards '
+ 'compatibility.',
+ {'new': new, 'old': old}
+ )
+ self.settings[old] = self.settings[new]
+
+ def run(self):
+ """Run the generators and return"""
+ start_time = time.time()
+
+ context = self.settings.copy()
+ # Share these among all the generators and content objects:
+ context['filenames'] = {} # maps source path to Content object or None
+ context['localsiteurl'] = self.settings['SITEURL']
+
+ generators = [
+ cls(
+ context=context,
+ settings=self.settings,
+ path=self.path,
+ theme=self.theme,
+ output_path=self.output_path,
+ ) for cls in self.get_generator_classes()
+ ]
+
+ # erase the directory if it is not the source and if that's
+ # explicitely asked
+ if (self.delete_outputdir and not
+ os.path.realpath(self.path).startswith(self.output_path)):
+ clean_output_dir(self.output_path, self.output_retention)
+
+ for p in generators:
+ if hasattr(p, 'generate_context'):
+ p.generate_context()
+
+ writer = self.get_writer()
+
+ for p in generators:
+ if hasattr(p, 'generate_output'):
+ p.generate_output(writer)
+
+ signals.finalized.send(self)
+
+ articles_generator = next(g for g in generators
+ if isinstance(g, ArticlesGenerator))
+ pages_generator = next(g for g in generators
+ if isinstance(g, PagesGenerator))
+
+ print('Done: Processed {} article(s), {} draft(s) and {} page(s) in ' \
+ '{:.2f} seconds.'.format(
+ len(articles_generator.articles) + len(articles_generator.translations),
+ len(articles_generator.drafts) + \
+ len(articles_generator.drafts_translations),
+ len(pages_generator.pages) + len(pages_generator.translations),
+ time.time() - start_time))
+
+ def get_generator_classes(self):
+ generators = [ArticlesGenerator, PagesGenerator]
+
+ if self.settings['TEMPLATE_PAGES']:
+ generators.append(TemplatePagesGenerator)
+ if self.settings['OUTPUT_SOURCES']:
+ generators.append(SourceFileGenerator)
+
+ for pair in signals.get_generators.send(self):
+ (funct, value) = pair
+
+ if not isinstance(value, collections.Iterable):
+ value = (value, )
+
+ for v in value:
+ if isinstance(v, type):
+ logger.debug('Found generator: %s', v)
+ generators.append(v)
+
+ # StaticGenerator runs last so it can see which files the others handle
+ generators.append(StaticGenerator)
+ return generators
+
+ def get_writer(self):
+ writers = [ w for (_, w) in signals.get_writer.send(self)
+ if isinstance(w, type) ]
+ writers_found = len(writers)
+ if writers_found == 0:
+ return Writer(self.output_path, settings=self.settings)
+ else:
+ writer = writers[0]
+ if writers_found == 1:
+ logger.debug('Found writer: %s', writer)
+ else:
+ logger.warning(
+ '%s writers found, using only first one: %s',
+ writers_found, writer)
+ return writer(self.output_path, settings=self.settings)
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(
+ description="""A tool to generate a static blog,
+ with restructured text input files.""",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
+ )
+
+ parser.add_argument(dest='path', nargs='?',
+ help='Path where to find the content files.',
+ default=None)
+
+ parser.add_argument('-t', '--theme-path', dest='theme',
+ help='Path where to find the theme templates. If not '
+ 'specified, it will use the default one included with '
+ 'pelican.')
+
+ parser.add_argument('-o', '--output', dest='output',
+ help='Where to output the generated files. If not '
+ 'specified, a directory will be created, named '
+ '"output" in the current path.')
+
+ parser.add_argument('-s', '--settings', dest='settings',
+ help='The settings of the application, this is '
+ 'automatically set to {0} if a file exists with this '
+ 'name.'.format(DEFAULT_CONFIG_NAME))
+
+ parser.add_argument('-d', '--delete-output-directory',
+ dest='delete_outputdir', action='store_true',
+ default=None, help='Delete the output directory.')
+
+ parser.add_argument('-v', '--verbose', action='store_const',
+ const=logging.INFO, dest='verbosity',
+ help='Show all messages.')
+
+ parser.add_argument('-q', '--quiet', action='store_const',
+ const=logging.CRITICAL, dest='verbosity',
+ help='Show only critical errors.')
+
+ parser.add_argument('-D', '--debug', action='store_const',
+ const=logging.DEBUG, dest='verbosity',
+ help='Show all messages, including debug messages.')
+
+ parser.add_argument('--version', action='version', version=__version__,
+ help='Print the pelican version and exit.')
+
+ parser.add_argument('-r', '--autoreload', dest='autoreload',
+ action='store_true',
+ help='Relaunch pelican each time a modification occurs'
+ ' on the content files.')
+
+ parser.add_argument('--cache-path', dest='cache_path',
+ help=('Directory in which to store cache files. '
+ 'If not specified, defaults to "cache".'))
+
+ parser.add_argument('--ignore-cache', action='store_true',
+ dest='ignore_cache', help='Ignore content cache '
+ 'from previous runs by not loading cache files.')
+
+ parser.add_argument('-w', '--write-selected', type=str,
+ dest='selected_paths', default=None,
+ help='Comma separated list of selected paths to write')
+
+ return parser.parse_args()
+
+
+def get_config(args):
+ config = {}
+ if args.path:
+ config['PATH'] = os.path.abspath(os.path.expanduser(args.path))
+ if args.output:
+ config['OUTPUT_PATH'] = \
+ os.path.abspath(os.path.expanduser(args.output))
+ if args.theme:
+ abstheme = os.path.abspath(os.path.expanduser(args.theme))
+ config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme
+ if args.delete_outputdir is not None:
+ config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
+ if args.ignore_cache:
+ config['LOAD_CONTENT_CACHE'] = False
+ if args.cache_path:
+ config['CACHE_PATH'] = args.cache_path
+ if args.selected_paths:
+ config['WRITE_SELECTED'] = args.selected_paths.split(',')
+ config['DEBUG'] = args.verbosity == logging.DEBUG
+
+ # argparse returns bytes in Py2. There is no definite answer as to which
+ # encoding argparse (or sys.argv) uses.
+ # "Best" option seems to be locale.getpreferredencoding()
+ # ref: http://mail.python.org/pipermail/python-list/2006-October/405766.html
+ if not six.PY3:
+ enc = locale.getpreferredencoding()
+ for key in config:
+ if key in ('PATH', 'OUTPUT_PATH', 'THEME'):
+ config[key] = config[key].decode(enc)
+ return config
+
+
+def get_instance(args):
+
+ config_file = args.settings
+ if config_file is None and os.path.isfile(DEFAULT_CONFIG_NAME):
+ config_file = DEFAULT_CONFIG_NAME
+
+ settings = read_settings(config_file, override=get_config(args))
+
+ cls = settings['PELICAN_CLASS']
+ if isinstance(cls, six.string_types):
+ module, cls_name = cls.rsplit('.', 1)
+ module = __import__(module)
+ cls = getattr(module, cls_name)
+
+ return cls(settings), settings
+
+
+def main():
+ args = parse_arguments()
+ init(args.verbosity)
+ pelican, settings = get_instance(args)
+ readers = Readers(settings)
+
+ watchers = {'content': folder_watcher(pelican.path,
+ readers.extensions,
+ pelican.ignore_files),
+ 'theme': folder_watcher(pelican.theme,
+ [''],
+ pelican.ignore_files),
+ 'settings': file_watcher(args.settings)}
+
+ for static_path in settings.get("STATIC_PATHS", []):
+ watchers[static_path] = folder_watcher(static_path, [''], pelican.ignore_files)
+
+ try:
+ if args.autoreload:
+ print(' --- AutoReload Mode: Monitoring `content`, `theme` and'
+ ' `settings` for changes. ---')
+
+ def _ignore_cache(pelican_obj):
+ if pelican_obj.settings['AUTORELOAD_IGNORE_CACHE']:
+ pelican_obj.settings['LOAD_CONTENT_CACHE'] = False
+
+ 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()}
+ original_load_cache = settings['LOAD_CONTENT_CACHE']
+
+ if modified['settings']:
+ pelican, settings = get_instance(args)
+ original_load_cache = settings['LOAD_CONTENT_CACHE']
+ _ignore_cache(pelican)
+
+ 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()
+ # restore original caching policy
+ pelican.settings['LOAD_CONTENT_CACHE'] = original_load_cache
+
+ except KeyboardInterrupt:
+ logger.warning("Keyboard interrupt, quitting.")
+ break
+
+ except Exception as e:
+ if (args.verbosity == logging.DEBUG):
+ logger.critical(e.args)
+ raise
+ logger.warning(
+ 'Caught exception "%s". Reloading.', e)
+
+ finally:
+ time.sleep(.5) # sleep to avoid cpu load
+
+ else:
+ if next(watchers['content']) is None:
+ logger.warning('No valid files found in content.')
+
+ if next(watchers['theme']) is None:
+ logger.warning('Empty theme folder. Using `basic` theme.')
+
+ pelican.run()
+
+ except Exception as e:
+ logger.critical('%s', e)
+
+ if args.verbosity == logging.DEBUG:
+ raise
+ else:
+ sys.exit(getattr(e, 'exitcode', 1))
diff --git a/pelican/contents.py b/pelican/contents.py
new file mode 100644
index 00000000..433d1ac7
--- /dev/null
+++ b/pelican/contents.py
@@ -0,0 +1,370 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+from six.moves.urllib.parse import (unquote, urlparse, urlunparse)
+
+import copy
+import locale
+import logging
+import functools
+import os
+import re
+import sys
+
+
+from pelican import signals
+from pelican.settings import DEFAULT_CONFIG
+from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
+ python_2_unicode_compatible, deprecated_attribute,
+ path_to_url, set_date_tzinfo, SafeDatetime)
+
+# Import these so that they're avalaible when you import from pelican.contents.
+from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+class Content(object):
+ """Represents a content.
+
+ :param content: the string to parse, containing the original content.
+ :param metadata: the metadata associated to this page (optional).
+ :param settings: the settings dictionary (optional).
+ :param source_path: The location of the source of this content (if any).
+ :param context: The shared context between generators.
+
+ """
+ @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))
+ def filename():
+ return None
+
+ def __init__(self, content, metadata=None, settings=None,
+ source_path=None, context=None):
+ if metadata is None:
+ metadata = {}
+ if settings is None:
+ settings = copy.deepcopy(DEFAULT_CONFIG)
+
+ self.settings = settings
+ self._content = content
+ if context is None:
+ context = {}
+ self._context = context
+ self.translations = []
+
+ local_metadata = dict(settings['DEFAULT_METADATA'])
+ local_metadata.update(metadata)
+
+ # set metadata as attributes
+ for key, value in local_metadata.items():
+ if key in ('save_as', 'url'):
+ key = 'override_' + key
+ setattr(self, key.lower(), value)
+
+ # also keep track of the metadata attributes available
+ self.metadata = local_metadata
+
+ #default template if it's not defined in page
+ self.template = self._get_template()
+
+ # First, read the authors from "authors", if not, fallback to "author"
+ # and if not use the settings defined one, if any.
+ if not hasattr(self, 'author'):
+ if hasattr(self, 'authors'):
+ self.author = self.authors[0]
+ elif 'AUTHOR' in settings:
+ self.author = Author(settings['AUTHOR'], settings)
+
+ if not hasattr(self, 'authors') and hasattr(self, 'author'):
+ self.authors = [self.author]
+
+ # XXX Split all the following code into pieces, there is too much here.
+
+ # manage languages
+ self.in_default_lang = True
+ if 'DEFAULT_LANG' in settings:
+ default_lang = settings['DEFAULT_LANG'].lower()
+ if not hasattr(self, 'lang'):
+ self.lang = default_lang
+
+ self.in_default_lang = (self.lang == default_lang)
+
+ # create the slug if not existing, generate slug according to
+ # setting of SLUG_ATTRIBUTE
+ if not hasattr(self, 'slug'):
+ if settings['SLUGIFY_SOURCE'] == 'title' and hasattr(self, 'title'):
+ self.slug = slugify(self.title,
+ settings.get('SLUG_SUBSTITUTIONS', ()))
+ elif settings['SLUGIFY_SOURCE'] == 'basename' and source_path != None:
+ basename = os.path.basename(os.path.splitext(source_path)[0])
+ self.slug = slugify(basename,
+ settings.get('SLUG_SUBSTITUTIONS', ()))
+
+ self.source_path = source_path
+
+ # manage the date format
+ if not hasattr(self, 'date_format'):
+ if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']:
+ self.date_format = settings['DATE_FORMATS'][self.lang]
+ else:
+ self.date_format = settings['DEFAULT_DATE_FORMAT']
+
+ if isinstance(self.date_format, tuple):
+ locale_string = self.date_format[0]
+ if sys.version_info < (3, ) and isinstance(locale_string,
+ six.text_type):
+ locale_string = locale_string.encode('ascii')
+ locale.setlocale(locale.LC_ALL, locale_string)
+ self.date_format = self.date_format[1]
+
+ # manage timezone
+ default_timezone = settings.get('TIMEZONE', 'UTC')
+ timezone = getattr(self, 'timezone', default_timezone)
+
+ if hasattr(self, 'date'):
+ self.date = set_date_tzinfo(self.date, timezone)
+ self.locale_date = strftime(self.date, self.date_format)
+
+ if hasattr(self, 'modified'):
+ self.modified = set_date_tzinfo(self.modified, timezone)
+ self.locale_modified = strftime(self.modified, self.date_format)
+
+ # manage status
+ if not hasattr(self, 'status'):
+ self.status = settings['DEFAULT_STATUS']
+ if not settings['WITH_FUTURE_DATES']:
+ if hasattr(self, 'date') and self.date > SafeDatetime.now():
+ self.status = 'draft'
+
+ # store the summary metadata if it is set
+ if 'summary' in metadata:
+ self._summary = metadata['summary']
+
+ signals.content_object_init.send(self)
+
+ def __str__(self):
+ if self.source_path is None:
+ return repr(self)
+ elif six.PY3:
+ return self.source_path or repr(self)
+ else:
+ return str(self.source_path.encode('utf-8', 'replace'))
+
+ def check_properties(self):
+ """Test mandatory properties are set."""
+ for prop in self.mandatory_properties:
+ if not hasattr(self, prop):
+ raise NameError(prop)
+
+ @property
+ def url_format(self):
+ """Returns the URL, formatted with the proper values"""
+ metadata = copy.copy(self.metadata)
+ path = self.metadata.get('path', self.get_relative_source_path())
+ default_category = self.settings['DEFAULT_CATEGORY']
+ slug_substitutions = self.settings.get('SLUG_SUBSTITUTIONS', ())
+ metadata.update({
+ 'path': path_to_url(path),
+ 'slug': getattr(self, 'slug', ''),
+ 'lang': getattr(self, 'lang', 'en'),
+ 'date': getattr(self, 'date', SafeDatetime.now()),
+ 'author': slugify(
+ getattr(self, 'author', ''),
+ slug_substitutions
+ ),
+ 'category': slugify(
+ getattr(self, 'category', default_category),
+ slug_substitutions
+ )
+ })
+ return metadata
+
+ def _expand_settings(self, key):
+ fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper()
+ return self.settings[fq_key].format(**self.url_format)
+
+ def get_url_setting(self, key):
+ if hasattr(self, 'override_' + key):
+ return getattr(self, 'override_' + key)
+ key = key if self.in_default_lang else 'lang_%s' % key
+ return self._expand_settings(key)
+
+ def _update_content(self, content, siteurl):
+ """Update the content attribute.
+
+ Change all the relative paths of the content to relative paths
+ suitable for the ouput content.
+
+ :param content: content resource that will be passed to the templates.
+ :param siteurl: siteurl which is locally generated by the writer in
+ case of RELATIVE_URLS.
+ """
+ if not content:
+ return content
+
+ instrasite_link_regex = self.settings['INTRASITE_LINK_REGEX']
+ regex = r"""
+ (?P<\s*[^\>]* # match tag with all url-value attributes
+ (?:href|src|poster|data|cite|formaction|action)\s*=)
+
+ (?P["\']) # require value to be quoted
+ (?P{0}(?P.*?)) # the url value
+ \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 == 'filename':
+ 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
+
+ if self._context['filenames'].get(path):
+ origin = '/'.join((siteurl,
+ self._context['filenames'][path].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 = Category(path, self.settings).url
+ elif what == 'tag':
+ origin = Tag(path, self.settings).url
+
+ # 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)
+
+ @memoized
+ def get_content(self, siteurl):
+
+ if hasattr(self, '_get_content'):
+ content = self._get_content()
+ else:
+ content = self._content
+ return self._update_content(content, siteurl)
+
+ @property
+ def content(self):
+ return self.get_content(self._context.get('localsiteurl', ''))
+
+ def _get_summary(self):
+ """Returns the summary of an article.
+
+ This is based on the summary metadata if set, otherwise truncate the
+ content.
+ """
+ if hasattr(self, '_summary'):
+ return self._summary
+
+ if self.settings['SUMMARY_MAX_LENGTH'] is None:
+ return self.content
+
+ return truncate_html_words(self.content,
+ self.settings['SUMMARY_MAX_LENGTH'])
+
+ def _set_summary(self, summary):
+ """Dummy function"""
+ pass
+
+ summary = property(_get_summary, _set_summary, "Summary of the article."
+ "Based on the content. Can't be set")
+ url = property(functools.partial(get_url_setting, key='url'))
+ save_as = property(functools.partial(get_url_setting, key='save_as'))
+
+ def _get_template(self):
+ if hasattr(self, 'template') and self.template is not None:
+ return self.template
+ else:
+ return self.default_template
+
+ def get_relative_source_path(self, source_path=None):
+ """Return the relative path (from the content path) to the given
+ source_path.
+
+ If no source path is specified, use the source path of this
+ content object.
+ """
+ if not source_path:
+ source_path = self.source_path
+ if source_path is None:
+ return None
+
+ return os.path.relpath(
+ os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
+ os.path.abspath(self.settings['PATH'])
+ )
+
+ @property
+ def relative_dir(self):
+ return os.path.dirname(os.path.relpath(
+ os.path.abspath(self.source_path),
+ os.path.abspath(self.settings['PATH']))
+ )
+
+
+class Page(Content):
+ mandatory_properties = ('title',)
+ default_template = 'page'
+
+
+class Article(Page):
+ mandatory_properties = ('title', 'date', 'category')
+ default_template = 'article'
+
+
+class Draft(Page):
+ mandatory_properties = ('title', 'category')
+ default_template = 'article'
+
+
+class Quote(Page):
+ base_properties = ('author', 'date')
+
+
+@python_2_unicode_compatible
+class Static(Page):
+ @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))
+ def filepath():
+ return None
+
+ @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0))
+ def src():
+ return None
+
+ @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0))
+ def dst():
+ return None
+
+
+def is_valid_content(content, f):
+ try:
+ content.check_properties()
+ return True
+ except NameError as e:
+ logger.error("Skipping %s: could not find information about '%s'", f, e)
+ return False
diff --git a/pelican/generators.py b/pelican/generators.py
new file mode 100644
index 00000000..4d5cb6cb
--- /dev/null
+++ b/pelican/generators.py
@@ -0,0 +1,737 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+import os
+import six
+import math
+import random
+import logging
+import shutil
+import fnmatch
+import calendar
+
+from codecs import open
+from collections import defaultdict
+from functools import partial
+from itertools import chain, groupby
+from operator import attrgetter, itemgetter
+
+from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader,
+ BaseLoader, TemplateNotFound)
+
+from pelican.contents import Article, Draft, Page, Static, is_valid_content
+from pelican.readers import Readers
+from pelican.utils import (copy, process_translations, mkdir_p, DateFormatter,
+ FileStampDataCacher, python_2_unicode_compatible)
+from pelican import signals
+
+
+logger = logging.getLogger(__name__)
+
+
+@python_2_unicode_compatible
+class Generator(object):
+ """Baseclass generator"""
+
+ def __init__(self, context, settings, path, theme, output_path,
+ readers_cache_name='', **kwargs):
+ self.context = context
+ self.settings = settings
+ self.path = path
+ self.theme = theme
+ self.output_path = output_path
+
+ for arg, value in kwargs.items():
+ setattr(self, arg, value)
+
+ self.readers = Readers(self.settings, readers_cache_name)
+
+ # 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']
+
+ theme_path = os.path.dirname(os.path.abspath(__file__))
+
+ simple_loader = FileSystemLoader(os.path.join(theme_path,
+ "themes", "simple", "templates"))
+ self.env = Environment(
+ trim_blocks=True,
+ lstrip_blocks=True,
+ loader=ChoiceLoader([
+ FileSystemLoader(self._templates_path),
+ simple_loader, # implicit inheritance
+ PrefixLoader({'!simple': simple_loader}) # explicit one
+ ]),
+ extensions=self.settings['JINJA_EXTENSIONS'],
+ )
+
+ logger.debug('Template list: %s', self.env.list_templates())
+
+ # provide utils.strftime as a jinja filter
+ self.env.filters.update({'strftime': DateFormatter()})
+
+ # get custom Jinja filters from user settings
+ custom_filters = self.settings['JINJA_FILTERS']
+ self.env.filters.update(custom_filters)
+
+ signals.generator_init.send(self)
+
+ def get_template(self, name):
+ """Return the template by name.
+ Use self.theme to get the templates to use, and return a list of
+ templates ready to use with Jinja2.
+ """
+ if name not in self._templates:
+ try:
+ self._templates[name] = self.env.get_template(name + '.html')
+ except TemplateNotFound:
+ raise Exception('[templates] unable to load %s.html from %s'
+ % (name, self._templates_path))
+ return self._templates[name]
+
+ def _include_path(self, path, extensions=None):
+ """Inclusion logic for .get_files(), returns True/False
+
+ :param path: the path which might be including
+ :param extensions: the list of allowed extensions (if False, all
+ extensions are allowed)
+ """
+ if extensions is None:
+ extensions = tuple(self.readers.extensions)
+ basename = os.path.basename(path)
+
+ #check IGNORE_FILES
+ ignores = self.settings['IGNORE_FILES']
+ if any(fnmatch.fnmatch(basename, ignore) for ignore in ignores):
+ return False
+
+ if extensions is False or basename.endswith(extensions):
+ return True
+ return False
+
+ def get_files(self, paths, exclude=[], extensions=None):
+ """Return a list of files to use, based on rules
+
+ :param paths: the list pf paths to search (relative to self.path)
+ :param exclude: the list of path to exclude
+ :param extensions: the list of allowed extensions (if False, all
+ extensions are allowed)
+ """
+ if isinstance(paths, six.string_types):
+ paths = [paths] # backward compatibility for older generators
+ files = []
+ for path in paths:
+ root = os.path.join(self.path, path)
+
+ if os.path.isdir(root):
+ for dirpath, dirs, temp_files in os.walk(root, followlinks=True):
+ for e in exclude:
+ if e in dirs:
+ dirs.remove(e)
+ reldir = os.path.relpath(dirpath, self.path)
+ for f in temp_files:
+ fp = os.path.join(reldir, f)
+ if self._include_path(fp, extensions):
+ files.append(fp)
+ elif os.path.exists(root) and self._include_path(path, extensions):
+ files.append(path) # can't walk non-directories
+ return files
+
+ def add_source_path(self, content):
+ """Record a source file path that a Generator found and processed.
+ Store a reference to its Content object, for url lookups later.
+ """
+ location = content.get_relative_source_path()
+ self.context['filenames'][location] = content
+
+ def _add_failed_source_path(self, path):
+ """Record a source file path that a Generator failed to process.
+ (For example, one that was missing mandatory metadata.)
+ The path argument is expected to be relative to self.path.
+ """
+ self.context['filenames'][os.path.normpath(path)] = None
+
+ def _is_potential_source_path(self, path):
+ """Return True if path was supposed to be used as a source file.
+ (This includes all source files that have been found by generators
+ before this method is called, even if they failed to process.)
+ The path argument is expected to be relative to self.path.
+ """
+ return os.path.normpath(path) in self.context['filenames']
+
+ def _update_context(self, items):
+ """Update the context with the given items from the currrent
+ processor.
+ """
+ for item in items:
+ value = getattr(self, item)
+ if hasattr(value, 'items'):
+ value = list(value.items()) # py3k safeguard for iterators
+ self.context[item] = value
+
+ def __str__(self):
+ # return the name of the class for logging purposes
+ return self.__class__.__name__
+
+
+class CachingGenerator(Generator, FileStampDataCacher):
+ '''Subclass of Generator and FileStampDataCacher classes
+
+ enables content caching, either at the generator or reader level
+ '''
+
+ def __init__(self, *args, **kwargs):
+ '''Initialize the generator, then set up caching
+
+ note the multiple inheritance structure
+ '''
+ cls_name = self.__class__.__name__
+ Generator.__init__(self, *args,
+ readers_cache_name=(cls_name + '-Readers'),
+ **kwargs)
+
+ cache_this_level = self.settings['CONTENT_CACHING_LAYER'] == 'generator'
+ caching_policy = cache_this_level and self.settings['CACHE_CONTENT']
+ load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE']
+ FileStampDataCacher.__init__(self, self.settings, cls_name,
+ caching_policy, load_policy
+ )
+
+ def _get_file_stamp(self, filename):
+ '''Get filestamp for path relative to generator.path'''
+ filename = os.path.join(self.path, filename)
+ return super(CachingGenerator, self)._get_file_stamp(filename)
+
+
+class _FileLoader(BaseLoader):
+
+ def __init__(self, path, basedir):
+ self.path = path
+ self.fullpath = os.path.join(basedir, path)
+
+ def get_source(self, environment, template):
+ if template != self.path or not os.path.exists(self.fullpath):
+ raise TemplateNotFound(template)
+ mtime = os.path.getmtime(self.fullpath)
+ with open(self.fullpath, 'r', encoding='utf-8') as f:
+ source = f.read()
+ return (source, self.fullpath,
+ lambda: mtime == os.path.getmtime(self.fullpath))
+
+
+class TemplatePagesGenerator(Generator):
+
+ def generate_output(self, writer):
+ for source, dest in self.settings['TEMPLATE_PAGES'].items():
+ self.env.loader.loaders.insert(0, _FileLoader(source, self.path))
+ try:
+ template = self.env.get_template(source)
+ rurls = self.settings['RELATIVE_URLS']
+ writer.write_file(dest, template, self.context, rurls,
+ override_output=True)
+ finally:
+ del self.env.loader.loaders[0]
+
+
+class ArticlesGenerator(CachingGenerator):
+ """Generate blog articles"""
+
+ def __init__(self, *args, **kwargs):
+ """initialize properties"""
+ self.articles = [] # only articles in default language
+ self.translations = []
+ self.dates = {}
+ self.tags = defaultdict(list)
+ self.categories = defaultdict(list)
+ self.related_posts = []
+ self.authors = defaultdict(list)
+ self.drafts = [] # only drafts in default language
+ self.drafts_translations = []
+ super(ArticlesGenerator, self).__init__(*args, **kwargs)
+ signals.article_generator_init.send(self)
+
+ def generate_feeds(self, writer):
+ """Generate the feeds from the current context, and output files."""
+
+ if self.settings.get('FEED_ATOM'):
+ writer.write_feed(self.articles, self.context,
+ self.settings['FEED_ATOM'])
+
+ if self.settings.get('FEED_RSS'):
+ writer.write_feed(self.articles, self.context,
+ 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)
+
+ if self.settings.get('FEED_ALL_ATOM'):
+ writer.write_feed(all_articles, self.context,
+ self.settings['FEED_ALL_ATOM'])
+
+ if self.settings.get('FEED_ALL_RSS'):
+ writer.write_feed(all_articles, self.context,
+ 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)
+
+ if self.settings.get('CATEGORY_FEED_RSS'):
+ writer.write_feed(arts, self.context,
+ self.settings['CATEGORY_FEED_RSS']
+ % cat.slug, 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)
+
+ if self.settings.get('AUTHOR_FEED_RSS'):
+ writer.write_feed(arts, self.context,
+ self.settings['AUTHOR_FEED_RSS']
+ % auth.slug, 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)
+
+ if self.settings.get('TAG_FEED_RSS'):
+ writer.write_feed(arts, self.context,
+ self.settings['TAG_FEED_RSS'] % tag.slug,
+ feed_type='rss')
+
+ if (self.settings.get('TRANSLATION_FEED_ATOM')
+ or self.settings.get('TRANSLATION_FEED_RSS')):
+ translations_feeds = defaultdict(list)
+ for article in chain(self.articles, self.translations):
+ translations_feeds[article.lang].append(article)
+
+ for lang, items in translations_feeds.items():
+ items.sort(key=attrgetter('date'), reverse=True)
+ if self.settings.get('TRANSLATION_FEED_ATOM'):
+ writer.write_feed(
+ items, self.context,
+ 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,
+ feed_type='rss')
+
+ def generate_articles(self, write):
+ """Generate the articles."""
+ for article in chain(self.translations, self.articles):
+ signals.article_generator_write_article.send(self, content=article)
+ write(article.save_as, self.get_template(article.template),
+ self.context, article=article, category=article.category,
+ override_output=hasattr(article, 'override_save_as'))
+
+ def generate_period_archives(self, write):
+ """Generate per-year, per-month, and per-day archives."""
+ try:
+ template = self.get_template('period_archives')
+ except Exception:
+ template = self.get_template('archives')
+
+ period_save_as = {
+ 'year': self.settings['YEAR_ARCHIVE_SAVE_AS'],
+ 'month': self.settings['MONTH_ARCHIVE_SAVE_AS'],
+ 'day': self.settings['DAY_ARCHIVE_SAVE_AS'],
+ }
+
+ 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):
+ """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)
+ # 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)
+ context = self.context.copy()
+
+ if key == period_date_key['year']:
+ context["period"] = (_period,)
+ else:
+ month_name = calendar.month_name[_period[1]]
+ if not six.PY3:
+ month_name = month_name.decode('utf-8')
+ if key == period_date_key['month']:
+ context["period"] = (_period[0],
+ month_name)
+ else:
+ context["period"] = (_period[0],
+ month_name,
+ _period[2])
+
+ write(save_as, template, context,
+ dates=archive, blog=True)
+
+ for period in 'year', 'month', 'day':
+ save_as = period_save_as[period]
+ if save_as:
+ key = period_date_key[period]
+ _generate_period_archives(self.dates, key, save_as)
+
+ 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)
+ 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])
+
+ 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},
+ 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,
+ category=cat, articles=articles, dates=dates,
+ paginated={'articles': articles, 'dates': dates},
+ 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},
+ page_name=aut.page_name, all_articles=self.articles)
+
+ def generate_drafts(self, write):
+ """Generate drafts pages."""
+ for draft in chain(self.drafts_translations, self.drafts):
+ write(draft.save_as, self.get_template(draft.template),
+ self.context, article=draft, category=draft.category,
+ override_output=hasattr(draft, 'override_save_as'),
+ all_articles=self.articles)
+
+ def generate_pages(self, writer):
+ """Generate the pages on the disk"""
+ write = partial(writer.write_file,
+ relative_urls=self.settings['RELATIVE_URLS'])
+
+ # to minimize the number of relative path stuff modification
+ # in writer, articles pass first
+ self.generate_articles(write)
+ self.generate_period_archives(write)
+ self.generate_direct_templates(write)
+
+ # and subfolders after that
+ self.generate_tags(write)
+ self.generate_categories(write)
+ self.generate_authors(write)
+ self.generate_drafts(write)
+
+ def generate_context(self):
+ """Add the articles into the shared context"""
+
+ all_articles = []
+ all_drafts = []
+ for f in self.get_files(
+ self.settings['ARTICLE_PATHS'],
+ exclude=self.settings['ARTICLE_EXCLUDES']):
+ article = self.get_cached_data(f, None)
+ if article is None:
+ try:
+ article = self.readers.read_file(
+ base_path=self.path, path=f, content_class=Article,
+ context=self.context,
+ preread_signal=signals.article_generator_preread,
+ preread_sender=self,
+ context_signal=signals.article_generator_context,
+ context_sender=self)
+ except Exception as e:
+ logger.error('Could not process %s\n%s', f, e,
+ exc_info=self.settings.get('DEBUG', False))
+ self._add_failed_source_path(f)
+ continue
+
+ if not is_valid_content(article, f):
+ self._add_failed_source_path(f)
+ continue
+
+ self.cache_data(f, article)
+
+ self.add_source_path(article)
+
+ if article.status.lower() == "published":
+ all_articles.append(article)
+ elif article.status.lower() == "draft":
+ draft = self.readers.read_file(
+ base_path=self.path, path=f, content_class=Draft,
+ context=self.context,
+ preread_signal=signals.article_generator_preread,
+ preread_sender=self,
+ context_signal=signals.article_generator_context,
+ context_sender=self)
+ all_drafts.append(draft)
+ else:
+ logger.error("Unknown status '%s' for file %s, skipping it.",
+ article.status, f)
+
+ self.articles, self.translations = process_translations(all_articles,
+ order_by=self.settings['ARTICLE_ORDER_BY'])
+ self.drafts, self.drafts_translations = \
+ process_translations(all_drafts)
+
+ signals.article_generator_pretaxonomy.send(self)
+
+ for article in self.articles:
+ # only main articles are listed in categories and tags
+ # not translations
+ self.categories[article.category].append(article)
+ if hasattr(article, 'tags'):
+ for tag in article.tags:
+ self.tags[tag].append(article)
+ # ignore blank authors as well as undefined
+ for author in getattr(article, 'authors', []):
+ if author.name != '':
+ self.authors[author].append(article)
+ # sort the articles by date
+ self.articles.sort(key=attrgetter('date'), reverse=True)
+ self.dates = list(self.articles)
+ self.dates.sort(key=attrgetter('date'),
+ reverse=self.context['NEWEST_FIRST_ARCHIVES'])
+
+ # create tag cloud
+ tag_cloud = defaultdict(int)
+ for article in self.articles:
+ for tag in getattr(article, 'tags', []):
+ tag_cloud[tag] += 1
+
+ tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True)
+ tag_cloud = tag_cloud[:self.settings.get('TAG_CLOUD_MAX_ITEMS')]
+
+ tags = list(map(itemgetter(1), tag_cloud))
+ if tags:
+ max_count = max(tags)
+ steps = self.settings.get('TAG_CLOUD_STEPS')
+
+ # calculate word sizes
+ self.tag_cloud = [
+ (
+ tag,
+ int(math.floor(steps - (steps - 1) * math.log(count)
+ / (math.log(max_count)or 1)))
+ )
+ for tag, count in tag_cloud
+ ]
+ # put words in chaos
+ random.shuffle(self.tag_cloud)
+
+ # and generate the output :)
+
+ # order the categories per name
+ self.categories = list(self.categories.items())
+ self.categories.sort(
+ reverse=self.settings['REVERSE_CATEGORY_ORDER'])
+
+ self.authors = list(self.authors.items())
+ self.authors.sort()
+
+ self._update_context(('articles', 'dates', 'tags', 'categories',
+ 'tag_cloud', 'authors', 'related_posts'))
+ self.save_cache()
+ self.readers.save_cache()
+ signals.article_generator_finalized.send(self)
+
+ def generate_output(self, writer):
+ self.generate_feeds(writer)
+ self.generate_pages(writer)
+ signals.article_writer_finalized.send(self, writer=writer)
+
+
+class PagesGenerator(CachingGenerator):
+ """Generate pages"""
+
+ def __init__(self, *args, **kwargs):
+ self.pages = []
+ self.hidden_pages = []
+ self.hidden_translations = []
+ super(PagesGenerator, self).__init__(*args, **kwargs)
+ signals.page_generator_init.send(self)
+
+ def generate_context(self):
+ all_pages = []
+ hidden_pages = []
+ for f in self.get_files(
+ self.settings['PAGE_PATHS'],
+ exclude=self.settings['PAGE_EXCLUDES']):
+ page = self.get_cached_data(f, None)
+ if page is None:
+ try:
+ page = self.readers.read_file(
+ base_path=self.path, path=f, content_class=Page,
+ context=self.context,
+ preread_signal=signals.page_generator_preread,
+ preread_sender=self,
+ context_signal=signals.page_generator_context,
+ context_sender=self)
+ except Exception as e:
+ logger.error('Could not process %s\n%s', f, e,
+ exc_info=self.settings.get('DEBUG', False))
+ self._add_failed_source_path(f)
+ continue
+
+ if not is_valid_content(page, f):
+ self._add_failed_source_path(f)
+ continue
+
+ self.cache_data(f, page)
+
+ self.add_source_path(page)
+
+ if page.status == "published":
+ all_pages.append(page)
+ elif page.status == "hidden":
+ hidden_pages.append(page)
+ else:
+ logger.error("Unknown status '%s' for file %s, skipping it.",
+ page.status, f)
+
+ 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))
+
+ self._update_context(('pages', ))
+ self.context['PAGES'] = self.pages
+
+ self.save_cache()
+ self.readers.save_cache()
+ signals.page_generator_finalized.send(self)
+
+ def generate_output(self, writer):
+ for page in chain(self.translations, self.pages,
+ self.hidden_translations, self.hidden_pages):
+ 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'))
+
+
+class StaticGenerator(Generator):
+ """copy static paths (what you want to copy, like images, medias etc.
+ to output"""
+
+ def __init__(self, *args, **kwargs):
+ super(StaticGenerator, self).__init__(*args, **kwargs)
+ signals.static_generator_init.send(self)
+
+ def _copy_paths(self, paths, source, destination, output_path,
+ final_path=None):
+ """Copy all the paths from source to destination"""
+ for path in paths:
+ if final_path:
+ copy(os.path.join(source, path),
+ os.path.join(output_path, destination, final_path))
+ else:
+ copy(os.path.join(source, path),
+ os.path.join(output_path, destination, path))
+
+ def generate_context(self):
+ self.staticfiles = []
+ for f in self.get_files(self.settings['STATIC_PATHS'],
+ exclude=self.settings['STATIC_EXCLUDES'],
+ extensions=False):
+
+ # skip content source files unless the user explicitly wants them
+ if self.settings['STATIC_EXCLUDE_SOURCES']:
+ if self._is_potential_source_path(f):
+ continue
+
+ static = self.readers.read_file(
+ base_path=self.path, path=f, content_class=Static,
+ fmt='static', context=self.context,
+ preread_signal=signals.static_generator_preread,
+ preread_sender=self,
+ context_signal=signals.static_generator_context,
+ context_sender=self)
+ self.staticfiles.append(static)
+ self.add_source_path(static)
+ self._update_context(('staticfiles',))
+ signals.static_generator_finalized.send(self)
+
+ def generate_output(self, writer):
+ self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
+ self.settings['THEME_STATIC_DIR'], self.output_path,
+ os.curdir)
+ # copy all Static files
+ for sc in self.context['staticfiles']:
+ source_path = os.path.join(self.path, sc.source_path)
+ save_as = os.path.join(self.output_path, sc.save_as)
+ mkdir_p(os.path.dirname(save_as))
+ shutil.copy2(source_path, save_as)
+ logger.info('Copying %s to %s', sc.source_path, sc.save_as)
+
+
+class SourceFileGenerator(Generator):
+
+ def generate_context(self):
+ self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION']
+
+ def _create_source(self, obj):
+ output_path, _ = os.path.splitext(obj.save_as)
+ dest = os.path.join(self.output_path,
+ output_path + self.output_extension)
+ copy(obj.source_path, dest)
+
+ def generate_output(self, writer=None):
+ logger.info('Generating source files...')
+ for obj in chain(self.context['articles'], self.context['pages']):
+ self._create_source(obj)
+ for obj_trans in obj.translations:
+ self._create_source(obj_trans)
diff --git a/pelican/log.py b/pelican/log.py
new file mode 100644
index 00000000..089bc0ac
--- /dev/null
+++ b/pelican/log.py
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+__all__ = [
+ 'init'
+]
+
+import os
+import sys
+import logging
+import locale
+
+from collections import defaultdict, Mapping
+
+import six
+
+class BaseFormatter(logging.Formatter):
+ def __init__(self, fmt=None, datefmt=None):
+ FORMAT = '%(customlevelname)s %(message)s'
+ super(BaseFormatter, self).__init__(fmt=FORMAT, datefmt=datefmt)
+
+ def format(self, record):
+ record.__dict__['customlevelname'] = self._get_levelname(record.levelname)
+ # format multiline messages 'nicely' to make it clear they are together
+ record.msg = record.msg.replace('\n', '\n | ')
+ return super(BaseFormatter, self).format(record)
+
+ def formatException(self, ei):
+ ''' prefix traceback info for better representation '''
+ # .formatException returns a bytestring in py2 and unicode in py3
+ # since .format will handle unicode conversion,
+ # str() calls are used to normalize formatting string
+ s = super(BaseFormatter, self).formatException(ei)
+ # fancy format traceback
+ s = str('\n').join(str(' | ') + line for line in s.splitlines())
+ # seperate the traceback from the preceding lines
+ s = str(' |___\n{}').format(s)
+ return s
+
+ def _get_levelname(self, name):
+ ''' NOOP: overridden by subclasses '''
+ return name
+
+
+class ANSIFormatter(BaseFormatter):
+ ANSI_CODES = {
+ 'red': '\033[1;31m',
+ 'yellow': '\033[1;33m',
+ 'cyan': '\033[1;36m',
+ 'white': '\033[1;37m',
+ 'bgred': '\033[1;41m',
+ 'bggrey': '\033[1;100m',
+ 'reset': '\033[0;m'}
+
+ LEVEL_COLORS = {
+ 'INFO': 'cyan',
+ 'WARNING': 'yellow',
+ 'ERROR': 'red',
+ 'CRITICAL': 'bgred',
+ 'DEBUG': 'bggrey'}
+
+ def _get_levelname(self, name):
+ color = self.ANSI_CODES[self.LEVEL_COLORS.get(name, 'white')]
+ if name == 'INFO':
+ fmt = '{0}->{2}'
+ else:
+ fmt = '{0}{1}{2}:'
+ return fmt.format(color, name, self.ANSI_CODES['reset'])
+
+
+class TextFormatter(BaseFormatter):
+ """
+ Convert a `logging.LogRecord' object into text.
+ """
+
+ def _get_levelname(self, name):
+ if name == 'INFO':
+ return '->'
+ else:
+ return name + ':'
+
+
+class LimitFilter(logging.Filter):
+ """
+ Remove duplicates records, and limit the number of records in the same
+ group.
+
+ Groups are specified by the message to use when the number of records in
+ the same group hit the limit.
+ E.g.: log.warning(('43 is not the answer', 'More erroneous answers'))
+ """
+
+ _ignore = set()
+ _threshold = 5
+ _group_count = defaultdict(int)
+
+ def filter(self, record):
+ # don't limit log messages for anything above "warning"
+ if record.levelno > logging.WARN:
+ return True
+
+ # extract group
+ group = record.__dict__.get('limit_msg', None)
+ group_args = record.__dict__.get('limit_args', ())
+
+ # ignore record if it was already raised
+ # use .getMessage() and not .msg for string formatting
+ ignore_key = (record.levelno, record.getMessage())
+ if ignore_key in self._ignore:
+ return False
+ else:
+ self._ignore.add(ignore_key)
+
+ # check if we went over threshold
+ if group:
+ key = (record.levelno, group)
+ self._group_count[key] += 1
+ if self._group_count[key] == self._threshold:
+ record.msg = group
+ record.args = group_args
+ elif self._group_count[key] > self._threshold:
+ return False
+ return True
+
+
+class SafeLogger(logging.Logger):
+ """
+ Base Logger which properly encodes Exceptions in Py2
+ """
+ _exc_encoding = locale.getpreferredencoding()
+
+ def _log(self, level, msg, args, exc_info=None, extra=None):
+ # if the only argument is a Mapping, Logger uses that for formatting
+ # format values for that case
+ if args and len(args)==1 and isinstance(args[0], Mapping):
+ args = ({k: self._decode_arg(v) for k, v in args[0].items()},)
+ # otherwise, format each arg
+ else:
+ args = tuple(self._decode_arg(arg) for arg in args)
+ super(SafeLogger, self)._log(level, msg, args,
+ exc_info=exc_info, extra=extra)
+
+ def _decode_arg(self, arg):
+ '''
+ properly decode an arg for Py2 if it's Exception
+
+
+ localized systems have errors in native language if locale is set
+ so convert the message to unicode with the correct encoding
+ '''
+ if isinstance(arg, Exception):
+ text = str(arg)
+ if six.PY2:
+ text = text.decode(self._exc_encoding)
+ return text
+ else:
+ return arg
+
+
+class LimitLogger(SafeLogger):
+ """
+ A logger which adds LimitFilter automatically
+ """
+
+ limit_filter = LimitFilter()
+
+ def __init__(self, *args, **kwargs):
+ super(LimitLogger, self).__init__(*args, **kwargs)
+ self.addFilter(LimitLogger.limit_filter)
+
+logging.setLoggerClass(LimitLogger)
+
+
+def init(level=None, handler=logging.StreamHandler()):
+
+ logger = logging.getLogger()
+
+ if (os.isatty(sys.stdout.fileno())
+ and not sys.platform.startswith('win')):
+ fmt = ANSIFormatter()
+ else:
+ fmt = TextFormatter()
+ handler.setFormatter(fmt)
+ logger.addHandler(handler)
+
+ if level:
+ logger.setLevel(level)
+
+
+if __name__ == '__main__':
+ init(level=logging.DEBUG)
+
+ root_logger = logging.getLogger()
+ root_logger.debug('debug')
+ root_logger.info('info')
+ root_logger.warning('warning')
+ root_logger.error('error')
+ root_logger.critical('critical')
diff --git a/pelican/paginator.py b/pelican/paginator.py
new file mode 100644
index 00000000..3f5cce47
--- /dev/null
+++ b/pelican/paginator.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+
+# From django.core.paginator
+from collections import namedtuple
+import functools
+import logging
+import os
+
+from math import ceil
+
+logger = logging.getLogger(__name__)
+
+
+PaginationRule = namedtuple(
+ 'PaginationRule',
+ 'min_page URL SAVE_AS',
+)
+
+
+class Paginator(object):
+ def __init__(self, name, object_list, settings):
+ self.name = name
+ 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')
+ else:
+ self.per_page = len(object_list)
+ self.orphans = 0
+
+ self._num_pages = self._count = None
+
+ def page(self, number):
+ "Returns a Page object for the given 1-based page number."
+ bottom = (number - 1) * self.per_page
+ top = bottom + self.per_page
+ if top + self.orphans >= self.count:
+ top = self.count
+ return Page(self.name, self.object_list[bottom:top], number, self,
+ self.settings)
+
+ def _get_count(self):
+ "Returns the total number of objects, across all pages."
+ if self._count is None:
+ self._count = len(self.object_list)
+ return self._count
+ count = property(_get_count)
+
+ def _get_num_pages(self):
+ "Returns the total number of pages."
+ if self._num_pages is None:
+ hits = max(1, self.count - self.orphans)
+ self._num_pages = int(ceil(hits / (float(self.per_page) or 1)))
+ return self._num_pages
+ num_pages = property(_get_num_pages)
+
+ def _get_page_range(self):
+ """
+ Returns a 1-based range of pages for iterating through within
+ a template for loop.
+ """
+ return list(range(1, self.num_pages + 1))
+ page_range = property(_get_page_range)
+
+
+class Page(object):
+ def __init__(self, name, object_list, number, paginator, settings):
+ self.name, self.extension = os.path.splitext(name)
+ self.object_list = object_list
+ self.number = number
+ self.paginator = paginator
+ self.settings = settings
+
+ def __repr__(self):
+ return '' % (self.number, self.paginator.num_pages)
+
+ def has_next(self):
+ return self.number < self.paginator.num_pages
+
+ def has_previous(self):
+ return self.number > 1
+
+ def has_other_pages(self):
+ return self.has_previous() or self.has_next()
+
+ def next_page_number(self):
+ return self.number + 1
+
+ def previous_page_number(self):
+ return self.number - 1
+
+ def start_index(self):
+ """
+ Returns the 1-based index of the first object on this page,
+ relative to total objects in the paginator.
+ """
+ # Special case, return zero if no items.
+ if self.paginator.count == 0:
+ return 0
+ return (self.paginator.per_page * (self.number - 1)) + 1
+
+ def end_index(self):
+ """
+ Returns the 1-based index of the last object on this page,
+ relative to total objects found (hits).
+ """
+ # Special case for the last page because there can be orphans.
+ if self.number == self.paginator.num_pages:
+ return self.paginator.count
+ return self.number * self.paginator.per_page
+
+ def _from_settings(self, key):
+ """Returns URL information as defined in settings. Similar to
+ URLWrapper._from_settings, but specialized to deal with pagination
+ logic."""
+
+ rule = None
+
+ # find the last matching pagination rule
+ for p in self.settings['PAGINATION_PATTERNS']:
+ if p.min_page <= self.number:
+ rule = p
+
+ if not rule:
+ return ''
+
+ prop_value = getattr(rule, key)
+
+ if not isinstance(prop_value, six.string_types):
+ logger.warning('%s is set to %s', key, prop_value)
+ return prop_value
+
+ # URL or SAVE_AS is a string, format it with a controlled context
+ context = {
+ 'name': self.name,
+ 'object_list': self.object_list,
+ 'number': self.number,
+ 'paginator': self.paginator,
+ 'settings': self.settings,
+ 'base_name': os.path.dirname(self.name),
+ 'number_sep': '/',
+ 'extension': self.extension,
+ }
+
+ 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:]
+ return ret
+
+ url = property(functools.partial(_from_settings, key='URL'))
+ save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
diff --git a/pelican/readers.py b/pelican/readers.py
new file mode 100644
index 00000000..85147e3e
--- /dev/null
+++ b/pelican/readers.py
@@ -0,0 +1,591 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+import logging
+import os
+import re
+
+import docutils
+import docutils.core
+import docutils.io
+from docutils.writers.html4css1 import HTMLTranslator
+import six
+
+# import the directives to have pygments support
+from pelican import rstdirectives # NOQA
+try:
+ from markdown import Markdown
+except ImportError:
+ Markdown = False # NOQA
+try:
+ from html import escape
+except ImportError:
+ from cgi import escape
+from six.moves.html_parser import HTMLParser
+
+from pelican import signals
+from pelican.contents import Page, Category, Tag, Author
+from pelican.utils import get_date, pelican_open, FileStampDataCacher, SafeDatetime
+
+
+METADATA_PROCESSORS = {
+ 'tags': lambda x, y: [Tag(tag, y) for tag in x.split(',')],
+ 'date': lambda x, y: get_date(x),
+ 'modified': lambda x, y: get_date(x),
+ 'status': lambda x, y: x.strip(),
+ 'category': Category,
+ 'author': Author,
+ 'authors': lambda x, y: [Author(author.strip(), y) for author in x.split(',')],
+}
+
+logger = logging.getLogger(__name__)
+
+class BaseReader(object):
+ """Base class to read files.
+
+ This class is used to process static files, and it can be inherited for
+ other types of file. A Reader class must have the following attributes:
+
+ - enabled: (boolean) tell if the Reader class is enabled. It
+ generally depends on the import of some dependency.
+ - file_extensions: a list of file extensions that the Reader will process.
+ - extensions: a list of extensions to use in the reader (typical use is
+ Markdown).
+
+ """
+ enabled = True
+ file_extensions = ['static']
+ extensions = None
+
+ def __init__(self, settings):
+ self.settings = settings
+
+ def process_metadata(self, name, value):
+ if name in METADATA_PROCESSORS:
+ return METADATA_PROCESSORS[name](value, self.settings)
+ return value
+
+ def read(self, source_path):
+ "No-op parser"
+ content = None
+ metadata = {}
+ return content, metadata
+
+
+class _FieldBodyTranslator(HTMLTranslator):
+
+ def __init__(self, document):
+ HTMLTranslator.__init__(self, document)
+ self.compact_p = None
+
+ def astext(self):
+ return ''.join(self.body)
+
+ def visit_field_body(self, node):
+ pass
+
+ def depart_field_body(self, node):
+ pass
+
+
+def render_node_to_html(document, node):
+ visitor = _FieldBodyTranslator(document)
+ node.walkabout(visitor)
+ return visitor.astext()
+
+
+class PelicanHTMLTranslator(HTMLTranslator):
+
+ def visit_abbreviation(self, node):
+ attrs = {}
+ if node.hasattr('explanation'):
+ attrs['title'] = node['explanation']
+ self.body.append(self.starttag(node, 'abbr', '', **attrs))
+
+ def depart_abbreviation(self, node):
+ self.body.append('')
+
+ def visit_image(self, node):
+ # set an empty alt if alt is not specified
+ # avoids that alt is taken from src
+ node['alt'] = node.get('alt', '')
+ return HTMLTranslator.visit_image(self, node)
+
+
+class RstReader(BaseReader):
+ """Reader for reStructuredText files"""
+
+ enabled = bool(docutils)
+ file_extensions = ['rst']
+
+ class FileInput(docutils.io.FileInput):
+ """Patch docutils.io.FileInput to remove "U" mode in py3.
+
+ Universal newlines is enabled by default and "U" mode is deprecated
+ in py3.
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ if six.PY3:
+ kwargs['mode'] = kwargs.get('mode', 'r').replace('U', '')
+ docutils.io.FileInput.__init__(self, *args, **kwargs)
+
+ def __init__(self, *args, **kwargs):
+ super(RstReader, self).__init__(*args, **kwargs)
+
+ def _parse_metadata(self, document):
+ """Return the dict containing document metadata"""
+ output = {}
+ for docinfo in document.traverse(docutils.nodes.docinfo):
+ for element in docinfo.children:
+ if element.tagname == 'field': # custom fields (e.g. summary)
+ name_elem, body_elem = element.children
+ name = name_elem.astext()
+ if name == 'summary':
+ value = render_node_to_html(document, body_elem)
+ else:
+ value = body_elem.astext()
+ elif element.tagname == 'authors': # author list
+ name = element.tagname
+ value = [element.astext() for element in element.children]
+ value = ','.join(value) # METADATA_PROCESSORS expects a string
+ else: # standard fields (e.g. address)
+ name = element.tagname
+ value = element.astext()
+ name = name.lower()
+
+ output[name] = self.process_metadata(name, value)
+ return output
+
+ def _get_publisher(self, source_path):
+ extra_params = {'initial_header_level': '2',
+ 'syntax_highlight': 'short',
+ 'input_encoding': 'utf-8',
+ 'exit_status_level': 2,
+ 'embed_stylesheet': False}
+ user_params = self.settings.get('DOCUTILS_SETTINGS')
+ if user_params:
+ extra_params.update(user_params)
+
+ pub = docutils.core.Publisher(
+ source_class=self.FileInput,
+ destination_class=docutils.io.StringOutput)
+ pub.set_components('standalone', 'restructuredtext', 'html')
+ pub.writer.translator_class = PelicanHTMLTranslator
+ pub.process_programmatic_settings(None, extra_params, None)
+ pub.set_source(source_path=source_path)
+ pub.publish(enable_exit_status=True)
+ return pub
+
+ def read(self, source_path):
+ """Parses restructured text"""
+ pub = self._get_publisher(source_path)
+ parts = pub.writer.parts
+ content = parts.get('body')
+
+ metadata = self._parse_metadata(pub.document)
+ metadata.setdefault('title', parts.get('title'))
+
+ return content, metadata
+
+
+class MarkdownReader(BaseReader):
+ """Reader for Markdown files"""
+
+ enabled = bool(Markdown)
+ file_extensions = ['md', 'markdown', 'mkd', 'mdown']
+
+ def __init__(self, *args, **kwargs):
+ super(MarkdownReader, self).__init__(*args, **kwargs)
+ self.extensions = list(self.settings['MD_EXTENSIONS'])
+ if 'meta' not in self.extensions:
+ self.extensions.append('meta')
+ self._source_path = None
+
+ def _parse_metadata(self, meta):
+ """Return the dict containing document metadata"""
+ output = {}
+ for name, value in meta.items():
+ name = name.lower()
+ if name == "summary":
+ # handle summary metadata as markdown
+ # summary metadata is special case and join all list values
+ summary_values = "\n".join(value)
+ # reset the markdown instance to clear any state
+ self._md.reset()
+ summary = self._md.convert(summary_values)
+ output[name] = self.process_metadata(name, summary)
+ elif name in METADATA_PROCESSORS:
+ if len(value) > 1:
+ logger.warning('Duplicate definition of `%s` '
+ 'for %s. Using first one.', name, self._source_path)
+ output[name] = self.process_metadata(name, value[0])
+ elif len(value) > 1:
+ # handle list metadata as list of string
+ output[name] = self.process_metadata(name, value)
+ else:
+ # otherwise, handle metadata as single string
+ output[name] = self.process_metadata(name, value[0])
+ return output
+
+ def read(self, source_path):
+ """Parse content and metadata of markdown files"""
+
+ self._source_path = source_path
+ self._md = Markdown(extensions=self.extensions)
+ with pelican_open(source_path) as text:
+ content = self._md.convert(text)
+
+ metadata = self._parse_metadata(self._md.Meta)
+ return content, metadata
+
+
+class HTMLReader(BaseReader):
+ """Parses HTML files as input, looking for meta, title, and body tags"""
+
+ file_extensions = ['htm', 'html']
+ enabled = True
+
+ class _HTMLParser(HTMLParser):
+ def __init__(self, settings, filename):
+ try:
+ # Python 3.4+
+ HTMLParser.__init__(self, convert_charrefs=False)
+ except TypeError:
+ HTMLParser.__init__(self)
+ self.body = ''
+ self.metadata = {}
+ self.settings = settings
+
+ self._data_buffer = ''
+
+ self._filename = filename
+
+ self._in_top_level = True
+ self._in_head = False
+ self._in_title = False
+ self._in_body = False
+ self._in_tags = False
+
+ def handle_starttag(self, tag, attrs):
+ if tag == 'head' and self._in_top_level:
+ self._in_top_level = False
+ self._in_head = True
+ elif tag == 'title' and self._in_head:
+ self._in_title = True
+ self._data_buffer = ''
+ elif tag == 'body' and self._in_top_level:
+ self._in_top_level = False
+ self._in_body = True
+ self._data_buffer = ''
+ elif tag == 'meta' and self._in_head:
+ self._handle_meta_tag(attrs)
+
+ elif self._in_body:
+ self._data_buffer += self.build_tag(tag, attrs, False)
+
+ def handle_endtag(self, tag):
+ if tag == 'head':
+ if self._in_head:
+ self._in_head = False
+ self._in_top_level = True
+ elif tag == 'title':
+ self._in_title = False
+ self.metadata['title'] = self._data_buffer
+ elif tag == 'body':
+ self.body = self._data_buffer
+ self._in_body = False
+ self._in_top_level = True
+ elif self._in_body:
+ self._data_buffer += '{}>'.format(escape(tag))
+
+ def handle_startendtag(self, tag, attrs):
+ if tag == 'meta' and self._in_head:
+ self._handle_meta_tag(attrs)
+ if self._in_body:
+ self._data_buffer += self.build_tag(tag, attrs, True)
+
+ def handle_comment(self, data):
+ self._data_buffer += ''.format(data)
+
+ def handle_data(self, data):
+ self._data_buffer += data
+
+ def handle_entityref(self, data):
+ self._data_buffer += '&{};'.format(data)
+
+ def handle_charref(self, data):
+ self._data_buffer += '{};'.format(data)
+
+ def build_tag(self, tag, attrs, close_tag):
+ result = '<{}'.format(escape(tag))
+ for k, v in attrs:
+ result += ' ' + escape(k)
+ if v is not None:
+ result += '="{}"'.format(escape(v))
+ if close_tag:
+ return result + ' />'
+ return result + '>'
+
+ def _handle_meta_tag(self, attrs):
+ name = self._attr_value(attrs, 'name')
+ if name is None:
+ attr_serialized = ', '.join(['{}="{}"'.format(k, v) for k, v in attrs])
+ logger.warning("Meta tag in file %s does not have a 'name' "
+ "attribute, skipping. Attributes: %s",
+ self._filename, attr_serialized)
+ return
+ name = name.lower()
+ contents = self._attr_value(attrs, 'content', '')
+ if not contents:
+ contents = self._attr_value(attrs, 'contents', '')
+ if contents:
+ logger.warning(
+ "Meta tag attribute 'contents' used in file %s, should"
+ " be changed to 'content'",
+ self._filename,
+ extra={'limit_msg': ("Other files have meta tag "
+ "attribute 'contents' that should "
+ "be changed to 'content'")})
+
+ if name == 'keywords':
+ name = 'tags'
+ self.metadata[name] = contents
+
+ @classmethod
+ def _attr_value(cls, attrs, name, default=None):
+ return next((x[1] for x in attrs if x[0] == name), default)
+
+ def read(self, filename):
+ """Parse content and metadata of HTML files"""
+ with pelican_open(filename) as content:
+ parser = self._HTMLParser(self.settings, filename)
+ parser.feed(content)
+ parser.close()
+
+ metadata = {}
+ for k in parser.metadata:
+ metadata[k] = self.process_metadata(k, parser.metadata[k])
+ return parser.body, metadata
+
+
+class Readers(FileStampDataCacher):
+ """Interface for all readers.
+
+ This class contains a mapping of file extensions / Reader classes, to know
+ which Reader class must be used to read a file (based on its extension).
+ This is customizable both with the 'READERS' setting, and with the
+ 'readers_init' signall for plugins.
+
+ """
+
+ def __init__(self, settings=None, cache_name=''):
+ self.settings = settings or {}
+ self.readers = {}
+ self.reader_classes = {}
+
+ for cls in [BaseReader] + BaseReader.__subclasses__():
+ if not cls.enabled:
+ logger.debug('Missing dependencies for %s',
+ ', '.join(cls.file_extensions))
+ continue
+
+ for ext in cls.file_extensions:
+ self.reader_classes[ext] = cls
+
+ if self.settings['READERS']:
+ self.reader_classes.update(self.settings['READERS'])
+
+ signals.readers_init.send(self)
+
+ for fmt, reader_class in self.reader_classes.items():
+ if not reader_class:
+ continue
+
+ self.readers[fmt] = reader_class(self.settings)
+
+ # set up caching
+ cache_this_level = (cache_name != '' and
+ self.settings['CONTENT_CACHING_LAYER'] == 'reader')
+ caching_policy = cache_this_level and self.settings['CACHE_CONTENT']
+ load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE']
+ super(Readers, self).__init__(settings, cache_name,
+ caching_policy, load_policy,
+ )
+
+ @property
+ def extensions(self):
+ return self.readers.keys()
+
+ def read_file(self, base_path, path, content_class=Page, fmt=None,
+ context=None, preread_signal=None, preread_sender=None,
+ context_signal=None, context_sender=None):
+ """Return a content object parsed with the given format."""
+
+ path = os.path.abspath(os.path.join(base_path, path))
+ source_path = os.path.relpath(path, base_path)
+ logger.debug('Read file %s -> %s',
+ source_path, content_class.__name__)
+
+ if not fmt:
+ _, ext = os.path.splitext(os.path.basename(path))
+ fmt = ext[1:]
+
+ if fmt not in self.readers:
+ raise TypeError(
+ 'Pelican does not know how to parse %s', path)
+
+ if preread_signal:
+ logger.debug('Signal %s.send(%s)',
+ preread_signal.name, preread_sender)
+ preread_signal.send(preread_sender)
+
+ reader = self.readers[fmt]
+
+ metadata = default_metadata(
+ settings=self.settings, process=reader.process_metadata)
+ metadata.update(path_metadata(
+ full_path=path, source_path=source_path,
+ settings=self.settings))
+ metadata.update(parse_path_metadata(
+ source_path=source_path, settings=self.settings,
+ process=reader.process_metadata))
+ reader_name = reader.__class__.__name__
+ metadata['reader'] = reader_name.replace('Reader', '').lower()
+
+ content, reader_metadata = self.get_cached_data(path, (None, None))
+ if content is None:
+ content, reader_metadata = reader.read(path)
+ self.cache_data(path, (content, reader_metadata))
+ metadata.update(reader_metadata)
+
+ if content:
+ # find images with empty alt
+ find_empty_alt(content, path)
+
+ # eventually filter the content with typogrify if asked so
+ if self.settings['TYPOGRIFY']:
+ from typogrify.filters import typogrify
+
+ def typogrify_wrapper(text):
+ """Ensures ignore_tags feature is backward compatible"""
+ try:
+ return typogrify(text, self.settings['TYPOGRIFY_IGNORE_TAGS'])
+ except TypeError:
+ return typogrify(text)
+
+ if content:
+ content = typogrify_wrapper(content)
+ metadata['title'] = typogrify_wrapper(metadata['title'])
+
+ if 'summary' in metadata:
+ metadata['summary'] = typogrify_wrapper(metadata['summary'])
+
+ if context_signal:
+ logger.debug('Signal %s.send(%s, )',
+ context_signal.name, context_sender)
+ context_signal.send(context_sender, metadata=metadata)
+
+ return content_class(content=content, metadata=metadata,
+ settings=self.settings, source_path=path,
+ context=context)
+
+
+def find_empty_alt(content, path):
+ """Find images with empty alt
+
+ Create warnings for all images with empty alt (up to a certain number),
+ as they are really likely to be accessibility flaws.
+
+ """
+ imgs = re.compile(r"""
+ (?:
+ # src before alt
+ ]*
+ src=(['"])(.*)\1
+ [^\>]*
+ alt=(['"])\3
+ )|(?:
+ # alt before src
+ ]*
+ alt=(['"])\4
+ [^\>]*
+ src=(['"])(.*)\5
+ )
+ """, re.X)
+ for match in re.findall(imgs, content):
+ logger.warning(
+ 'Empty alt attribute for image %s in %s',
+ os.path.basename(match[1] + match[5]), path,
+ extra={'limit_msg': 'Other images have empty alt attributes'})
+
+
+def default_metadata(settings=None, process=None):
+ metadata = {}
+ if settings:
+ if 'DEFAULT_CATEGORY' in settings:
+ value = settings['DEFAULT_CATEGORY']
+ if process:
+ value = process('category', value)
+ metadata['category'] = value
+ if settings.get('DEFAULT_DATE', None) and settings['DEFAULT_DATE'] != 'fs':
+ metadata['date'] = SafeDatetime(*settings['DEFAULT_DATE'])
+ return metadata
+
+
+def path_metadata(full_path, source_path, settings=None):
+ metadata = {}
+ if settings:
+ if settings.get('DEFAULT_DATE', None) == 'fs':
+ metadata['date'] = SafeDatetime.fromtimestamp(
+ os.stat(full_path).st_ctime)
+ metadata.update(settings.get('EXTRA_PATH_METADATA', {}).get(
+ source_path, {}))
+ return metadata
+
+
+def parse_path_metadata(source_path, settings=None, process=None):
+ """Extract a metadata dictionary from a file's path
+
+ >>> import pprint
+ >>> settings = {
+ ... 'FILENAME_METADATA': '(?P[^.]*).*',
+ ... 'PATH_METADATA':
+ ... '(?P[^/]*)/(?P\d{4}-\d{2}-\d{2})/.*',
+ ... }
+ >>> reader = BaseReader(settings=settings)
+ >>> metadata = parse_path_metadata(
+ ... source_path='my-cat/2013-01-01/my-slug.html',
+ ... settings=settings,
+ ... process=reader.process_metadata)
+ >>> pprint.pprint(metadata) # doctest: +ELLIPSIS
+ {'category': ,
+ 'date': SafeDatetime(2013, 1, 1, 0, 0),
+ 'slug': 'my-slug'}
+ """
+ metadata = {}
+ dirname, basename = os.path.split(source_path)
+ base, ext = os.path.splitext(basename)
+ subdir = os.path.basename(dirname)
+ if settings:
+ checks = []
+ for key, data in [('FILENAME_METADATA', base),
+ ('PATH_METADATA', source_path)]:
+ checks.append((settings.get(key, None), data))
+ if settings.get('USE_FOLDER_AS_CATEGORY', None):
+ checks.insert(0, ('(?P.*)', subdir))
+ for regexp, data in checks:
+ if regexp and data:
+ match = re.match(regexp, data)
+ if match:
+ # .items() for py3k compat.
+ for k, v in match.groupdict().items():
+ if k not in metadata:
+ k = k.lower() # metadata must be lowercase
+ if process:
+ v = process(k, v)
+ metadata[k] = v
+ return metadata
diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py
new file mode 100644
index 00000000..1bf6971c
--- /dev/null
+++ b/pelican/rstdirectives.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+from docutils import nodes, utils
+from docutils.parsers.rst import directives, roles, Directive
+from pygments.formatters import HtmlFormatter
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name, TextLexer
+import re
+import six
+import pelican.settings as pys
+
+
+class Pygments(Directive):
+ """ Source code syntax highlighting.
+ """
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+ option_spec = {
+ 'anchorlinenos': directives.flag,
+ 'classprefix': directives.unchanged,
+ 'hl_lines': directives.unchanged,
+ 'lineanchors': directives.unchanged,
+ 'linenos': directives.unchanged,
+ 'linenospecial': directives.nonnegative_int,
+ 'linenostart': directives.nonnegative_int,
+ 'linenostep': directives.nonnegative_int,
+ 'lineseparator': directives.unchanged,
+ 'linespans': directives.unchanged,
+ 'nobackground': directives.flag,
+ 'nowrap': directives.flag,
+ 'tagsfile': directives.unchanged,
+ 'tagurlformat': directives.unchanged,
+ }
+ has_content = True
+
+ def run(self):
+ self.assert_has_content()
+ try:
+ lexer = get_lexer_by_name(self.arguments[0])
+ except ValueError:
+ # no lexer found - use the text one instead of an exception
+ lexer = TextLexer()
+
+ # Fetch the defaults
+ if pys.PYGMENTS_RST_OPTIONS is not None:
+ for k, v in six.iteritems(pys.PYGMENTS_RST_OPTIONS):
+ # Locally set options overrides the defaults
+ if k not in self.options:
+ self.options[k] = v
+
+ if ('linenos' in self.options and
+ self.options['linenos'] not in ('table', 'inline')):
+ if self.options['linenos'] == 'none':
+ self.options.pop('linenos')
+ else:
+ self.options['linenos'] = 'table'
+
+ for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
+ if flag in self.options:
+ self.options[flag] = True
+
+ # noclasses should already default to False, but just in case...
+ formatter = HtmlFormatter(noclasses=False, **self.options)
+ parsed = highlight('\n'.join(self.content), lexer, formatter)
+ return [nodes.raw('', parsed, format='html')]
+
+directives.register_directive('code-block', Pygments)
+directives.register_directive('sourcecode', Pygments)
+
+
+_abbr_re = re.compile('\((.*)\)$')
+
+
+class abbreviation(nodes.Inline, nodes.TextElement):
+ pass
+
+
+def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ m = _abbr_re.search(text)
+ if m is None:
+ return [abbreviation(text, text)], []
+ abbr = text[:m.start()].strip()
+ expl = m.group(1)
+ return [abbreviation(abbr, abbr, explanation=expl)], []
+
+roles.register_local_role('abbr', abbr_role)
diff --git a/pelican/server.py b/pelican/server.py
new file mode 100644
index 00000000..d4518acd
--- /dev/null
+++ b/pelican/server.py
@@ -0,0 +1,52 @@
+from __future__ import print_function
+import os
+import sys
+import logging
+try:
+ import SimpleHTTPServer as srvmod
+except ImportError:
+ import http.server as srvmod # NOQA
+
+try:
+ import SocketServer as socketserver
+except ImportError:
+ import socketserver # NOQA
+
+PORT = len(sys.argv) == 2 and int(sys.argv[1]) or 8000
+SUFFIXES = ['', '.html', '/index.html']
+
+
+class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler):
+ def do_GET(self):
+ # we are trying to detect the file by having a fallback mechanism
+ found = False
+ for suffix in 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)
+ found = True
+ break
+ logging.info("Tried to find file %s, but it doesn't exist. ", self.path)
+ if not found:
+ logging.warning("Unable to find file %s or variations.", self.path)
+
+Handler = ComplexHTTPRequestHandler
+
+socketserver.TCPServer.allow_reuse_address = True
+try:
+ httpd = socketserver.TCPServer(("", PORT), Handler)
+except OSError as e:
+ logging.error("Could not listen on port %s", PORT)
+ sys.exit(getattr(e, 'exitcode', 1))
+
+
+logging.info("Serving at port %s", PORT)
+try:
+ httpd.serve_forever()
+except KeyboardInterrupt as e:
+ logging.info("Shutting down server")
+ httpd.socket.close()
diff --git a/pelican/settings.py b/pelican/settings.py
new file mode 100644
index 00000000..794733d7
--- /dev/null
+++ b/pelican/settings.py
@@ -0,0 +1,381 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+
+import copy
+import inspect
+import os
+import locale
+import logging
+
+try:
+ # SourceFileLoader is the recommended way in 3.3+
+ from importlib.machinery import SourceFileLoader
+ load_source = lambda name, path: SourceFileLoader(name, path).load_module()
+except ImportError:
+ # but it does not exist in 3.2-, so fall back to imp
+ import imp
+ load_source = imp.load_source
+
+from os.path import isabs
+
+from pelican.log import LimitFilter
+
+
+logger = logging.getLogger(__name__)
+
+
+DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ 'themes', 'notmyidea')
+DEFAULT_CONFIG = {
+ 'PATH': os.curdir,
+ 'ARTICLE_PATHS': [''],
+ 'ARTICLE_EXCLUDES': [],
+ 'PAGE_PATHS': ['pages'],
+ 'PAGE_EXCLUDES': [],
+ 'THEME': DEFAULT_THEME,
+ 'OUTPUT_PATH': 'output',
+ 'READERS': {},
+ 'STATIC_PATHS': ['images'],
+ 'STATIC_EXCLUDES': [],
+ 'STATIC_EXCLUDE_SOURCES': True,
+ 'THEME_STATIC_DIR': 'theme',
+ 'THEME_STATIC_PATHS': ['static', ],
+ 'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'),
+ 'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
+ 'AUTHOR_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
+ 'AUTHOR_FEED_RSS': os.path.join('feeds', '%s.rss.xml'),
+ 'TRANSLATION_FEED_ATOM': os.path.join('feeds', 'all-%s.atom.xml'),
+ 'FEED_MAX_ITEMS': '',
+ 'SITEURL': '',
+ 'SITENAME': 'A Pelican Blog',
+ 'DISPLAY_PAGES_ON_MENU': True,
+ 'DISPLAY_CATEGORIES_ON_MENU': True,
+ 'DOCUTILS_SETTINGS': {},
+ 'OUTPUT_SOURCES': False,
+ 'OUTPUT_SOURCES_EXTENSION': '.text',
+ 'USE_FOLDER_AS_CATEGORY': True,
+ 'DEFAULT_CATEGORY': 'misc',
+ 'WITH_FUTURE_DATES': True,
+ 'CSS_FILE': 'main.css',
+ 'NEWEST_FIRST_ARCHIVES': True,
+ 'REVERSE_CATEGORY_ORDER': False,
+ 'DELETE_OUTPUT_DIRECTORY': False,
+ 'OUTPUT_RETENTION': (),
+ 'ARTICLE_URL': '{slug}.html',
+ 'ARTICLE_SAVE_AS': '{slug}.html',
+ 'ARTICLE_ORDER_BY': 'slug',
+ 'ARTICLE_LANG_URL': '{slug}-{lang}.html',
+ 'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
+ 'DRAFT_URL': 'drafts/{slug}.html',
+ 'DRAFT_SAVE_AS': os.path.join('drafts', '{slug}.html'),
+ 'DRAFT_LANG_URL': 'drafts/{slug}-{lang}.html',
+ 'DRAFT_LANG_SAVE_AS': os.path.join('drafts', '{slug}-{lang}.html'),
+ 'PAGE_URL': 'pages/{slug}.html',
+ 'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'),
+ 'PAGE_ORDER_BY': 'basename',
+ 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
+ 'PAGE_LANG_SAVE_AS': os.path.join('pages', '{slug}-{lang}.html'),
+ 'STATIC_URL': '{path}',
+ 'STATIC_SAVE_AS': '{path}',
+ 'PDF_GENERATOR': False,
+ 'PDF_STYLE_PATH': '',
+ 'PDF_STYLE': 'twelvepoint',
+ 'CATEGORY_URL': 'category/{slug}.html',
+ 'CATEGORY_SAVE_AS': os.path.join('category', '{slug}.html'),
+ 'TAG_URL': 'tag/{slug}.html',
+ 'TAG_SAVE_AS': os.path.join('tag', '{slug}.html'),
+ 'AUTHOR_URL': 'author/{slug}.html',
+ 'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'),
+ 'PAGINATION_PATTERNS': [
+ (0, '{name}{number}{extension}', '{name}{number}{extension}'),
+ ],
+ 'YEAR_ARCHIVE_SAVE_AS': '',
+ 'MONTH_ARCHIVE_SAVE_AS': '',
+ 'DAY_ARCHIVE_SAVE_AS': '',
+ 'RELATIVE_URLS': False,
+ 'DEFAULT_LANG': 'en',
+ 'TAG_CLOUD_STEPS': 4,
+ 'TAG_CLOUD_MAX_ITEMS': 100,
+ 'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'authors', 'archives'),
+ 'EXTRA_TEMPLATES_PATHS': [],
+ 'PAGINATED_DIRECT_TEMPLATES': ('index', ),
+ 'PELICAN_CLASS': 'pelican.Pelican',
+ 'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
+ 'DATE_FORMATS': {},
+ 'MD_EXTENSIONS': ['codehilite(css_class=highlight)', 'extra'],
+ 'JINJA_EXTENSIONS': [],
+ 'JINJA_FILTERS': {},
+ 'LOG_FILTER': [],
+ 'LOCALE': [''], # defaults to user locale
+ 'DEFAULT_PAGINATION': False,
+ 'DEFAULT_ORPHANS': 0,
+ 'DEFAULT_METADATA': (),
+ 'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2}).*',
+ 'PATH_METADATA': '',
+ 'EXTRA_PATH_METADATA': {},
+ 'DEFAULT_STATUS': 'published',
+ 'ARTICLE_PERMALINK_STRUCTURE': '',
+ 'TYPOGRIFY': False,
+ 'TYPOGRIFY_IGNORE_TAGS': [],
+ 'SUMMARY_MAX_LENGTH': 50,
+ 'PLUGIN_PATHS': [],
+ 'PLUGINS': [],
+ 'PYGMENTS_RST_OPTIONS': {},
+ 'TEMPLATE_PAGES': {},
+ 'IGNORE_FILES': ['.#*'],
+ 'SLUG_SUBSTITUTIONS': (),
+ 'INTRASITE_LINK_REGEX': '[{|](?P.*?)[|}]',
+ 'SLUGIFY_SOURCE': 'title',
+ 'CACHE_CONTENT': True,
+ 'CONTENT_CACHING_LAYER': 'reader',
+ 'CACHE_PATH': 'cache',
+ 'GZIP_CACHE': True,
+ 'CHECK_MODIFIED_METHOD': 'mtime',
+ 'LOAD_CONTENT_CACHE': True,
+ 'AUTORELOAD_IGNORE_CACHE': False,
+ 'WRITE_SELECTED': [],
+ }
+
+PYGMENTS_RST_OPTIONS = None
+
+
+def read_settings(path=None, override=None):
+ if path:
+ local_settings = get_settings_from_file(path)
+ # Make the paths relative to the settings file
+ 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 p not in ('THEME') or os.path.exists(absp):
+ local_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 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:
+ local_settings['PLUGIN_PATHS'] = [os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(path), pluginpath)))
+ if not isabs(pluginpath) else pluginpath for pluginpath in local_settings['PLUGIN_PATHS']]
+ else:
+ local_settings = copy.deepcopy(DEFAULT_CONFIG)
+
+ 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
+
+
+def get_settings_from_module(module=None, default_settings=DEFAULT_CONFIG):
+ """Loads settings from a module, returns a dictionary."""
+
+ context = copy.deepcopy(default_settings)
+ 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):
+ """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)
+
+
+def configure_settings(settings):
+ """Provide optimizations, error checking, and warnings for the given
+ settings.
+ Also, specify the log messages to be ignored.
+ """
+ if not 'PATH' in settings or not os.path.isdir(settings['PATH']):
+ raise Exception('You need to specify a path containing the content'
+ ' (see pelican --help for more information)')
+
+ # specify the log messages to be ignored
+ LimitFilter._ignore.update(set(settings.get('LOG_FILTER',
+ DEFAULT_CONFIG['LOG_FILTER'])))
+
+ # lookup the theme in "pelican/themes" if the given one doesn't exist
+ if not os.path.isdir(settings['THEME']):
+ theme_path = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ 'themes',
+ settings['THEME'])
+ if os.path.exists(theme_path):
+ settings['THEME'] = theme_path
+ else:
+ raise Exception("Could not find the theme %s"
+ % settings['THEME'])
+
+ # make paths selected for writing absolute if necessary
+ settings['WRITE_SELECTED'] = [
+ os.path.abspath(path) for path in
+ settings.get('WRITE_SELECTED', DEFAULT_CONFIG['WRITE_SELECTED'])
+ ]
+
+ # standardize strings to lowercase strings
+ for key in [
+ 'DEFAULT_LANG',
+ ]:
+ if key in settings:
+ settings[key] = settings[key].lower()
+
+ # standardize strings to lists
+ for key in [
+ 'LOCALE',
+ ]:
+ if key in settings and isinstance(settings[key], six.string_types):
+ settings[key] = [settings[key]]
+
+ # check settings that must be a particular type
+ for key, types in [
+ ('OUTPUT_SOURCES_EXTENSION', six.string_types),
+ ('FILENAME_METADATA', six.string_types),
+ ]:
+ if key in settings and not isinstance(settings[key], types):
+ value = settings.pop(key)
+ logger.warn('Detected misconfigured %s (%s), '
+ 'falling back to the default (%s)',
+ key, value, DEFAULT_CONFIG[key])
+
+ # try to set the different locales, fallback on the default.
+ locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE'])
+
+ for locale_ in locales:
+ try:
+ locale.setlocale(locale.LC_ALL, str(locale_))
+ break # break if it is successful
+ except locale.Error:
+ pass
+ else:
+ logger.warning("LOCALE option doesn't contain a correct value")
+
+ if ('SITEURL' in settings):
+ # If SITEURL has a trailing slash, remove it and provide a warning
+ siteurl = settings['SITEURL']
+ if (siteurl.endswith('/')):
+ settings['SITEURL'] = siteurl[:-1]
+ logger.warning("Removed extraneous trailing slash from SITEURL.")
+ # If SITEURL is defined but FEED_DOMAIN isn't,
+ # set FEED_DOMAIN to SITEURL
+ if not 'FEED_DOMAIN' in settings:
+ settings['FEED_DOMAIN'] = settings['SITEURL']
+
+ # check content caching layer and warn of incompatibilities
+ if (settings.get('CACHE_CONTENT', False) and
+ settings.get('CONTENT_CACHING_LAYER', '') == 'generator' and
+ settings.get('WITH_FUTURE_DATES', DEFAULT_CONFIG['WITH_FUTURE_DATES'])):
+ logger.warning('WITH_FUTURE_DATES conflicts with '
+ "CONTENT_CACHING_LAYER set to 'generator', "
+ "use 'reader' layer instead")
+
+ # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined
+ feed_keys = [
+ 'FEED_ATOM', 'FEED_RSS',
+ 'FEED_ALL_ATOM', 'FEED_ALL_RSS',
+ 'CATEGORY_FEED_ATOM', 'CATEGORY_FEED_RSS',
+ 'AUTHOR_FEED_ATOM', 'AUTHOR_FEED_RSS',
+ 'TAG_FEED_ATOM', 'TAG_FEED_RSS',
+ 'TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS',
+ ]
+
+ if any(settings.get(k) for k in feed_keys):
+ if not settings.get('SITEURL'):
+ logger.warning('Feeds generated without SITEURL set properly may'
+ ' not be valid')
+
+ if not 'TIMEZONE' in settings:
+ logger.warning(
+ 'No timezone information specified in the settings. Assuming'
+ ' your timezone is UTC for feed generation. Check '
+ 'http://docs.getpelican.com/en/latest/settings.html#timezone '
+ 'for more information')
+
+ # fix up pagination rules
+ from pelican.paginator import PaginationRule
+ pagination_rules = [
+ PaginationRule(*r) for r in settings.get(
+ 'PAGINATION_PATTERNS',
+ DEFAULT_CONFIG['PAGINATION_PATTERNS'],
+ )
+ ]
+ settings['PAGINATION_PATTERNS'] = sorted(
+ pagination_rules,
+ 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',
+ 'FILES_TO_COPY',
+ 'IGNORE_FILES',
+ 'JINJA_EXTENSIONS',
+ 'PAGINATED_DIRECT_TEMPLATES',
+ 'PLUGINS',
+ 'STATIC_EXCLUDES',
+ 'STATIC_PATHS',
+ 'THEME_STATIC_PATHS',
+ 'ARTICLE_PATHS',
+ 'PAGE_PATHS',
+ )
+ for PATH_KEY in filter(lambda k: k in settings, path_keys):
+ if isinstance(settings[PATH_KEY], six.string_types):
+ logger.warning("Detected misconfiguration with %s setting "
+ "(must be a list), falling back to the default",
+ PATH_KEY)
+ settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY]
+
+ # Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES
+ mutually_exclusive = ('ARTICLE', 'PAGE')
+ for type_1, type_2 in [mutually_exclusive, mutually_exclusive[::-1]]:
+ try:
+ includes = settings[type_1 + '_PATHS']
+ excludes = settings[type_2 + '_EXCLUDES']
+ for path in includes:
+ if path not in excludes:
+ excludes.append(path)
+ 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
diff --git a/pelican/signals.py b/pelican/signals.py
new file mode 100644
index 00000000..f858c249
--- /dev/null
+++ b/pelican/signals.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+from blinker import signal
+
+# Run-level signals:
+
+initialized = signal('pelican_initialized')
+get_generators = signal('get_generators')
+get_writer = signal('get_writer')
+finalized = signal('pelican_finalized')
+
+# Reader-level signals
+
+readers_init = signal('readers_init')
+
+# Generator-level signals
+
+generator_init = signal('generator_init')
+
+article_generator_init = signal('article_generator_init')
+article_generator_pretaxonomy = signal('article_generator_pretaxonomy')
+article_generator_finalized = signal('article_generator_finalized')
+article_generator_write_article = signal('article_generator_write_article')
+article_writer_finalized = signal('article_writer_finalized')
+
+page_generator_init = signal('page_generator_init')
+page_generator_finalized = signal('page_generator_finalized')
+
+static_generator_init = signal('static_generator_init')
+static_generator_finalized = signal('static_generator_finalized')
+
+# Page-level signals
+
+article_generator_preread = signal('article_generator_preread')
+article_generator_context = signal('article_generator_context')
+
+page_generator_preread = signal('page_generator_preread')
+page_generator_context = signal('page_generator_context')
+
+static_generator_preread = signal('static_generator_preread')
+static_generator_context = signal('static_generator_context')
+
+content_object_init = signal('content_object_init')
+
+# Writers signals
+content_written = signal('content_written')
+feed_written = signal('feed_written')
diff --git a/pelican/tests/TestPages/bad_page.rst b/pelican/tests/TestPages/bad_page.rst
new file mode 100644
index 00000000..bc62948b
--- /dev/null
+++ b/pelican/tests/TestPages/bad_page.rst
@@ -0,0 +1,8 @@
+This is a test bad page
+#######################
+
+:status: invalid
+
+The quick brown fox jumped over the lazy dog's back.
+
+The status here is invalid, the page should not render.
diff --git a/pelican/tests/TestPages/hidden_page.rst b/pelican/tests/TestPages/hidden_page.rst
new file mode 100644
index 00000000..57ca329c
--- /dev/null
+++ b/pelican/tests/TestPages/hidden_page.rst
@@ -0,0 +1,8 @@
+This is a test hidden page
+##########################
+
+:status: hidden
+
+The quick brown fox jumped over the lazy dog's back.
+
+This page is hidden
diff --git a/pelican/tests/TestPages/hidden_page_markdown.md b/pelican/tests/TestPages/hidden_page_markdown.md
new file mode 100644
index 00000000..1e532fe7
--- /dev/null
+++ b/pelican/tests/TestPages/hidden_page_markdown.md
@@ -0,0 +1,12 @@
+title: This is a markdown test hidden page
+status: hidden
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
+
+This page is hidden
diff --git a/pelican/tests/TestPages/hidden_page_with_template.rst b/pelican/tests/TestPages/hidden_page_with_template.rst
new file mode 100644
index 00000000..36104a09
--- /dev/null
+++ b/pelican/tests/TestPages/hidden_page_with_template.rst
@@ -0,0 +1,11 @@
+This is a test hidden page with a custom template
+#################################################
+
+:status: hidden
+:template: custom
+
+The quick brown fox jumped over the lazy dog's back.
+
+This page is hidden
+
+This page has a custom template to be called when rendered
diff --git a/pelican/tests/TestPages/page.rst b/pelican/tests/TestPages/page.rst
new file mode 100644
index 00000000..2d13976d
--- /dev/null
+++ b/pelican/tests/TestPages/page.rst
@@ -0,0 +1,4 @@
+This is a test page
+###################
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican/tests/TestPages/page_markdown.md b/pelican/tests/TestPages/page_markdown.md
new file mode 100644
index 00000000..d5416a6f
--- /dev/null
+++ b/pelican/tests/TestPages/page_markdown.md
@@ -0,0 +1,9 @@
+title: This is a markdown test page
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican/tests/TestPages/page_used_for_sorting_test.rst b/pelican/tests/TestPages/page_used_for_sorting_test.rst
new file mode 100644
index 00000000..40cdc7ea
--- /dev/null
+++ b/pelican/tests/TestPages/page_used_for_sorting_test.rst
@@ -0,0 +1,6 @@
+A Page (Test) for sorting
+#########################
+
+:slug: zzzz
+
+When using title, should be first. When using slug, should be last.
diff --git a/pelican/tests/TestPages/page_with_template.rst b/pelican/tests/TestPages/page_with_template.rst
new file mode 100644
index 00000000..9388dc2f
--- /dev/null
+++ b/pelican/tests/TestPages/page_with_template.rst
@@ -0,0 +1,8 @@
+This is a test page with a preset template
+##########################################
+
+:template: custom
+
+The quick brown fox jumped over the lazy dog's back.
+
+This article has a custom template to be called when rendered
diff --git a/pelican/tests/__init__.py b/pelican/tests/__init__.py
new file mode 100644
index 00000000..32353ea2
--- /dev/null
+++ b/pelican/tests/__init__.py
@@ -0,0 +1,2 @@
+import logging
+logging.getLogger().addHandler(logging.NullHandler())
diff --git a/pelican/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst b/pelican/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst
new file mode 100644
index 00000000..43f05a15
--- /dev/null
+++ b/pelican/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst
@@ -0,0 +1,6 @@
+
+Rst with filename metadata
+##########################
+
+:category: yeah
+:author: Alexis Métaireau
diff --git a/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md b/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md
new file mode 100644
index 00000000..cdccfc8a
--- /dev/null
+++ b/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md
@@ -0,0 +1,6 @@
+category: yeah
+author: Alexis Métaireau
+
+Markdown with filename metadata
+===============================
+
diff --git a/pelican/tests/content/TestCategory/article_with_category.rst b/pelican/tests/content/TestCategory/article_with_category.rst
new file mode 100644
index 00000000..69ffa98b
--- /dev/null
+++ b/pelican/tests/content/TestCategory/article_with_category.rst
@@ -0,0 +1,7 @@
+This is an article with category !
+##################################
+
+:category: yeah
+:date: 1970-01-01
+
+This article should be in 'yeah' category.
diff --git a/pelican/tests/content/TestCategory/article_without_category.rst b/pelican/tests/content/TestCategory/article_without_category.rst
new file mode 100644
index 00000000..4bc5d78d
--- /dev/null
+++ b/pelican/tests/content/TestCategory/article_without_category.rst
@@ -0,0 +1,4 @@
+This is an article without category !
+#####################################
+
+This article should be in 'TestCategory' category.
diff --git a/pelican/tests/content/article.rst b/pelican/tests/content/article.rst
new file mode 100644
index 00000000..793e6869
--- /dev/null
+++ b/pelican/tests/content/article.rst
@@ -0,0 +1,6 @@
+Article title
+#############
+
+THIS is some content. With some stuff to "typogrify"...
+
+Now with added support for :abbr:`TLA (three letter acronym)`.
diff --git a/pelican/tests/content/article_with_code_block.rst b/pelican/tests/content/article_with_code_block.rst
new file mode 100644
index 00000000..586878cf
--- /dev/null
+++ b/pelican/tests/content/article_with_code_block.rst
@@ -0,0 +1,15 @@
+An Article With Code Block To Test Typogrify Ignore
+###################################################
+
+An article with some code
+
+.. code-block:: python
+
+ x & y
+
+A block quote:
+
+ x & y
+
+Normal:
+x & y
diff --git a/pelican/tests/content/article_with_comments.html b/pelican/tests/content/article_with_comments.html
new file mode 100644
index 00000000..289e4a66
--- /dev/null
+++ b/pelican/tests/content/article_with_comments.html
@@ -0,0 +1,8 @@
+
+
+
+
+ Body content
+
+
+
diff --git a/pelican/tests/content/article_with_keywords.html b/pelican/tests/content/article_with_keywords.html
new file mode 100644
index 00000000..0744c754
--- /dev/null
+++ b/pelican/tests/content/article_with_keywords.html
@@ -0,0 +1,6 @@
+
+
+ This is a super article !
+
+
+
diff --git a/pelican/tests/content/article_with_markdown_and_footnote.md b/pelican/tests/content/article_with_markdown_and_footnote.md
new file mode 100644
index 00000000..6fea2d6e
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_and_footnote.md
@@ -0,0 +1,15 @@
+Title: Article with markdown containing footnotes
+Date: 2012-10-31
+Modified: 2012-11-01
+Summary: Summary with **inline** markup *should* be supported.
+Multiline: Line Metadata should be handle properly.
+ See syntax of Meta-Data extension of Python Markdown package:
+ If a line is indented by 4 or more spaces,
+ that line is assumed to be an additional line of the value
+ for the previous keyword.
+ A keyword may have as many lines as desired.
+
+This is some content[^1] with some footnotes[^footnote]
+
+[^1]: Numbered footnote
+[^footnote]: Named footnote
diff --git a/pelican/tests/content/article_with_markdown_and_nonascii_summary.md b/pelican/tests/content/article_with_markdown_and_nonascii_summary.md
new file mode 100644
index 00000000..d76ed4a1
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_and_nonascii_summary.md
@@ -0,0 +1,19 @@
+Title: マックOS X 10.8でパイソンとVirtualenvをインストールと設定
+Slug: python-virtualenv-on-mac-osx-mountain-lion-10.8
+Date: 2012-12-20
+Modified: 2012-12-22
+Tags: パイソン, マック
+Category: 指導書
+Summary: パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+Writing unicode is certainly fun.
+
+パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
+
+And let's mix languages.
+
+первый пост
+
+Now another.
+
+İlk yazı çok özel değil.
diff --git a/pelican/tests/content/article_with_markdown_and_summary_metadata_multi.md b/pelican/tests/content/article_with_markdown_and_summary_metadata_multi.md
new file mode 100644
index 00000000..b6ef666c
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_and_summary_metadata_multi.md
@@ -0,0 +1,7 @@
+Title: Article with markdown and summary metadata multi
+Date: 2012-10-31
+Summary:
+ A multi-line summary should be supported
+ as well as **inline markup**.
+
+This is some content.
diff --git a/pelican/tests/content/article_with_markdown_and_summary_metadata_single.md b/pelican/tests/content/article_with_markdown_and_summary_metadata_single.md
new file mode 100644
index 00000000..a7d6f09b
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_and_summary_metadata_single.md
@@ -0,0 +1,5 @@
+Title: Article with markdown and summary metadata single
+Date: 2012-10-30
+Summary: A single-line summary should be supported as well as **inline markup**.
+
+This is some content.
diff --git a/pelican/tests/content/article_with_markdown_extension.markdown b/pelican/tests/content/article_with_markdown_extension.markdown
new file mode 100644
index 00000000..94e92871
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_extension.markdown
@@ -0,0 +1,10 @@
+title: Test markdown File
+category: test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+This is another markdown test file. Uses the markdown extension.
diff --git a/pelican/tests/content/article_with_markdown_markup_extensions.md b/pelican/tests/content/article_with_markdown_markup_extensions.md
new file mode 100644
index 00000000..6cf56403
--- /dev/null
+++ b/pelican/tests/content/article_with_markdown_markup_extensions.md
@@ -0,0 +1,8 @@
+Title: Test Markdown extensions
+
+[TOC]
+
+## Level1
+
+### Level2
+
diff --git a/pelican/tests/content/article_with_md_extension.md b/pelican/tests/content/article_with_md_extension.md
new file mode 100644
index 00000000..89b6980c
--- /dev/null
+++ b/pelican/tests/content/article_with_md_extension.md
@@ -0,0 +1,14 @@
+Title: Test md File
+Category: test
+Tags: foo, bar, foobar
+Date: 2010-12-02 10:14
+Modified: 2010-12-02 10:20
+Summary: I have a lot to test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+The quick brown fox jumped over the lazy dog's back.
diff --git a/pelican/tests/content/article_with_mdown_extension.mdown b/pelican/tests/content/article_with_mdown_extension.mdown
new file mode 100644
index 00000000..bdaf74cd
--- /dev/null
+++ b/pelican/tests/content/article_with_mdown_extension.mdown
@@ -0,0 +1,10 @@
+title: Test mdown File
+category: test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+This is another markdown test file. Uses the mdown extension.
diff --git a/pelican/tests/content/article_with_metadata.html b/pelican/tests/content/article_with_metadata.html
new file mode 100644
index 00000000..b501ea29
--- /dev/null
+++ b/pelican/tests/content/article_with_metadata.html
@@ -0,0 +1,15 @@
+
+
+ This is a super article !
+
+
+
+
+
+
+
+
+ Multi-line metadata should be supported
+ as well as inline markup .
+
+
diff --git a/pelican/tests/content/article_with_metadata.rst b/pelican/tests/content/article_with_metadata.rst
new file mode 100644
index 00000000..9b65a4b0
--- /dev/null
+++ b/pelican/tests/content/article_with_metadata.rst
@@ -0,0 +1,13 @@
+
+This is a super article !
+#########################
+
+:tags: foo, bar, foobar
+:date: 2010-12-02 10:14
+:modified: 2010-12-02 10:20
+:category: yeah
+:author: Alexis Métaireau
+:summary:
+ Multi-line metadata should be supported
+ as well as **inline markup** and stuff to "typogrify"...
+:custom_field: http://notmyidea.org
diff --git a/pelican/tests/content/article_with_metadata.unknownextension b/pelican/tests/content/article_with_metadata.unknownextension
new file mode 100644
index 00000000..d4bac1c0
--- /dev/null
+++ b/pelican/tests/content/article_with_metadata.unknownextension
@@ -0,0 +1,12 @@
+
+This is a super article !
+#########################
+
+:tags: foo, bar, foobar
+:date: 2010-12-02 10:14
+:category: yeah
+:author: Alexis Métaireau
+:summary:
+ Multi-line metadata should be supported
+ as well as **inline markup**.
+:custom_field: http://notmyidea.org
diff --git a/pelican/tests/content/article_with_metadata_and_contents.html b/pelican/tests/content/article_with_metadata_and_contents.html
new file mode 100644
index 00000000..b108ac8a
--- /dev/null
+++ b/pelican/tests/content/article_with_metadata_and_contents.html
@@ -0,0 +1,15 @@
+
+
+ This is a super article !
+
+
+
+
+
+
+
+
+ Multi-line metadata should be supported
+ as well as inline markup .
+
+
diff --git a/pelican/tests/content/article_with_mkd_extension.mkd b/pelican/tests/content/article_with_mkd_extension.mkd
new file mode 100644
index 00000000..c946cb87
--- /dev/null
+++ b/pelican/tests/content/article_with_mkd_extension.mkd
@@ -0,0 +1,10 @@
+title: Test mkd File
+category: test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+This is another markdown test file. Uses the mkd extension.
diff --git a/pelican/tests/content/article_with_multiple_authors.html b/pelican/tests/content/article_with_multiple_authors.html
new file mode 100644
index 00000000..a74442c9
--- /dev/null
+++ b/pelican/tests/content/article_with_multiple_authors.html
@@ -0,0 +1,6 @@
+
+
+ This is an article with multiple authors!
+
+
+
diff --git a/pelican/tests/content/article_with_multiple_authors.rst b/pelican/tests/content/article_with_multiple_authors.rst
new file mode 100644
index 00000000..04af017c
--- /dev/null
+++ b/pelican/tests/content/article_with_multiple_authors.rst
@@ -0,0 +1,6 @@
+This is an article with multiple authors!
+#########################################
+
+:date: 2014-02-09 02:20
+:modified: 2014-02-09 02:20
+:authors: First Author, Second Author
diff --git a/pelican/tests/content/article_with_nonconformant_meta_tags.html b/pelican/tests/content/article_with_nonconformant_meta_tags.html
new file mode 100644
index 00000000..5ed44bbd
--- /dev/null
+++ b/pelican/tests/content/article_with_nonconformant_meta_tags.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Article with Nonconformant HTML meta tags
+
+
+
+ Multi-line metadata should be supported
+ as well as inline markup .
+
+
diff --git a/pelican/tests/content/article_with_null_attributes.html b/pelican/tests/content/article_with_null_attributes.html
new file mode 100644
index 00000000..68da704c
--- /dev/null
+++ b/pelican/tests/content/article_with_null_attributes.html
@@ -0,0 +1,8 @@
+
+
+
+
+ Ensure that empty attributes are copied properly.
+
+
+
diff --git a/pelican/tests/content/article_with_template.rst b/pelican/tests/content/article_with_template.rst
new file mode 100644
index 00000000..eb55738c
--- /dev/null
+++ b/pelican/tests/content/article_with_template.rst
@@ -0,0 +1,8 @@
+Article with template
+#####################
+
+:template: custom
+
+This article has a custom template to be called when rendered
+
+This is some content. With some stuff to "typogrify".
diff --git a/pelican/tests/content/article_with_uppercase_metadata.html b/pelican/tests/content/article_with_uppercase_metadata.html
new file mode 100644
index 00000000..b4cedf39
--- /dev/null
+++ b/pelican/tests/content/article_with_uppercase_metadata.html
@@ -0,0 +1,6 @@
+
+
+ This is a super article !
+
+
+
diff --git a/pelican/tests/content/article_with_uppercase_metadata.rst b/pelican/tests/content/article_with_uppercase_metadata.rst
new file mode 100644
index 00000000..e26cdd13
--- /dev/null
+++ b/pelican/tests/content/article_with_uppercase_metadata.rst
@@ -0,0 +1,6 @@
+
+This is a super article !
+#########################
+
+:Category: Yeah
+
diff --git a/pelican/tests/content/article_without_category.rst b/pelican/tests/content/article_without_category.rst
new file mode 100644
index 00000000..ff47f6ef
--- /dev/null
+++ b/pelican/tests/content/article_without_category.rst
@@ -0,0 +1,6 @@
+
+This is an article without category !
+#####################################
+
+This article should be in the DEFAULT_CATEGORY.
+
diff --git a/pelican/tests/content/wordpress_content_decoded b/pelican/tests/content/wordpress_content_decoded
new file mode 100644
index 00000000..6e91338c
--- /dev/null
+++ b/pelican/tests/content/wordpress_content_decoded
@@ -0,0 +1,48 @@
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
+ a = [1, 2, 3]
+ b = [4, 5, 6]
+ for i in zip(a, b):
+ print i
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/pelican/tests/content/wordpress_content_encoded b/pelican/tests/content/wordpress_content_encoded
new file mode 100644
index 00000000..da35de3b
--- /dev/null
+++ b/pelican/tests/content/wordpress_content_encoded
@@ -0,0 +1,55 @@
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
+
+ a = [1, 2, 3]
+ b = [4, 5, 6]
+ for i in zip(a, b):
+ print i
+
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
diff --git a/pelican/tests/content/wordpressexport.xml b/pelican/tests/content/wordpressexport.xml
new file mode 100644
index 00000000..686b0fa7
--- /dev/null
+++ b/pelican/tests/content/wordpressexport.xml
@@ -0,0 +1,953 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pelican test channel
+ http://thisisa.test
+ Not a real feed, just for test
+ Sun, 13 May 2012 01:13:52 +0000
+ en
+ 1.1
+ http://thisisa.test
+ http://thisisa.test
+
+ 2 Bob bob@thisisa.test
+ 3 Jonh jonh@thisisa.test
+
+ 7 categ-1
+ 11 categ-2
+ 1 uncategorized
+ 15 categ-3
+ 25 tag-1
+ 122 tag2
+ 68 tag-3
+
+ http://wordpress.org/?v=3.3.1
+
+ -
+
Empty post
+ http://thisisa.test/?attachment_id=24
+ Sat, 04 Feb 2012 03:17:33 +0000
+ bob
+ https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Pelican_lakes_entrance02.jpg/240px-Pelican_lakes_entrance02.jpg
+
+
+
+ 24
+ 2012-02-04 03:17:33
+ 2012-02-04 03:17:33
+ open
+ open
+ empty-post
+ inherit
+ 0
+ 0
+ attachment
+
+ 0
+ https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Pelican_lakes_entrance02.jpg/240px-Pelican_lakes_entrance02.jpg
+
+ _wp_attachment_metadata
+
+
+
+ _wp_attached_file
+
+
+
+ _wp_attachment_image_alt
+
+
+
+ -
+
+ http://thisisa.test/?p=168
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=168
+
+
+
+ 168
+ 2012-02-15 21:23:57
+ 0000-00-00 00:00:00
+ open
+ open
+
+ draft
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
A normal post
+ http://thisisa.test/?p=174
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=174
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 174
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+
+ draft
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
Complete draft
+ http://thisisa.test/?p=176
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=176
+
+
+
+ 176
+ 2012-02-17 15:11:55
+ 0000-00-00 00:00:00
+ open
+ open
+
+ draft
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
Page
+ http://thisisa.test/contact/
+ Wed, 11 Apr 2012 11:38:08 +0000
+ bob
+ http://thisisa.test/?page_id=334
+
+
+
+ 334
+ 2012-04-11 06:38:08
+ 2012-04-11 11:38:08
+ open
+ open
+ contact
+ publish
+ 0
+ 0
+ page
+
+ 0
+
+ sharing_disabled
+
+
+
+ _wp_page_template
+
+
+
+ _edit_last
+
+
+
+ -
+
Empty Page
+ http://thisisa.test/empty/
+ Wed, 11 Apr 2012 11:38:08 +0000
+ bob
+ http://thisisa.test/?page_id=334
+
+
+
+ 334
+ 2012-04-11 06:38:08
+ 2012-04-11 11:38:08
+ open
+ open
+ empty
+ publish
+ 0
+ 0
+ page
+
+ 0
+
+ sharing_disabled
+
+
+
+ _wp_page_template
+
+
+
+ _edit_last
+
+
+
+ -
+
Special chars: l'é
+ http://thisisa.test/?p=471
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=471
+
+
+
+ 471
+ 2012-04-29 09:44:27
+ 0000-00-00 00:00:00
+ open
+ open
+
+ draft
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+
+ -
+
With excerpt
+ http://thisisa.test/with-excerpt/
+ Sat, 04 Feb 2012 02:03:06 +0000
+ bob
+ http://thisisa.test/?p=8
+
+
+
+ 8
+ 2012-02-04 02:03:06
+ 2012-02-04 02:03:06
+ open
+ open
+ with-excerpt
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+
+
+ _edit_last
+
+
+
+ et_bigpost
+
+
+
+ _thumbnail_id
+
+
+
+ -
+
With tags
+ http://thisisa.test/tags/
+ Sat, 04 Feb 2012 21:05:25 +0000
+ bob
+ http://thisisa.test/?p=25
+
+
+
+ 25
+ 2012-02-04 21:05:25
+ 2012-02-04 21:05:25
+ open
+ open
+ with-tags
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+
+
+
+ _edit_last
+
+
+
+ et_bigpost
+
+
+
+ _thumbnail_id
+
+
+
+ -
+
With comments
+ http://thisisa.test/with-comments/
+ Wed, 18 Apr 2012 08:36:26 +0000
+ john
+ http://thisisa.test/?p=422
+
+
+
+ 422
+ 2012-04-18 03:36:26
+ 2012-04-18 08:36:26
+ open
+ open
+ with-comments
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ _thumbnail_id
+
+
+
+ 116
+
+ User2@mail.test
+
+ 127.0.0.1
+ 2012-05-06 15:46:06
+ 2012-05-06 20:46:06
+
+ 1
+
+ 0
+ 0
+
+ akismet_result
+
+
+
+ akismet_history
+
+
+
+ akismet_as_submitted
+
+
+
+
+ 117
+
+ bob@thisisa.test
+
+ 127.0.0.1
+ 2012-05-06 17:44:06
+ 2012-05-06 22:44:06
+
+ 1
+
+ 116
+ 3
+
+ akismet_result
+
+
+
+ akismet_history
+
+
+
+ akismet_as_submitted
+
+
+
+
+ 156
+
+
+ http://thisisa.test/to-article-you-ping-back/
+ 127.0.0.1
+ 2012-05-09 19:30:19
+ 2012-05-10 00:30:19
+
+ trash
+ pingback
+ 0
+ 0
+
+ akismet_history
+
+
+
+ _wp_trash_meta_status
+
+
+
+ _wp_trash_meta_time
+
+
+
+
+ 122
+
+ bob@thisisa.test
+
+ 127.0.0.1
+ 2012-05-07 14:11:34
+ 2012-05-07 19:11:34
+
+ 1
+
+ 121
+ 3
+
+ akismet_result
+
+
+
+ akismet_history
+
+
+
+ akismet_as_submitted
+
+
+
+
+ -
+
Post with raw data
+ http://thisisa.test/?p=173
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=173
+
+ Pelicans are scary
+
+Pelicans are supposed to eat fish, damn it!
+
+VIDEO
+
+Bottom line: don't mess up with birds]]>
+
+ 173
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ post-with-raw-data
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
A normal post with some <html> entities in the title. You can't miss them.
+ http://thisisa.test/?p=175
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=175
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 175
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ html-entity-test
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
Code in List
+ http://thisisa.test/?p=175
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=175
+
+
+ List Item One!
+ List Item Two!
+This is a code sample
+
+
+ a = [1, 2, 3]
+ b = [4, 5, 6]
+ for i in zip(a, b):
+ print i
+
+
+ List Item Four!
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 175
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ code-in-list-test
+ publish
+ 0
+ 0
+ post
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
A custom post in category 4
+ http://thisisa.test/?p=175
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=175
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 175
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ custpost1cat4
+ publish
+ 0
+ 0
+ custom1
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
A custom post in category 5
+ http://thisisa.test/?p=176
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=176
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 176
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ custpost1cat5
+ publish
+ 0
+ 0
+ custom1
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
A 2nd custom post type also in category 5
+ http://thisisa.test/?p=177
+ Thu, 01 Jan 1970 00:00:00 +0000
+ bob
+ http://thisisa.test/?p=177
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>
+
+ 177
+ 2012-02-16 15:52:55
+ 0000-00-00 00:00:00
+ open
+ open
+ custpost2cat5
+ publish
+ 0
+ 0
+ custom2
+
+ 0
+
+
+ _edit_last
+
+
+
+ -
+
Attachment with a parent
+ http://thisisa.test/?attachment_id=24
+ Sat, 04 Feb 2012 03:17:33 +0000
+ bob
+ http://thisurlisinvalid.notarealdomain/not_an_image.jpg
+
+
+
+ 25
+ 2012-02-04 03:17:33
+ 2012-02-04 03:17:33
+ open
+ open
+ attachment-with-a-parent
+ inherit
+ 8
+ 0
+ attachment
+
+ 0
+ http://thisurlisinvalid.notarealdomain/not_an_image.jpg
+
+ _wp_attachment_metadata
+
+
+
+ _wp_attached_file
+
+
+
+ _wp_attachment_image_alt
+
+
+
+ -
+
2nd Attachment to same parent
+ http://thisisa.test/?attachment_id=25
+ Sat, 04 Feb 2012 03:17:33 +0000
+ bob
+ http://en.wikipedia.org/wiki/File:Pelikan_Walvis_Bay.jpg
+
+
+
+ 25
+ 2012-02-04 03:17:33
+ 2012-02-04 03:17:33
+ open
+ open
+ 2nd[attachment-to-same-parent
+ inherit
+ 8
+ 0
+ attachment
+
+ 0
+ http://en.wikipedia.org/wiki/File:Pelikan_Walvis_Bay.jpg
+
+ _wp_attachment_metadata
+
+
+
+ _wp_attached_file
+
+
+
+ _wp_attachment_image_alt
+
+
+
+ -
+
Attachment with a different parent
+ http://thisisa.test/?attachment_id=26
+ Sat, 04 Feb 2012 03:17:33 +0000
+ bob
+ http://thisurlisinvalid.notarealdomain
+
+
+
+ 25
+ 2012-02-04 03:17:33
+ 2012-02-04 03:17:33
+ open
+ open
+ attachment-with-a-different-parent
+ inherit
+ 25
+ 0
+ attachment
+
+ 0
+ http://thisurlisinvalid.notarealdomain
+
+ _wp_attachment_metadata
+
+
+
+ _wp_attached_file
+
+
+
+ _wp_attachment_image_alt
+
+
+
+
+
diff --git a/pelican/tests/default_conf.py b/pelican/tests/default_conf.py
new file mode 100644
index 00000000..62594894
--- /dev/null
+++ b/pelican/tests/default_conf.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+AUTHOR = 'Alexis Métaireau'
+SITENAME = "Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+TIMEZONE = 'UTC'
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_GENERATOR = False
+REVERSE_CATEGORY_ORDER = True
+DEFAULT_PAGINATION = 2
+
+FEED_RSS = 'feeds/all.rss.xml'
+CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+ ('Filyb', "http://filyb.info/"),
+ ('Libert-fr', "http://www.libert-fr.com"),
+ ('N1k0', "http://prendreuncafe.com/blog/"),
+ ('Tarek Ziadé', "http://ziade.org/blog"),
+ ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+ ('lastfm', 'http://lastfm.com/user/akounet'),
+ ('github', 'http://github.com/ametaireau'),)
+
+# global metadata to all the contents
+DEFAULT_METADATA = (('yeah', 'it is'),)
+
+# path-specific metadata
+EXTRA_PATH_METADATA = {
+ 'extra/robots.txt': {'path': 'robots.txt'},
+ }
+
+# static paths will be copied without parsing their contents
+STATIC_PATHS = [
+ 'pictures',
+ 'extra/robots.txt',
+ ]
+
+# foobar will not be used, because it's not in caps. All configuration keys
+# have to be in caps
+foobar = "barbaz"
diff --git a/pelican/tests/mixed_content/fake_image.jpg b/pelican/tests/mixed_content/fake_image.jpg
new file mode 100644
index 00000000..e69de29b
diff --git a/pelican/tests/mixed_content/short_page.md b/pelican/tests/mixed_content/short_page.md
new file mode 100644
index 00000000..46ca45ac
--- /dev/null
+++ b/pelican/tests/mixed_content/short_page.md
@@ -0,0 +1,3 @@
+Title: Short Page
+
+This is a page with little text.
diff --git a/pelican/tests/mixed_content/subdir/subdir_fake_image.jpg b/pelican/tests/mixed_content/subdir/subdir_fake_image.jpg
new file mode 100644
index 00000000..e69de29b
diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html
new file mode 100644
index 00000000..5fcc42a9
--- /dev/null
+++ b/pelican/tests/output/basic/a-markdown-powered-article.html
@@ -0,0 +1,69 @@
+
+
+
+
+ A markdown powered article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 April 2011
+
+
+In cat1 .
+
+ You're mutually oblivious.
+
a root-relative link to unbelievable
+a file-relative link to unbelievable
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html
new file mode 100644
index 00000000..f8f1a67f
--- /dev/null
+++ b/pelican/tests/output/basic/archives.html
@@ -0,0 +1,72 @@
+
+
+
+
+ A Pelican Blog
+
+
+
+
+
+
+
+
+
+Archives for A Pelican Blog
+
+
+ Fri 30 November 2012
+ FILENAME_METADATA example
+ Wed 29 February 2012
+ Second article
+ Wed 20 April 2011
+ A markdown powered article
+ Thu 17 February 2011
+ Article 1
+ Thu 17 February 2011
+ Article 2
+ Thu 17 February 2011
+ Article 3
+ Thu 02 December 2010
+ This is a super article !
+ Wed 20 October 2010
+ Oh yeah !
+ Fri 15 October 2010
+ Unbelievable !
+ Sun 14 March 2010
+ The baz tag
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html
new file mode 100644
index 00000000..4ea7f4e3
--- /dev/null
+++ b/pelican/tests/output/basic/article-1.html
@@ -0,0 +1,68 @@
+
+
+
+
+ Article 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 1
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html
new file mode 100644
index 00000000..45130e55
--- /dev/null
+++ b/pelican/tests/output/basic/article-2.html
@@ -0,0 +1,68 @@
+
+
+
+
+ Article 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 2
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html
new file mode 100644
index 00000000..8603430f
--- /dev/null
+++ b/pelican/tests/output/basic/article-3.html
@@ -0,0 +1,68 @@
+
+
+
+
+ Article 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 3
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html
new file mode 100644
index 00000000..11d54185
--- /dev/null
+++ b/pelican/tests/output/basic/author/alexis-metaireau.html
@@ -0,0 +1,112 @@
+
+
+
+
+ A Pelican Blog - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html
new file mode 100644
index 00000000..20df01d2
--- /dev/null
+++ b/pelican/tests/output/basic/authors.html
@@ -0,0 +1,54 @@
+
+
+
+
+ A Pelican Blog - Authors
+
+
+
+
+
+
+
+
+
+
+ Authors on A Pelican Blog
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html
new file mode 100644
index 00000000..55e955c8
--- /dev/null
+++ b/pelican/tests/output/basic/categories.html
@@ -0,0 +1,52 @@
+
+
+
+
+ A Pelican Blog
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html
new file mode 100644
index 00000000..18e434cb
--- /dev/null
+++ b/pelican/tests/output/basic/category/bar.html
@@ -0,0 +1,68 @@
+
+
+
+
+ A Pelican Blog - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html
new file mode 100644
index 00000000..f99eb497
--- /dev/null
+++ b/pelican/tests/output/basic/category/cat1.html
@@ -0,0 +1,127 @@
+
+
+
+
+ A Pelican Blog - cat1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 April 2011
+
+
+In cat1 .
+
+ You're mutually oblivious.
+a root-relative link to unbelievable
+a file-relative link to unbelievable
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 1
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 2
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 3
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html
new file mode 100644
index 00000000..fc724edb
--- /dev/null
+++ b/pelican/tests/output/basic/category/misc.html
@@ -0,0 +1,138 @@
+
+
+
+
+ A Pelican Blog - misc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+In misc .
+
+ Some cool stuff!
+
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html
new file mode 100644
index 00000000..7fe75a86
--- /dev/null
+++ b/pelican/tests/output/basic/category/yeah.html
@@ -0,0 +1,78 @@
+
+
+
+
+ A Pelican Blog - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml
new file mode 100644
index 00000000..f9a2e6e1
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml
@@ -0,0 +1,20 @@
+
+A Pelican Blog / 2013-11-17T23:29:00+00:00 This is a super article ! 2013-11-17T23:29:00+00:00 Alexis Métaireau tag:,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+00:00 Alexis Métaireau tag:,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+
diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml
new file mode 100644
index 00000000..511ffa0e
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml
@@ -0,0 +1,20 @@
+
+A Pelican Blog /Sun, 17 Nov 2013 23:29:00 +0000 This is a super article ! /this-is-a-super-article.html<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0000 tag:,2010-12-02:this-is-a-super-article.html foo bar foobar Oh yeah ! /oh-yeah.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>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0000 tag:,2010-10-20:oh-yeah.html oh bar yeah
diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml
new file mode 100644
index 00000000..3ce41d77
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/all-en.atom.xml
@@ -0,0 +1,61 @@
+
+A Pelican Blog / 2013-11-17T23:29:00+00:00 FILENAME_METADATA example 2012-11-30T00:00:00+00:00 tag:,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+00:00 tag:,2012-02-29:second-article.html <p>This is some article, in english</p>
+ A markdown powered article 2011-04-20T00:00:00+00:00 tag:,2011-04-20:a-markdown-powered-article.html <p>You're mutually oblivious.</p>
+<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
+<a href="/unbelievable.html">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-3.html <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+00:00 Alexis Métaireau tag:,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+00:00 Alexis Métaireau tag:,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+00:00 tag:,2010-10-15:unbelievable.html <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
+<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+00:00 tag:,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
diff --git a/pelican/tests/output/basic/feeds/all-fr.atom.xml b/pelican/tests/output/basic/feeds/all-fr.atom.xml
new file mode 100644
index 00000000..54056df6
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/all-fr.atom.xml
@@ -0,0 +1,3 @@
+
+A Pelican Blog / 2012-02-29T00:00:00+00:00 Deuxième article 2012-02-29T00:00:00+00:00 tag:,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+
diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml
new file mode 100644
index 00000000..3a9478a4
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/all.atom.xml
@@ -0,0 +1,62 @@
+
+A Pelican Blog / 2013-11-17T23:29:00+00:00 FILENAME_METADATA example 2012-11-30T00:00:00+00:00 tag:,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+00:00 tag:,2012-02-29:second-article.html <p>This is some article, in english</p>
+ Deuxième article 2012-02-29T00:00:00+00:00 tag:,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+ A markdown powered article 2011-04-20T00:00:00+00:00 tag:,2011-04-20:a-markdown-powered-article.html <p>You're mutually oblivious.</p>
+<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
+<a href="/unbelievable.html">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-3.html <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+00:00 Alexis Métaireau tag:,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+00:00 Alexis Métaireau tag:,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+00:00 tag:,2010-10-15:unbelievable.html <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
+<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+00:00 tag:,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
diff --git a/pelican/tests/output/basic/feeds/bar.atom.xml b/pelican/tests/output/basic/feeds/bar.atom.xml
new file mode 100644
index 00000000..d1c678ae
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/bar.atom.xml
@@ -0,0 +1,8 @@
+
+A Pelican Blog / 2010-10-20T10:14:00+00:00 Oh yeah ! 2010-10-20T10:14:00+00:00 Alexis Métaireau tag:,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+
diff --git a/pelican/tests/output/basic/feeds/cat1.atom.xml b/pelican/tests/output/basic/feeds/cat1.atom.xml
new file mode 100644
index 00000000..a3c35956
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/cat1.atom.xml
@@ -0,0 +1,7 @@
+
+A Pelican Blog / 2011-04-20T00:00:00+00:00 A markdown powered article 2011-04-20T00:00:00+00:00 tag:,2011-04-20:a-markdown-powered-article.html <p>You're mutually oblivious.</p>
+<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
+<a href="/unbelievable.html">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+00:00 tag:,2011-02-17:article-3.html <p>Article 3</p>
+
diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml
new file mode 100644
index 00000000..a9b5977b
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/misc.atom.xml
@@ -0,0 +1,38 @@
+
+A Pelican Blog / 2012-11-30T00:00:00+00:00 FILENAME_METADATA example 2012-11-30T00:00:00+00:00 tag:,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+00:00 tag:,2012-02-29:second-article.html <p>This is some article, in english</p>
+ Unbelievable ! 2010-10-15T20:30:00+00:00 tag:,2010-10-15:unbelievable.html <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
+<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+00:00 tag:,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
diff --git a/pelican/tests/output/basic/feeds/yeah.atom.xml b/pelican/tests/output/basic/feeds/yeah.atom.xml
new file mode 100644
index 00000000..0df396a2
--- /dev/null
+++ b/pelican/tests/output/basic/feeds/yeah.atom.xml
@@ -0,0 +1,14 @@
+
+A Pelican Blog / 2013-11-17T23:29:00+00:00 This is a super article ! 2013-11-17T23:29:00+00:00 Alexis Métaireau tag:,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+
diff --git a/pelican/tests/output/basic/filename_metadata-example.html b/pelican/tests/output/basic/filename_metadata-example.html
new file mode 100644
index 00000000..638c65dd
--- /dev/null
+++ b/pelican/tests/output/basic/filename_metadata-example.html
@@ -0,0 +1,68 @@
+
+
+
+
+ FILENAME_METADATA example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+In misc .
+
+ Some cool stuff!
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html
new file mode 100644
index 00000000..f3814b00
--- /dev/null
+++ b/pelican/tests/output/basic/index.html
@@ -0,0 +1,275 @@
+
+
+
+
+ A Pelican Blog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+In misc .
+
+ Some cool stuff!
+
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 1
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 2
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+In cat1 .
+
+ Article 3
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/oh-yeah.html b/pelican/tests/output/basic/oh-yeah.html
new file mode 100644
index 00000000..76be69fe
--- /dev/null
+++ b/pelican/tests/output/basic/oh-yeah.html
@@ -0,0 +1,76 @@
+
+
+
+
+ Oh yeah !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/override/index.html b/pelican/tests/output/basic/override/index.html
new file mode 100644
index 00000000..ed9fa92a
--- /dev/null
+++ b/pelican/tests/output/basic/override/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Override url/save_as
+
+
+
+
+
+
+
+
+
+ Override url/save_as
+
+ Test page which overrides save_as and url so that this page will be generated
+at a custom location.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html
new file mode 100644
index 00000000..ac31987a
--- /dev/null
+++ b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html
@@ -0,0 +1,53 @@
+
+
+
+
+ This is a test hidden page
+
+
+
+
+
+
+
+
+
+ This is a test hidden page
+
+ This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/pages/this-is-a-test-page.html b/pelican/tests/output/basic/pages/this-is-a-test-page.html
new file mode 100644
index 00000000..43e5f72e
--- /dev/null
+++ b/pelican/tests/output/basic/pages/this-is-a-test-page.html
@@ -0,0 +1,53 @@
+
+
+
+
+ This is a test page
+
+
+
+
+
+
+
+
+
+ This is a test page
+
+ Just an image.
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/second-article-fr.html b/pelican/tests/output/basic/second-article-fr.html
new file mode 100644
index 00000000..551027da
--- /dev/null
+++ b/pelican/tests/output/basic/second-article-fr.html
@@ -0,0 +1,70 @@
+
+
+
+
+ Deuxième article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ en
+
+ Ceci est un article, en français.
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/second-article.html b/pelican/tests/output/basic/second-article.html
new file mode 100644
index 00000000..ed350752
--- /dev/null
+++ b/pelican/tests/output/basic/second-article.html
@@ -0,0 +1,70 @@
+
+
+
+
+ Second article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tag/bar.html b/pelican/tests/output/basic/tag/bar.html
new file mode 100644
index 00000000..5331767b
--- /dev/null
+++ b/pelican/tests/output/basic/tag/bar.html
@@ -0,0 +1,124 @@
+
+
+
+
+ A Pelican Blog - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tag/baz.html b/pelican/tests/output/basic/tag/baz.html
new file mode 100644
index 00000000..dc26d8e1
--- /dev/null
+++ b/pelican/tests/output/basic/tag/baz.html
@@ -0,0 +1,68 @@
+
+
+
+
+ The baz tag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tag/foo.html b/pelican/tests/output/basic/tag/foo.html
new file mode 100644
index 00000000..aed3ad17
--- /dev/null
+++ b/pelican/tests/output/basic/tag/foo.html
@@ -0,0 +1,96 @@
+
+
+
+
+ A Pelican Blog - foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html
new file mode 100644
index 00000000..540cde25
--- /dev/null
+++ b/pelican/tests/output/basic/tag/foobar.html
@@ -0,0 +1,78 @@
+
+
+
+
+ A Pelican Blog - foobar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tag/oh.html b/pelican/tests/output/basic/tag/oh.html
new file mode 100644
index 00000000..ef876b8d
--- /dev/null
+++ b/pelican/tests/output/basic/tag/oh.html
@@ -0,0 +1,52 @@
+
+
+
+
+ Oh Oh Oh
+
+
+
+
+
+
+
+
+
+ Oh Oh Oh
+
+ This page overrides the listening of the articles under the oh tag.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html
new file mode 100644
index 00000000..b8da2bc6
--- /dev/null
+++ b/pelican/tests/output/basic/tag/yeah.html
@@ -0,0 +1,68 @@
+
+
+
+
+ A Pelican Blog - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html
new file mode 100644
index 00000000..0eda47d7
--- /dev/null
+++ b/pelican/tests/output/basic/tags.html
@@ -0,0 +1,59 @@
+
+
+
+
+ A Pelican Blog - Tags
+
+
+
+
+
+
+
+
+
+
+ Tags for A Pelican Blog
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css
new file mode 100644
index 00000000..2efb518d
--- /dev/null
+++ b/pelican/tests/output/basic/theme/css/main.css
@@ -0,0 +1,451 @@
+/*
+ Name: Smashing HTML5
+ Date: July 2009
+ Description: Sample layout for HTML5 and CSS3 goodness.
+ Version: 1.0
+ License: MIT
+ Licensed by: Smashing Media GmbH
+ Original author: Enrique Ramírez
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("pygment.css");
+@import url("typogrify.css");
+@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
+
+/***** Global *****/
+/* Body */
+body {
+ background: #F5F4EF;
+ color: #000305;
+ font-size: 87.5%; /* Base font size: 14px */
+ font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+ line-height: 1.429;
+ margin: 0;
+ padding: 0;
+ text-align: left;
+}
+
+/* Headings */
+h1 {font-size: 2em }
+h2 {font-size: 1.571em} /* 22px */
+h3 {font-size: 1.429em} /* 20px */
+h4 {font-size: 1.286em} /* 18px */
+h5 {font-size: 1.143em} /* 16px */
+h6 {font-size: 1em} /* 14px */
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 400;
+ line-height: 1.1;
+ margin-bottom: .8em;
+ font-family: 'Yanone Kaffeesatz', arial, serif;
+}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+
+hr { border: 2px solid #EEEEEE; }
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+ color: #C74350;
+ padding: 0 1px;
+ text-decoration: underline;
+}
+a:hover, a:active {
+ background-color: #C74350;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: 1px 1px 1px #333;
+}
+
+h1 a:hover {
+ background-color: inherit
+}
+
+/* Paragraphs */
+div.line-block,
+p { margin-top: 1em;
+ margin-bottom: 1em;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+/* Lists */
+ul {
+ list-style: outside disc;
+ margin: 0em 0 0 1.5em;
+}
+
+ol {
+ list-style: outside decimal;
+ margin: 0em 0 0 1.5em;
+}
+
+li { margin-top: 0.5em;}
+
+.post-info {
+ float:right;
+ margin:10px;
+ padding:5px;
+}
+
+.post-info p{
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+.readmore { float: right }
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
+
+/* Quotes */
+blockquote {
+ margin: 20px;
+ font-style: italic;
+}
+cite {}
+
+q {}
+
+div.note {
+ float: right;
+ margin: 5px;
+ font-size: 85%;
+ max-width: 300px;
+}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+
+ /* Thead */
+ thead th {padding: .5em .4em; text-align: left;}
+ thead td {}
+
+ /* Tbody */
+ tbody td {padding: .5em .4em;}
+ tbody th {}
+
+ tbody .alt td {}
+ tbody .alt th {}
+
+ /* Tfoot */
+ tfoot th {}
+ tfoot td {}
+
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+ display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right, figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: left; margin: 0 2em 2em 0;}
+
+/*
+ Header
+*****************/
+#banner {
+ margin: 0 auto;
+ padding: 2.5em 0 0 0;
+}
+
+ /* Banner */
+ #banner h1 {font-size: 3.571em; line-height: 0;}
+ #banner h1 a:link, #banner h1 a:visited {
+ color: #000305;
+ display: block;
+ font-weight: bold;
+ margin: 0 0 .6em .2em;
+ text-decoration: none;
+ }
+ #banner h1 a:hover, #banner h1 a:active {
+ background: none;
+ color: #C74350;
+ text-shadow: none;
+ }
+
+ #banner h1 strong {font-size: 0.36em; font-weight: normal;}
+
+ /* Main Nav */
+ #banner nav {
+ background: #000305;
+ font-size: 1.143em;
+ height: 40px;
+ line-height: 30px;
+ margin: 0 auto 2em auto;
+ padding: 0;
+ text-align: center;
+ width: 800px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+ #banner nav li {float: left; display: inline; margin: 0;}
+
+ #banner nav a:link, #banner nav a:visited {
+ color: #fff;
+ display: inline-block;
+ height: 30px;
+ padding: 5px 1.5em;
+ text-decoration: none;
+ }
+ #banner nav a:hover, #banner nav a:active,
+ #banner nav .active a:link, #banner nav .active a:visited {
+ background: #C74451;
+ color: #fff;
+ text-shadow: none !important;
+ }
+
+ #banner nav li:first-child a {
+ border-top-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -webkit-border-top-left-radius: 5px;
+
+ border-bottom-left-radius: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ }
+
+/*
+ Featured
+*****************/
+#featured {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#featured figure {
+ border: 2px solid #eee;
+ float: right;
+ margin: 0.786em 2em 0 5em;
+ width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+ Body
+*****************/
+#content {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+/*
+ Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+ color: #C74350;
+ font-size: 1.429em;
+ margin-bottom: .25em;
+ padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+ color: #444;
+ display: block;
+ border-bottom: 1px solid #F4E3E3;
+ text-decoration: none;
+ padding: .3em .25em;
+}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+ /* Blogroll */
+ #extras .blogroll {
+ float: left;
+ width: 615px;
+ }
+
+ #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+
+ /* Social */
+ #extras .social {
+ float: right;
+ width: 175px;
+ }
+
+ #extras div[class='social'] a {
+ background-repeat: no-repeat;
+ background-position: 3px 6px;
+ padding-left: 25px;
+ }
+
+ /* Icons */
+ .social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
+ .social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');}
+ .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+ .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+ .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+ .social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
+ .social a[href*='github.com'],
+ .social a[href*='git.io'] {
+ background-image: url('../images/icons/github.png');
+ background-size: 16px 16px;
+ }
+ .social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
+ .social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');}
+ .social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');}
+ .social a[href*='news.ycombinator.com'],
+ .social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');}
+ .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+ .social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
+ .social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');}
+ .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
+ .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');}
+ .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');}
+ .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');}
+ .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+ .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');}
+ .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');}
+
+/*
+ About
+*****************/
+#about {
+ background: #fff;
+ font-style: normal;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ text-align: left;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+ Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+ display: block;
+ clear: both;
+ border-bottom: 1px solid #eee;
+ padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+.hentry img{display : none ;}
+.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
+.entry-title a:visited {background-color: #fff;}
+
+.hentry .post-info * {font-style: normal;}
+
+ /* Content */
+ .hentry footer {margin-bottom: 2em;}
+ .hentry footer address {display: inline;}
+ #posts-list footer address {display: block;}
+
+ /* Blog Index */
+ #posts-list {list-style: none; margin: 0;}
+ #posts-list .hentry {padding-left: 10px; position: relative;}
+
+ #posts-list footer {
+ left: 10px;
+ position: relative;
+ float: left;
+ top: 0.5em;
+ width: 190px;
+ }
+
+ /* About the Author */
+ #about-author {
+ background: #f9f9f9;
+ clear: both;
+ font-style: normal;
+ margin: 2em 0;
+ padding: 10px 20px 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #about-author strong {
+ color: #C64350;
+ clear: both;
+ display: block;
+ font-size: 1.429em;
+ }
+
+ #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+
+ /* Comments */
+ #comments-list {list-style: none; margin: 0 1em;}
+ #comments-list blockquote {
+ background: #f8f8f8;
+ clear: both;
+ font-style: normal;
+ margin: 0;
+ padding: 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+ #comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+
+ #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+
+ /* Add a Comment */
+ #add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+ #add-comment input[type='text'],
+ #add-comment input[type='email'],
+ #add-comment input[type='url'] {float: left; width: 200px;}
+
+ #add-comment textarea {float: left; height: 150px; width: 495px;}
+
+ #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+
+ #add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+ #add-comment * {margin-bottom: .5em;}
diff --git a/pelican/tests/output/basic/theme/css/pygment.css b/pelican/tests/output/basic/theme/css/pygment.css
new file mode 100644
index 00000000..fdd056f6
--- /dev/null
+++ b/pelican/tests/output/basic/theme/css/pygment.css
@@ -0,0 +1,205 @@
+.hll {
+background-color:#eee;
+}
+.c {
+color:#408090;
+font-style:italic;
+}
+.err {
+border:1px solid #FF0000;
+}
+.k {
+color:#007020;
+font-weight:bold;
+}
+.o {
+color:#666666;
+}
+.cm {
+color:#408090;
+font-style:italic;
+}
+.cp {
+color:#007020;
+}
+.c1 {
+color:#408090;
+font-style:italic;
+}
+.cs {
+background-color:#FFF0F0;
+color:#408090;
+}
+.gd {
+color:#A00000;
+}
+.ge {
+font-style:italic;
+}
+.gr {
+color:#FF0000;
+}
+.gh {
+color:#000080;
+font-weight:bold;
+}
+.gi {
+color:#00A000;
+}
+.go {
+color:#303030;
+}
+.gp {
+color:#C65D09;
+font-weight:bold;
+}
+.gs {
+font-weight:bold;
+}
+.gu {
+color:#800080;
+font-weight:bold;
+}
+.gt {
+color:#0040D0;
+}
+.kc {
+color:#007020;
+font-weight:bold;
+}
+.kd {
+color:#007020;
+font-weight:bold;
+}
+.kn {
+color:#007020;
+font-weight:bold;
+}
+.kp {
+color:#007020;
+}
+.kr {
+color:#007020;
+font-weight:bold;
+}
+.kt {
+color:#902000;
+}
+.m {
+color:#208050;
+}
+.s {
+color:#4070A0;
+}
+.na {
+color:#4070A0;
+}
+.nb {
+color:#007020;
+}
+.nc {
+color:#0E84B5;
+font-weight:bold;
+}
+.no {
+color:#60ADD5;
+}
+.nd {
+color:#555555;
+font-weight:bold;
+}
+.ni {
+color:#D55537;
+font-weight:bold;
+}
+.ne {
+color:#007020;
+}
+.nf {
+color:#06287E;
+}
+.nl {
+color:#002070;
+font-weight:bold;
+}
+.nn {
+color:#0E84B5;
+font-weight:bold;
+}
+.nt {
+color:#062873;
+font-weight:bold;
+}
+.nv {
+color:#BB60D5;
+}
+.ow {
+color:#007020;
+font-weight:bold;
+}
+.w {
+color:#BBBBBB;
+}
+.mf {
+color:#208050;
+}
+.mh {
+color:#208050;
+}
+.mi {
+color:#208050;
+}
+.mo {
+color:#208050;
+}
+.sb {
+color:#4070A0;
+}
+.sc {
+color:#4070A0;
+}
+.sd {
+color:#4070A0;
+font-style:italic;
+}
+.s2 {
+color:#4070A0;
+}
+.se {
+color:#4070A0;
+font-weight:bold;
+}
+.sh {
+color:#4070A0;
+}
+.si {
+color:#70A0D0;
+font-style:italic;
+}
+.sx {
+color:#C65D09;
+}
+.sr {
+color:#235388;
+}
+.s1 {
+color:#4070A0;
+}
+.ss {
+color:#517918;
+}
+.bp {
+color:#007020;
+}
+.vc {
+color:#BB60D5;
+}
+.vg {
+color:#BB60D5;
+}
+.vi {
+color:#BB60D5;
+}
+.il {
+color:#208050;
+}
diff --git a/pelican/tests/output/basic/theme/css/reset.css b/pelican/tests/output/basic/theme/css/reset.css
new file mode 100644
index 00000000..1e217566
--- /dev/null
+++ b/pelican/tests/output/basic/theme/css/reset.css
@@ -0,0 +1,52 @@
+/*
+ Name: Reset Stylesheet
+ Description: Resets browser's default CSS
+ Author: Eric Meyer
+ Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
\ No newline at end of file
diff --git a/pelican/tests/output/basic/theme/css/typogrify.css b/pelican/tests/output/basic/theme/css/typogrify.css
new file mode 100644
index 00000000..c9b34dc8
--- /dev/null
+++ b/pelican/tests/output/basic/theme/css/typogrify.css
@@ -0,0 +1,3 @@
+.caps {font-size:.92em;}
+.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;}
+.dquo {margin-left:-.38em;}
diff --git a/pelican/tests/output/basic/theme/css/wide.css b/pelican/tests/output/basic/theme/css/wide.css
new file mode 100644
index 00000000..88fd59ce
--- /dev/null
+++ b/pelican/tests/output/basic/theme/css/wide.css
@@ -0,0 +1,48 @@
+@import url("main.css");
+
+body {
+ font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
+}
+
+.post-info{
+ display: none;
+}
+
+#banner nav {
+ display: none;
+ -moz-border-radius: 0px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ font-size: 1em;
+ background: #F5F4EF;
+}
+
+#banner nav ul{
+ padding-right: 50px;
+}
+
+#banner nav li{
+ float: right;
+ color: #000;
+}
+
+#banner nav li a {
+ color: #000;
+}
+
+#banner h1 {
+ margin-bottom: -18px;
+}
+
+#featured, #extras {
+ padding: 50px;
+}
+
+#featured {
+ padding-top: 20px;
+}
+
+#extras {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
diff --git a/pelican/tests/output/basic/theme/images/icons/aboutme.png b/pelican/tests/output/basic/theme/images/icons/aboutme.png
new file mode 100644
index 00000000..9609df3b
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/aboutme.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/bitbucket.png b/pelican/tests/output/basic/theme/images/icons/bitbucket.png
new file mode 100644
index 00000000..d05ba161
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/bitbucket.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/delicious.png b/pelican/tests/output/basic/theme/images/icons/delicious.png
new file mode 100644
index 00000000..3dccdd84
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/delicious.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/facebook.png b/pelican/tests/output/basic/theme/images/icons/facebook.png
new file mode 100644
index 00000000..74e7ad52
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/facebook.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/github.png b/pelican/tests/output/basic/theme/images/icons/github.png
new file mode 100644
index 00000000..8b25551a
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/github.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/gitorious.png b/pelican/tests/output/basic/theme/images/icons/gitorious.png
new file mode 100644
index 00000000..3eeb3ece
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/gitorious.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/gittip.png b/pelican/tests/output/basic/theme/images/icons/gittip.png
new file mode 100644
index 00000000..af949625
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/gittip.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/google-groups.png b/pelican/tests/output/basic/theme/images/icons/google-groups.png
new file mode 100644
index 00000000..5de15e68
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/google-groups.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/google-plus.png b/pelican/tests/output/basic/theme/images/icons/google-plus.png
new file mode 100644
index 00000000..3c6b7432
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/google-plus.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/hackernews.png b/pelican/tests/output/basic/theme/images/icons/hackernews.png
new file mode 100644
index 00000000..fc7a82d4
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/hackernews.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/lastfm.png b/pelican/tests/output/basic/theme/images/icons/lastfm.png
new file mode 100644
index 00000000..3a6c6262
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/lastfm.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/linkedin.png b/pelican/tests/output/basic/theme/images/icons/linkedin.png
new file mode 100644
index 00000000..d29c1201
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/linkedin.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/reddit.png b/pelican/tests/output/basic/theme/images/icons/reddit.png
new file mode 100644
index 00000000..71ae1215
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/reddit.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/rss.png b/pelican/tests/output/basic/theme/images/icons/rss.png
new file mode 100644
index 00000000..7862c65a
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/rss.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/slideshare.png b/pelican/tests/output/basic/theme/images/icons/slideshare.png
new file mode 100644
index 00000000..ecc97410
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/slideshare.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/speakerdeck.png b/pelican/tests/output/basic/theme/images/icons/speakerdeck.png
new file mode 100644
index 00000000..087d0931
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/speakerdeck.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/stackoverflow.png b/pelican/tests/output/basic/theme/images/icons/stackoverflow.png
new file mode 100644
index 00000000..f5b65e99
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/stackoverflow.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/twitter.png b/pelican/tests/output/basic/theme/images/icons/twitter.png
new file mode 100644
index 00000000..d0ef3cc1
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/twitter.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/vimeo.png b/pelican/tests/output/basic/theme/images/icons/vimeo.png
new file mode 100644
index 00000000..dba47202
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/vimeo.png differ
diff --git a/pelican/tests/output/basic/theme/images/icons/youtube.png b/pelican/tests/output/basic/theme/images/icons/youtube.png
new file mode 100644
index 00000000..ce6cbe4f
Binary files /dev/null and b/pelican/tests/output/basic/theme/images/icons/youtube.png differ
diff --git a/pelican/tests/output/basic/this-is-a-super-article.html b/pelican/tests/output/basic/this-is-a-super-article.html
new file mode 100644
index 00000000..cf957ebf
--- /dev/null
+++ b/pelican/tests/output/basic/this-is-a-super-article.html
@@ -0,0 +1,86 @@
+
+
+
+
+ This is a super article !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html
new file mode 100644
index 00000000..b9b52031
--- /dev/null
+++ b/pelican/tests/output/basic/unbelievable.html
@@ -0,0 +1,100 @@
+
+
+
+
+ Unbelievable !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
Testing more sourcecode directives
+
8 def run ( self ): self . assert_has_content () 10 try : lexer = get_lexer_by_name ( self . arguments [ 0 ]) 12 except ValueError : # no lexer found - use the text one instead of an exception 14 lexer = TextLexer () 16 if ( 'linenos' in self . options and self . options [ 'linenos' ] not in ( 'table' , 'inline' )): 18 self . options [ 'linenos' ] = 'table' 20 for flag in ( 'nowrap' , 'nobackground' , 'anchorlinenos' ): if flag in self . options : 22 self . options [ flag ] = True 24 # noclasses should already default to False, but just in case... formatter = HtmlFormatter ( noclasses = False , ** self . options ) 26 parsed = highlight ( ' \n ' . join ( self . content ), lexer , formatter ) return [ nodes . raw ( '' , parsed , format = 'html' )]
+
Lovely.
+
+
+
Testing even more sourcecode directives
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
Lovely.
+
+
+
Testing overriding config defaults
+
Even if the default is line numbers, we can override it here
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html
new file mode 100644
index 00000000..577d61b8
--- /dev/null
+++ b/pelican/tests/output/custom/a-markdown-powered-article.html
@@ -0,0 +1,115 @@
+
+
+
+
+ A markdown powered article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html
new file mode 100644
index 00000000..13c3d980
--- /dev/null
+++ b/pelican/tests/output/custom/archives.html
@@ -0,0 +1,100 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+Archives for Alexis' log
+
+
+ Fri 30 November 2012
+ FILENAME_METADATA example
+ Wed 29 February 2012
+ Second article
+ Wed 20 April 2011
+ A markdown powered article
+ Thu 17 February 2011
+ Article 1
+ Thu 17 February 2011
+ Article 2
+ Thu 17 February 2011
+ Article 3
+ Thu 02 December 2010
+ This is a super article !
+ Wed 20 October 2010
+ Oh yeah !
+ Fri 15 October 2010
+ Unbelievable !
+ Sun 14 March 2010
+ The baz tag
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html
new file mode 100644
index 00000000..dea09247
--- /dev/null
+++ b/pelican/tests/output/custom/article-1.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html
new file mode 100644
index 00000000..8d8ad1b3
--- /dev/null
+++ b/pelican/tests/output/custom/article-2.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/article-3.html b/pelican/tests/output/custom/article-3.html
new file mode 100644
index 00000000..7539e0f9
--- /dev/null
+++ b/pelican/tests/output/custom/article-3.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html
new file mode 100644
index 00000000..cb6d93a9
--- /dev/null
+++ b/pelican/tests/output/custom/author/alexis-metaireau.html
@@ -0,0 +1,173 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html
new file mode 100644
index 00000000..abf5bdbb
--- /dev/null
+++ b/pelican/tests/output/custom/author/alexis-metaireau2.html
@@ -0,0 +1,187 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 2 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html
new file mode 100644
index 00000000..30aa3eff
--- /dev/null
+++ b/pelican/tests/output/custom/author/alexis-metaireau3.html
@@ -0,0 +1,138 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 3 / 3
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html
new file mode 100644
index 00000000..d9aaef34
--- /dev/null
+++ b/pelican/tests/output/custom/authors.html
@@ -0,0 +1,82 @@
+
+
+
+
+ Alexis' log - Authors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Authors on Alexis' log
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html
new file mode 100644
index 00000000..17d9de76
--- /dev/null
+++ b/pelican/tests/output/custom/categories.html
@@ -0,0 +1,80 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html
new file mode 100644
index 00000000..9ab46a2e
--- /dev/null
+++ b/pelican/tests/output/custom/category/bar.html
@@ -0,0 +1,101 @@
+
+
+
+
+ Alexis' log - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html
new file mode 100644
index 00000000..76075edf
--- /dev/null
+++ b/pelican/tests/output/custom/category/cat1.html
@@ -0,0 +1,170 @@
+
+
+
+
+ Alexis' log - cat1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 April 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ You're mutually oblivious.
+a root-relative link to unbelievable
+a file-relative link to unbelievable
There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html
new file mode 100644
index 00000000..3a470b7e
--- /dev/null
+++ b/pelican/tests/output/custom/category/misc.html
@@ -0,0 +1,181 @@
+
+
+
+
+ Alexis' log - misc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html
new file mode 100644
index 00000000..a632664f
--- /dev/null
+++ b/pelican/tests/output/custom/category/yeah.html
@@ -0,0 +1,109 @@
+
+
+
+
+ Alexis' log - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html
new file mode 100644
index 00000000..15a3f1db
--- /dev/null
+++ b/pelican/tests/output/custom/drafts/a-draft-article.html
@@ -0,0 +1,100 @@
+
+
+
+
+ A draft article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 02 March 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This is a draft article, it should live under the /drafts/ folder and not be
+listed anywhere else.
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml
new file mode 100644
index 00000000..cb746377
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article.html <p>This is some article, in english</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.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> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-3.html <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:unbelievable.html <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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml
new file mode 100644
index 00000000..2c4b1160
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/filename_metadata-example.html<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html Second article http://blog.notmyidea.org/second-article.html<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:second-article.html foo bar baz A markdown powered article http://blog.notmyidea.org/a-markdown-powered-article.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> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.html Article 1 http://blog.notmyidea.org/article-1.html<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-1.html Article 2 http://blog.notmyidea.org/article-2.html<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-2.html Article 3 http://blog.notmyidea.org/article-3.html<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-3.html This is a super article ! http://blog.notmyidea.org/this-is-a-super-article.html<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html foo bar foobar Oh yeah ! http://blog.notmyidea.org/oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:oh-yeah.html oh bar yeah Unbelievable ! http://blog.notmyidea.org/unbelievable.html<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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:unbelievable.html The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml
new file mode 100644
index 00000000..3574ab42
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/all-en.atom.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article.html <p>This is some article, in english</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.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> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-3.html <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:unbelievable.html <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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/all-fr.atom.xml b/pelican/tests/output/custom/feeds/all-fr.atom.xml
new file mode 100644
index 00000000..5d58742c
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/all-fr.atom.xml
@@ -0,0 +1,4 @@
+
+Alexis' log http://blog.notmyidea.org/ 2012-03-02T14:01:01+01:00 Trop bien ! 2012-03-02T14:01:01+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html <p>Et voila du contenu en français</p>
+ Deuxième article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml
new file mode 100644
index 00000000..391ab85f
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/all.atom.xml
@@ -0,0 +1,63 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Trop bien ! 2012-03-02T14:01:01+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html <p>Et voila du contenu en français</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article.html <p>This is some article, in english</p>
+ Deuxième article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.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> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-3.html <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:unbelievable.html <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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml
new file mode 100644
index 00000000..a78d2dec
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/all.rss.xml
@@ -0,0 +1,63 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/filename_metadata-example.html<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html Trop bien ! http://blog.notmyidea.org/oh-yeah-fr.html<p>Et voila du contenu en français</p>
+ Alexis Métaireau Fri, 02 Mar 2012 14:01:01 +0100 tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html Second article http://blog.notmyidea.org/second-article.html<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:second-article.html foo bar baz Deuxième article http://blog.notmyidea.org/second-article-fr.html<p>Ceci est un article, en français.</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:second-article-fr.html foo bar baz A markdown powered article http://blog.notmyidea.org/a-markdown-powered-article.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> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.html Article 1 http://blog.notmyidea.org/article-1.html<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-1.html Article 2 http://blog.notmyidea.org/article-2.html<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-2.html Article 3 http://blog.notmyidea.org/article-3.html<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-3.html This is a super article ! http://blog.notmyidea.org/this-is-a-super-article.html<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html foo bar foobar Oh yeah ! http://blog.notmyidea.org/oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:oh-yeah.html oh bar yeah Unbelievable ! http://blog.notmyidea.org/unbelievable.html<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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:unbelievable.html The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/bar.atom.xml b/pelican/tests/output/custom/feeds/bar.atom.xml
new file mode 100644
index 00000000..99b7cc45
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/bar.atom.xml
@@ -0,0 +1,8 @@
+
+Alexis' log http://blog.notmyidea.org/ 2010-10-20T10:14:00+02:00 Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/bar.rss.xml b/pelican/tests/output/custom/feeds/bar.rss.xml
new file mode 100644
index 00000000..94bd93f2
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/bar.rss.xml
@@ -0,0 +1,8 @@
+
+Alexis' log http://blog.notmyidea.org/Wed, 20 Oct 2010 10:14:00 +0200 Oh yeah ! http://blog.notmyidea.org/oh-yeah.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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:oh-yeah.html oh bar yeah
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/cat1.atom.xml b/pelican/tests/output/custom/feeds/cat1.atom.xml
new file mode 100644
index 00000000..5454ce4d
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/cat1.atom.xml
@@ -0,0 +1,7 @@
+
+Alexis' log http://blog.notmyidea.org/ 2011-04-20T00:00:00+02:00 A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.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> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-1.html <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-2.html <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:article-3.html <p>Article 3</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/cat1.rss.xml b/pelican/tests/output/custom/feeds/cat1.rss.xml
new file mode 100644
index 00000000..62b93fd1
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/cat1.rss.xml
@@ -0,0 +1,7 @@
+
+Alexis' log http://blog.notmyidea.org/Wed, 20 Apr 2011 00:00:00 +0200 A markdown powered article http://blog.notmyidea.org/a-markdown-powered-article.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> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.html Article 1 http://blog.notmyidea.org/article-1.html<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-1.html Article 2 http://blog.notmyidea.org/article-2.html<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-2.html Article 3 http://blog.notmyidea.org/article-3.html<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:article-3.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml
new file mode 100644
index 00000000..91d6b28f
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/misc.atom.xml
@@ -0,0 +1,38 @@
+
+Alexis' log http://blog.notmyidea.org/ 2012-11-30T00:00:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article.html <p>This is some article, in english</p>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:unbelievable.html <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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml
new file mode 100644
index 00000000..3493d2a3
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/misc.rss.xml
@@ -0,0 +1,38 @@
+
+Alexis' log http://blog.notmyidea.org/Fri, 30 Nov 2012 00:00:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/filename_metadata-example.html<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html Second article http://blog.notmyidea.org/second-article.html<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:second-article.html foo bar baz Unbelievable ! http://blog.notmyidea.org/unbelievable.html<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>
+<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:unbelievable.html The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/yeah.atom.xml b/pelican/tests/output/custom/feeds/yeah.atom.xml
new file mode 100644
index 00000000..bd1da591
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/yeah.atom.xml
@@ -0,0 +1,14 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/feeds/yeah.rss.xml b/pelican/tests/output/custom/feeds/yeah.rss.xml
new file mode 100644
index 00000000..f054d3a6
--- /dev/null
+++ b/pelican/tests/output/custom/feeds/yeah.rss.xml
@@ -0,0 +1,14 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 This is a super article ! http://blog.notmyidea.org/this-is-a-super-article.html<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html foo bar foobar
\ No newline at end of file
diff --git a/pelican/tests/output/custom/filename_metadata-example.html b/pelican/tests/output/custom/filename_metadata-example.html
new file mode 100644
index 00000000..047f12af
--- /dev/null
+++ b/pelican/tests/output/custom/filename_metadata-example.html
@@ -0,0 +1,114 @@
+
+
+
+
+ FILENAME_METADATA example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html
new file mode 100644
index 00000000..c375b208
--- /dev/null
+++ b/pelican/tests/output/custom/index.html
@@ -0,0 +1,173 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 30 November 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html
new file mode 100644
index 00000000..1f2479d6
--- /dev/null
+++ b/pelican/tests/output/custom/index2.html
@@ -0,0 +1,187 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 17 February 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 2 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html
new file mode 100644
index 00000000..e7e4f34a
--- /dev/null
+++ b/pelican/tests/output/custom/index3.html
@@ -0,0 +1,138 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 3 / 3
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html
new file mode 100644
index 00000000..0eafa913
--- /dev/null
+++ b/pelican/tests/output/custom/jinja2_template.html
@@ -0,0 +1,77 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+Some text
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/oh-yeah-fr.html b/pelican/tests/output/custom/oh-yeah-fr.html
new file mode 100644
index 00000000..fa02ac2b
--- /dev/null
+++ b/pelican/tests/output/custom/oh-yeah-fr.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Trop bien !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 02 March 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+Translations:
+ en
+
+ Et voila du contenu en français
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/oh-yeah.html b/pelican/tests/output/custom/oh-yeah.html
new file mode 100644
index 00000000..6d108cf2
--- /dev/null
+++ b/pelican/tests/output/custom/oh-yeah.html
@@ -0,0 +1,121 @@
+
+
+
+
+ Oh yeah !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/override/index.html b/pelican/tests/output/custom/override/index.html
new file mode 100644
index 00000000..e84d79fe
--- /dev/null
+++ b/pelican/tests/output/custom/override/index.html
@@ -0,0 +1,81 @@
+
+
+
+
+ Override url/save_as
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Override url/save_as
+
+ Test page which overrides save_as and url so that this page will be generated
+at a custom location.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html
new file mode 100644
index 00000000..dced8107
--- /dev/null
+++ b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html
@@ -0,0 +1,81 @@
+
+
+
+
+ This is a test hidden page
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a test hidden page
+
+ This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/pages/this-is-a-test-page.html b/pelican/tests/output/custom/pages/this-is-a-test-page.html
new file mode 100644
index 00000000..46ea4fef
--- /dev/null
+++ b/pelican/tests/output/custom/pages/this-is-a-test-page.html
@@ -0,0 +1,81 @@
+
+
+
+
+ This is a test page
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a test page
+
+ Just an image.
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/pictures/Fat_Cat.jpg b/pelican/tests/output/custom/pictures/Fat_Cat.jpg
new file mode 100644
index 00000000..d8a96d35
Binary files /dev/null and b/pelican/tests/output/custom/pictures/Fat_Cat.jpg differ
diff --git a/pelican/tests/output/custom/pictures/Sushi.jpg b/pelican/tests/output/custom/pictures/Sushi.jpg
new file mode 100644
index 00000000..e49e5f0a
Binary files /dev/null and b/pelican/tests/output/custom/pictures/Sushi.jpg differ
diff --git a/pelican/tests/output/custom/pictures/Sushi_Macro.jpg b/pelican/tests/output/custom/pictures/Sushi_Macro.jpg
new file mode 100644
index 00000000..21f935a1
Binary files /dev/null and b/pelican/tests/output/custom/pictures/Sushi_Macro.jpg differ
diff --git a/pelican/tests/output/custom/robots.txt b/pelican/tests/output/custom/robots.txt
new file mode 100644
index 00000000..19a6e299
--- /dev/null
+++ b/pelican/tests/output/custom/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /pictures
diff --git a/pelican/tests/output/custom/second-article-fr.html b/pelican/tests/output/custom/second-article-fr.html
new file mode 100644
index 00000000..6c537609
--- /dev/null
+++ b/pelican/tests/output/custom/second-article-fr.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Deuxième article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ en
+
+ Ceci est un article, en français.
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/second-article.html b/pelican/tests/output/custom/second-article.html
new file mode 100644
index 00000000..30179205
--- /dev/null
+++ b/pelican/tests/output/custom/second-article.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Second article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html
new file mode 100644
index 00000000..28cbbe75
--- /dev/null
+++ b/pelican/tests/output/custom/tag/bar.html
@@ -0,0 +1,160 @@
+
+
+
+
+ Alexis' log - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/tag/baz.html b/pelican/tests/output/custom/tag/baz.html
new file mode 100644
index 00000000..06f3c301
--- /dev/null
+++ b/pelican/tests/output/custom/tag/baz.html
@@ -0,0 +1,114 @@
+
+
+
+
+ The baz tag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Sun 14 March 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html
new file mode 100644
index 00000000..ba3c843e
--- /dev/null
+++ b/pelican/tests/output/custom/tag/foo.html
@@ -0,0 +1,130 @@
+
+
+
+
+ Alexis' log - foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 29 February 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html
new file mode 100644
index 00000000..dc51889a
--- /dev/null
+++ b/pelican/tests/output/custom/tag/foobar.html
@@ -0,0 +1,109 @@
+
+
+
+
+ Alexis' log - foobar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/tag/oh.html b/pelican/tests/output/custom/tag/oh.html
new file mode 100644
index 00000000..21c8e352
--- /dev/null
+++ b/pelican/tests/output/custom/tag/oh.html
@@ -0,0 +1,80 @@
+
+
+
+
+ Oh Oh Oh
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Oh Oh Oh
+
+ This page overrides the listening of the articles under the oh tag.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html
new file mode 100644
index 00000000..3b09c6c5
--- /dev/null
+++ b/pelican/tests/output/custom/tag/yeah.html
@@ -0,0 +1,101 @@
+
+
+
+
+ Alexis' log - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Wed 20 October 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html
new file mode 100644
index 00000000..ba8d53a4
--- /dev/null
+++ b/pelican/tests/output/custom/tags.html
@@ -0,0 +1,87 @@
+
+
+
+
+ Alexis' log - Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tags for Alexis' log
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/theme/css/main.css b/pelican/tests/output/custom/theme/css/main.css
new file mode 100644
index 00000000..2efb518d
--- /dev/null
+++ b/pelican/tests/output/custom/theme/css/main.css
@@ -0,0 +1,451 @@
+/*
+ Name: Smashing HTML5
+ Date: July 2009
+ Description: Sample layout for HTML5 and CSS3 goodness.
+ Version: 1.0
+ License: MIT
+ Licensed by: Smashing Media GmbH
+ Original author: Enrique Ramírez
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("pygment.css");
+@import url("typogrify.css");
+@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
+
+/***** Global *****/
+/* Body */
+body {
+ background: #F5F4EF;
+ color: #000305;
+ font-size: 87.5%; /* Base font size: 14px */
+ font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+ line-height: 1.429;
+ margin: 0;
+ padding: 0;
+ text-align: left;
+}
+
+/* Headings */
+h1 {font-size: 2em }
+h2 {font-size: 1.571em} /* 22px */
+h3 {font-size: 1.429em} /* 20px */
+h4 {font-size: 1.286em} /* 18px */
+h5 {font-size: 1.143em} /* 16px */
+h6 {font-size: 1em} /* 14px */
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 400;
+ line-height: 1.1;
+ margin-bottom: .8em;
+ font-family: 'Yanone Kaffeesatz', arial, serif;
+}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+
+hr { border: 2px solid #EEEEEE; }
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+ color: #C74350;
+ padding: 0 1px;
+ text-decoration: underline;
+}
+a:hover, a:active {
+ background-color: #C74350;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: 1px 1px 1px #333;
+}
+
+h1 a:hover {
+ background-color: inherit
+}
+
+/* Paragraphs */
+div.line-block,
+p { margin-top: 1em;
+ margin-bottom: 1em;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+/* Lists */
+ul {
+ list-style: outside disc;
+ margin: 0em 0 0 1.5em;
+}
+
+ol {
+ list-style: outside decimal;
+ margin: 0em 0 0 1.5em;
+}
+
+li { margin-top: 0.5em;}
+
+.post-info {
+ float:right;
+ margin:10px;
+ padding:5px;
+}
+
+.post-info p{
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+.readmore { float: right }
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
+
+/* Quotes */
+blockquote {
+ margin: 20px;
+ font-style: italic;
+}
+cite {}
+
+q {}
+
+div.note {
+ float: right;
+ margin: 5px;
+ font-size: 85%;
+ max-width: 300px;
+}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+
+ /* Thead */
+ thead th {padding: .5em .4em; text-align: left;}
+ thead td {}
+
+ /* Tbody */
+ tbody td {padding: .5em .4em;}
+ tbody th {}
+
+ tbody .alt td {}
+ tbody .alt th {}
+
+ /* Tfoot */
+ tfoot th {}
+ tfoot td {}
+
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+ display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right, figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: left; margin: 0 2em 2em 0;}
+
+/*
+ Header
+*****************/
+#banner {
+ margin: 0 auto;
+ padding: 2.5em 0 0 0;
+}
+
+ /* Banner */
+ #banner h1 {font-size: 3.571em; line-height: 0;}
+ #banner h1 a:link, #banner h1 a:visited {
+ color: #000305;
+ display: block;
+ font-weight: bold;
+ margin: 0 0 .6em .2em;
+ text-decoration: none;
+ }
+ #banner h1 a:hover, #banner h1 a:active {
+ background: none;
+ color: #C74350;
+ text-shadow: none;
+ }
+
+ #banner h1 strong {font-size: 0.36em; font-weight: normal;}
+
+ /* Main Nav */
+ #banner nav {
+ background: #000305;
+ font-size: 1.143em;
+ height: 40px;
+ line-height: 30px;
+ margin: 0 auto 2em auto;
+ padding: 0;
+ text-align: center;
+ width: 800px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+ #banner nav li {float: left; display: inline; margin: 0;}
+
+ #banner nav a:link, #banner nav a:visited {
+ color: #fff;
+ display: inline-block;
+ height: 30px;
+ padding: 5px 1.5em;
+ text-decoration: none;
+ }
+ #banner nav a:hover, #banner nav a:active,
+ #banner nav .active a:link, #banner nav .active a:visited {
+ background: #C74451;
+ color: #fff;
+ text-shadow: none !important;
+ }
+
+ #banner nav li:first-child a {
+ border-top-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -webkit-border-top-left-radius: 5px;
+
+ border-bottom-left-radius: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ }
+
+/*
+ Featured
+*****************/
+#featured {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#featured figure {
+ border: 2px solid #eee;
+ float: right;
+ margin: 0.786em 2em 0 5em;
+ width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+ Body
+*****************/
+#content {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+/*
+ Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+ color: #C74350;
+ font-size: 1.429em;
+ margin-bottom: .25em;
+ padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+ color: #444;
+ display: block;
+ border-bottom: 1px solid #F4E3E3;
+ text-decoration: none;
+ padding: .3em .25em;
+}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+ /* Blogroll */
+ #extras .blogroll {
+ float: left;
+ width: 615px;
+ }
+
+ #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+
+ /* Social */
+ #extras .social {
+ float: right;
+ width: 175px;
+ }
+
+ #extras div[class='social'] a {
+ background-repeat: no-repeat;
+ background-position: 3px 6px;
+ padding-left: 25px;
+ }
+
+ /* Icons */
+ .social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
+ .social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');}
+ .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+ .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+ .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+ .social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
+ .social a[href*='github.com'],
+ .social a[href*='git.io'] {
+ background-image: url('../images/icons/github.png');
+ background-size: 16px 16px;
+ }
+ .social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
+ .social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');}
+ .social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');}
+ .social a[href*='news.ycombinator.com'],
+ .social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');}
+ .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+ .social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
+ .social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');}
+ .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
+ .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');}
+ .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');}
+ .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');}
+ .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+ .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');}
+ .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');}
+
+/*
+ About
+*****************/
+#about {
+ background: #fff;
+ font-style: normal;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ text-align: left;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+ Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+ display: block;
+ clear: both;
+ border-bottom: 1px solid #eee;
+ padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+.hentry img{display : none ;}
+.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
+.entry-title a:visited {background-color: #fff;}
+
+.hentry .post-info * {font-style: normal;}
+
+ /* Content */
+ .hentry footer {margin-bottom: 2em;}
+ .hentry footer address {display: inline;}
+ #posts-list footer address {display: block;}
+
+ /* Blog Index */
+ #posts-list {list-style: none; margin: 0;}
+ #posts-list .hentry {padding-left: 10px; position: relative;}
+
+ #posts-list footer {
+ left: 10px;
+ position: relative;
+ float: left;
+ top: 0.5em;
+ width: 190px;
+ }
+
+ /* About the Author */
+ #about-author {
+ background: #f9f9f9;
+ clear: both;
+ font-style: normal;
+ margin: 2em 0;
+ padding: 10px 20px 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #about-author strong {
+ color: #C64350;
+ clear: both;
+ display: block;
+ font-size: 1.429em;
+ }
+
+ #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+
+ /* Comments */
+ #comments-list {list-style: none; margin: 0 1em;}
+ #comments-list blockquote {
+ background: #f8f8f8;
+ clear: both;
+ font-style: normal;
+ margin: 0;
+ padding: 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+ #comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+
+ #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+
+ /* Add a Comment */
+ #add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+ #add-comment input[type='text'],
+ #add-comment input[type='email'],
+ #add-comment input[type='url'] {float: left; width: 200px;}
+
+ #add-comment textarea {float: left; height: 150px; width: 495px;}
+
+ #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+
+ #add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+ #add-comment * {margin-bottom: .5em;}
diff --git a/pelican/tests/output/custom/theme/css/pygment.css b/pelican/tests/output/custom/theme/css/pygment.css
new file mode 100644
index 00000000..fdd056f6
--- /dev/null
+++ b/pelican/tests/output/custom/theme/css/pygment.css
@@ -0,0 +1,205 @@
+.hll {
+background-color:#eee;
+}
+.c {
+color:#408090;
+font-style:italic;
+}
+.err {
+border:1px solid #FF0000;
+}
+.k {
+color:#007020;
+font-weight:bold;
+}
+.o {
+color:#666666;
+}
+.cm {
+color:#408090;
+font-style:italic;
+}
+.cp {
+color:#007020;
+}
+.c1 {
+color:#408090;
+font-style:italic;
+}
+.cs {
+background-color:#FFF0F0;
+color:#408090;
+}
+.gd {
+color:#A00000;
+}
+.ge {
+font-style:italic;
+}
+.gr {
+color:#FF0000;
+}
+.gh {
+color:#000080;
+font-weight:bold;
+}
+.gi {
+color:#00A000;
+}
+.go {
+color:#303030;
+}
+.gp {
+color:#C65D09;
+font-weight:bold;
+}
+.gs {
+font-weight:bold;
+}
+.gu {
+color:#800080;
+font-weight:bold;
+}
+.gt {
+color:#0040D0;
+}
+.kc {
+color:#007020;
+font-weight:bold;
+}
+.kd {
+color:#007020;
+font-weight:bold;
+}
+.kn {
+color:#007020;
+font-weight:bold;
+}
+.kp {
+color:#007020;
+}
+.kr {
+color:#007020;
+font-weight:bold;
+}
+.kt {
+color:#902000;
+}
+.m {
+color:#208050;
+}
+.s {
+color:#4070A0;
+}
+.na {
+color:#4070A0;
+}
+.nb {
+color:#007020;
+}
+.nc {
+color:#0E84B5;
+font-weight:bold;
+}
+.no {
+color:#60ADD5;
+}
+.nd {
+color:#555555;
+font-weight:bold;
+}
+.ni {
+color:#D55537;
+font-weight:bold;
+}
+.ne {
+color:#007020;
+}
+.nf {
+color:#06287E;
+}
+.nl {
+color:#002070;
+font-weight:bold;
+}
+.nn {
+color:#0E84B5;
+font-weight:bold;
+}
+.nt {
+color:#062873;
+font-weight:bold;
+}
+.nv {
+color:#BB60D5;
+}
+.ow {
+color:#007020;
+font-weight:bold;
+}
+.w {
+color:#BBBBBB;
+}
+.mf {
+color:#208050;
+}
+.mh {
+color:#208050;
+}
+.mi {
+color:#208050;
+}
+.mo {
+color:#208050;
+}
+.sb {
+color:#4070A0;
+}
+.sc {
+color:#4070A0;
+}
+.sd {
+color:#4070A0;
+font-style:italic;
+}
+.s2 {
+color:#4070A0;
+}
+.se {
+color:#4070A0;
+font-weight:bold;
+}
+.sh {
+color:#4070A0;
+}
+.si {
+color:#70A0D0;
+font-style:italic;
+}
+.sx {
+color:#C65D09;
+}
+.sr {
+color:#235388;
+}
+.s1 {
+color:#4070A0;
+}
+.ss {
+color:#517918;
+}
+.bp {
+color:#007020;
+}
+.vc {
+color:#BB60D5;
+}
+.vg {
+color:#BB60D5;
+}
+.vi {
+color:#BB60D5;
+}
+.il {
+color:#208050;
+}
diff --git a/pelican/tests/output/custom/theme/css/reset.css b/pelican/tests/output/custom/theme/css/reset.css
new file mode 100644
index 00000000..1e217566
--- /dev/null
+++ b/pelican/tests/output/custom/theme/css/reset.css
@@ -0,0 +1,52 @@
+/*
+ Name: Reset Stylesheet
+ Description: Resets browser's default CSS
+ Author: Eric Meyer
+ Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
\ No newline at end of file
diff --git a/pelican/tests/output/custom/theme/css/typogrify.css b/pelican/tests/output/custom/theme/css/typogrify.css
new file mode 100644
index 00000000..c9b34dc8
--- /dev/null
+++ b/pelican/tests/output/custom/theme/css/typogrify.css
@@ -0,0 +1,3 @@
+.caps {font-size:.92em;}
+.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;}
+.dquo {margin-left:-.38em;}
diff --git a/pelican/tests/output/custom/theme/css/wide.css b/pelican/tests/output/custom/theme/css/wide.css
new file mode 100644
index 00000000..88fd59ce
--- /dev/null
+++ b/pelican/tests/output/custom/theme/css/wide.css
@@ -0,0 +1,48 @@
+@import url("main.css");
+
+body {
+ font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
+}
+
+.post-info{
+ display: none;
+}
+
+#banner nav {
+ display: none;
+ -moz-border-radius: 0px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ font-size: 1em;
+ background: #F5F4EF;
+}
+
+#banner nav ul{
+ padding-right: 50px;
+}
+
+#banner nav li{
+ float: right;
+ color: #000;
+}
+
+#banner nav li a {
+ color: #000;
+}
+
+#banner h1 {
+ margin-bottom: -18px;
+}
+
+#featured, #extras {
+ padding: 50px;
+}
+
+#featured {
+ padding-top: 20px;
+}
+
+#extras {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
diff --git a/pelican/tests/output/custom/theme/images/icons/aboutme.png b/pelican/tests/output/custom/theme/images/icons/aboutme.png
new file mode 100644
index 00000000..9609df3b
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/aboutme.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/bitbucket.png b/pelican/tests/output/custom/theme/images/icons/bitbucket.png
new file mode 100644
index 00000000..d05ba161
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/bitbucket.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/delicious.png b/pelican/tests/output/custom/theme/images/icons/delicious.png
new file mode 100644
index 00000000..3dccdd84
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/delicious.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/facebook.png b/pelican/tests/output/custom/theme/images/icons/facebook.png
new file mode 100644
index 00000000..74e7ad52
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/facebook.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/github.png b/pelican/tests/output/custom/theme/images/icons/github.png
new file mode 100644
index 00000000..8b25551a
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/github.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/gitorious.png b/pelican/tests/output/custom/theme/images/icons/gitorious.png
new file mode 100644
index 00000000..3eeb3ece
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/gitorious.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/gittip.png b/pelican/tests/output/custom/theme/images/icons/gittip.png
new file mode 100644
index 00000000..af949625
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/gittip.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/google-groups.png b/pelican/tests/output/custom/theme/images/icons/google-groups.png
new file mode 100644
index 00000000..5de15e68
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/google-groups.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/google-plus.png b/pelican/tests/output/custom/theme/images/icons/google-plus.png
new file mode 100644
index 00000000..3c6b7432
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/google-plus.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/hackernews.png b/pelican/tests/output/custom/theme/images/icons/hackernews.png
new file mode 100644
index 00000000..fc7a82d4
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/hackernews.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/lastfm.png b/pelican/tests/output/custom/theme/images/icons/lastfm.png
new file mode 100644
index 00000000..3a6c6262
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/lastfm.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/linkedin.png b/pelican/tests/output/custom/theme/images/icons/linkedin.png
new file mode 100644
index 00000000..d29c1201
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/linkedin.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/reddit.png b/pelican/tests/output/custom/theme/images/icons/reddit.png
new file mode 100644
index 00000000..71ae1215
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/reddit.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/rss.png b/pelican/tests/output/custom/theme/images/icons/rss.png
new file mode 100644
index 00000000..7862c65a
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/rss.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/slideshare.png b/pelican/tests/output/custom/theme/images/icons/slideshare.png
new file mode 100644
index 00000000..ecc97410
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/slideshare.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/speakerdeck.png b/pelican/tests/output/custom/theme/images/icons/speakerdeck.png
new file mode 100644
index 00000000..087d0931
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/speakerdeck.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/stackoverflow.png b/pelican/tests/output/custom/theme/images/icons/stackoverflow.png
new file mode 100644
index 00000000..f5b65e99
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/stackoverflow.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/twitter.png b/pelican/tests/output/custom/theme/images/icons/twitter.png
new file mode 100644
index 00000000..d0ef3cc1
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/twitter.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/vimeo.png b/pelican/tests/output/custom/theme/images/icons/vimeo.png
new file mode 100644
index 00000000..dba47202
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/vimeo.png differ
diff --git a/pelican/tests/output/custom/theme/images/icons/youtube.png b/pelican/tests/output/custom/theme/images/icons/youtube.png
new file mode 100644
index 00000000..ce6cbe4f
Binary files /dev/null and b/pelican/tests/output/custom/theme/images/icons/youtube.png differ
diff --git a/pelican/tests/output/custom/this-is-a-super-article.html b/pelican/tests/output/custom/this-is-a-super-article.html
new file mode 100644
index 00000000..34ab434e
--- /dev/null
+++ b/pelican/tests/output/custom/this-is-a-super-article.html
@@ -0,0 +1,129 @@
+
+
+
+
+ This is a super article !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Thu 02 December 2010
+
+
+
+ Updated: Sun 17 November 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html
new file mode 100644
index 00000000..395cd1b6
--- /dev/null
+++ b/pelican/tests/output/custom/unbelievable.html
@@ -0,0 +1,146 @@
+
+
+
+
+ Unbelievable !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: Fri 15 October 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
Testing more sourcecode directives
+
8 def run ( self ): self . assert_has_content () 10 try : lexer = get_lexer_by_name ( self . arguments [ 0 ]) 12 except ValueError : # no lexer found - use the text one instead of an exception 14 lexer = TextLexer () 16 if ( 'linenos' in self . options and self . options [ 'linenos' ] not in ( 'table' , 'inline' )): 18 self . options [ 'linenos' ] = 'table' 20 for flag in ( 'nowrap' , 'nobackground' , 'anchorlinenos' ): if flag in self . options : 22 self . options [ flag ] = True 24 # noclasses should already default to False, but just in case... formatter = HtmlFormatter ( noclasses = False , ** self . options ) 26 parsed = highlight ( ' \n ' . join ( self . content ), lexer , formatter ) return [ nodes . raw ( '' , parsed , format = 'html' )]
+
Lovely.
+
+
+
Testing even more sourcecode directives
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
Lovely.
+
+
+
Testing overriding config defaults
+
Even if the default is line numbers, we can override it here
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html
new file mode 100644
index 00000000..1336b487
--- /dev/null
+++ b/pelican/tests/output/custom_locale/archives.html
@@ -0,0 +1,100 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+Archives for Alexis' log
+
+
+ 30 novembre 2012
+ FILENAME_METADATA example
+ 29 février 2012
+ Second article
+ 20 avril 2011
+ A markdown powered article
+ 17 février 2011
+ Article 1
+ 17 février 2011
+ Article 2
+ 17 février 2011
+ Article 3
+ 02 décembre 2010
+ This is a super article !
+ 20 octobre 2010
+ Oh yeah !
+ 15 octobre 2010
+ Unbelievable !
+ 14 mars 2010
+ The baz tag
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html
new file mode 100644
index 00000000..1882015e
--- /dev/null
+++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html
@@ -0,0 +1,173 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 30 novembre 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html
new file mode 100644
index 00000000..d1b50107
--- /dev/null
+++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html
@@ -0,0 +1,187 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 2 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html
new file mode 100644
index 00000000..333ef320
--- /dev/null
+++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html
@@ -0,0 +1,138 @@
+
+
+
+
+ Alexis' log - Alexis Métaireau
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 15 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 14 mars 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 3 / 3
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html
new file mode 100644
index 00000000..2558c4d8
--- /dev/null
+++ b/pelican/tests/output/custom_locale/authors.html
@@ -0,0 +1,82 @@
+
+
+
+
+ Alexis' log - Authors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Authors on Alexis' log
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html
new file mode 100644
index 00000000..17d9de76
--- /dev/null
+++ b/pelican/tests/output/custom_locale/categories.html
@@ -0,0 +1,80 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html
new file mode 100644
index 00000000..fc11f06e
--- /dev/null
+++ b/pelican/tests/output/custom_locale/category/bar.html
@@ -0,0 +1,101 @@
+
+
+
+
+ Alexis' log - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html
new file mode 100644
index 00000000..e1556d9e
--- /dev/null
+++ b/pelican/tests/output/custom_locale/category/cat1.html
@@ -0,0 +1,170 @@
+
+
+
+
+ Alexis' log - cat1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 20 avril 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ You're mutually oblivious.
+a root-relative link to unbelievable
+a file-relative link to unbelievable
There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html
new file mode 100644
index 00000000..3ff2f8e4
--- /dev/null
+++ b/pelican/tests/output/custom_locale/category/misc.html
@@ -0,0 +1,181 @@
+
+
+
+
+ Alexis' log - misc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 30 novembre 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 15 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 14 mars 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html
new file mode 100644
index 00000000..7ee2bbe9
--- /dev/null
+++ b/pelican/tests/output/custom_locale/category/yeah.html
@@ -0,0 +1,109 @@
+
+
+
+
+ Alexis' log - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html
new file mode 100644
index 00000000..a33e6797
--- /dev/null
+++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html
@@ -0,0 +1,100 @@
+
+
+
+
+ A draft article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 02 mars 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This is a draft article, it should live under the /drafts/ folder and not be
+listed anywhere else.
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml
new file mode 100644
index 00000000..202b9f71
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ <p>This is some article, in english</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ <p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ <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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml
new file mode 100644
index 00000000..dfb83630
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ Second article http://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ foo bar baz A markdown powered article http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ Article 1 http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ Article 2 http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ Article 3 http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ This is a super article ! http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ foo bar foobar Oh yeah ! http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ oh bar yeah Unbelievable ! http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml
new file mode 100644
index 00000000..3bb10e38
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml
@@ -0,0 +1,61 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ <p>This is some article, in english</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ <p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ <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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml
new file mode 100644
index 00000000..5d58742c
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml
@@ -0,0 +1,4 @@
+
+Alexis' log http://blog.notmyidea.org/ 2012-03-02T14:01:01+01:00 Trop bien ! 2012-03-02T14:01:01+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html <p>Et voila du contenu en français</p>
+ Deuxième article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/all.atom.xml b/pelican/tests/output/custom_locale/feeds/all.atom.xml
new file mode 100644
index 00000000..f709f2b1
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/all.atom.xml
@@ -0,0 +1,63 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ <p>Some cool stuff!</p>
+ Trop bien ! 2012-03-02T14:01:01+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html <p>Et voila du contenu en français</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ <p>This is some article, in english</p>
+ Deuxième article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:second-article-fr.html <p>Ceci est un article, en français.</p>
+ A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ <p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ <p>Article 3</p>
+ This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ <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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/all.rss.xml b/pelican/tests/output/custom_locale/feeds/all.rss.xml
new file mode 100644
index 00000000..39fbc240
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/all.rss.xml
@@ -0,0 +1,63 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ Trop bien ! http://blog.notmyidea.org/oh-yeah-fr.html<p>Et voila du contenu en français</p>
+ Alexis Métaireau Fri, 02 Mar 2012 14:01:01 +0100 tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html Second article http://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ foo bar baz Deuxième article http://blog.notmyidea.org/second-article-fr.html<p>Ceci est un article, en français.</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:second-article-fr.html foo bar baz A markdown powered article http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ Article 1 http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ Article 2 http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ Article 3 http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ This is a super article ! http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ foo bar foobar Oh yeah ! http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ oh bar yeah Unbelievable ! http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/bar.atom.xml b/pelican/tests/output/custom_locale/feeds/bar.atom.xml
new file mode 100644
index 00000000..13a5cde2
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/bar.atom.xml
@@ -0,0 +1,8 @@
+
+Alexis' log http://blog.notmyidea.org/ 2010-10-20T10:14:00+02:00 Oh yeah ! 2010-10-20T10:14:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ <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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/bar.rss.xml b/pelican/tests/output/custom_locale/feeds/bar.rss.xml
new file mode 100644
index 00000000..4426eb6a
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/bar.rss.xml
@@ -0,0 +1,8 @@
+
+Alexis' log http://blog.notmyidea.org/Wed, 20 Oct 2010 10:14:00 +0200 Oh yeah ! http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<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>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+</div>
+ Alexis Métaireau Wed, 20 Oct 2010 10:14:00 +0200 tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ oh bar yeah
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/cat1.atom.xml b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml
new file mode 100644
index 00000000..54d382c4
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml
@@ -0,0 +1,7 @@
+
+Alexis' log http://blog.notmyidea.org/ 2011-04-20T00:00:00+02:00 A markdown powered article 2011-04-20T00:00:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ <p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Article 1 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ <p>Article 1</p>
+ Article 2 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ <p>Article 2</p>
+ Article 3 2011-02-17T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ <p>Article 3</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/cat1.rss.xml b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml
new file mode 100644
index 00000000..4f3b12f5
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml
@@ -0,0 +1,7 @@
+
+Alexis' log http://blog.notmyidea.org/Wed, 20 Apr 2011 00:00:00 +0200 A markdown powered article http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p>
+<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a>
+<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p> Alexis Métaireau Wed, 20 Apr 2011 00:00:00 +0200 tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/ Article 1 http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/ Article 2 http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/ Article 3 http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p>
+ Alexis Métaireau Thu, 17 Feb 2011 00:00:00 +0100 tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/misc.atom.xml b/pelican/tests/output/custom_locale/feeds/misc.atom.xml
new file mode 100644
index 00000000..18800485
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/misc.atom.xml
@@ -0,0 +1,38 @@
+
+Alexis' log http://blog.notmyidea.org/ 2012-11-30T00:00:00+01:00 FILENAME_METADATA example 2012-11-30T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ <p>Some cool stuff!</p>
+ Second article 2012-02-29T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ <p>This is some article, in english</p>
+ Unbelievable ! 2010-10-15T20:30:00+02:00 Alexis Métaireau tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ <p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ The baz tag 2010-03-14T00:00:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-03-14:tag/baz.html <p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/misc.rss.xml b/pelican/tests/output/custom_locale/feeds/misc.rss.xml
new file mode 100644
index 00000000..0be49f79
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/misc.rss.xml
@@ -0,0 +1,38 @@
+
+Alexis' log http://blog.notmyidea.org/Fri, 30 Nov 2012 00:00:00 +0100 FILENAME_METADATA example http://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p>
+ Alexis Métaireau Fri, 30 Nov 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/ Second article http://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p>
+ Alexis Métaireau Wed, 29 Feb 2012 00:00:00 +0100 tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/ foo bar baz Unbelievable ! http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p>
+<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a>
+<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p>
+<div class="section" id="testing-sourcecode-directive">
+<h2>Testing sourcecode directive</h2>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="testing-another-case">
+<h2>Testing another case</h2>
+<p>This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.</p>
+<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+</td></tr></table><p>Lovely.</p>
+</div>
+<div class="section" id="testing-more-sourcecode-directives">
+<h2>Testing more sourcecode directives</h2>
+<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">'linenos'</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'table'</span><span class="testingp">,</span> <span class="testings">'inline'</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">'linenos'</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">'table'</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">'nowrap'</span><span class="testingp">,</span> <span class="testings">'nobackground'</span><span class="testingp">,</span> <span class="testings">'anchorlinenos'</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">'</span><span class="testingse">\n</span><span class="testings">'</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">''</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">'html'</span><span class="testingp">)]</span><br></span></pre></div>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-even-more-sourcecode-directives">
+<h2>Testing even more sourcecode directives</h2>
+<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+<p>Lovely.</p>
+</div>
+<div class="section" id="testing-overriding-config-defaults">
+<h2>Testing overriding config defaults</h2>
+<p>Even if the default is line numbers, we can override it here</p>
+<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span>
+</pre></div>
+<p>Lovely.</p>
+</div>
+ Alexis Métaireau Fri, 15 Oct 2010 20:30:00 +0200 tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/ The baz tag http://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p>
+ Alexis Métaireau Sun, 14 Mar 2010 00:00:00 +0100 tag:blog.notmyidea.org,2010-03-14:tag/baz.html
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/yeah.atom.xml b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml
new file mode 100644
index 00000000..5f7d8c4f
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml
@@ -0,0 +1,14 @@
+
+Alexis' log http://blog.notmyidea.org/ 2013-11-17T23:29:00+01:00 This is a super article ! 2013-11-17T23:29:00+01:00 Alexis Métaireau tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ <p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/feeds/yeah.rss.xml b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml
new file mode 100644
index 00000000..50c5803c
--- /dev/null
+++ b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml
@@ -0,0 +1,14 @@
+
+Alexis' log http://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100 This is a super article ! http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p>
+<div class="section" id="this-is-a-simple-title">
+<h2>This is a simple title</h2>
+<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
+<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
+<pre class="literal-block">
+>>> from ipdb import set_trace
+>>> set_trace()
+</pre>
+<p>→ And now try with some utf8 hell: ééé</p>
+</div>
+ Alexis Métaireau Sun, 17 Nov 2013 23:29:00 +0100 tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/ foo bar foobar
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html
new file mode 100644
index 00000000..d64bea7d
--- /dev/null
+++ b/pelican/tests/output/custom_locale/index.html
@@ -0,0 +1,173 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 30 novembre 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html
new file mode 100644
index 00000000..3b3d2d52
--- /dev/null
+++ b/pelican/tests/output/custom_locale/index2.html
@@ -0,0 +1,187 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 2 / 3
+ »
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html
new file mode 100644
index 00000000..96d70261
--- /dev/null
+++ b/pelican/tests/output/custom_locale/index3.html
@@ -0,0 +1,138 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 15 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it ...
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 14 mars 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
read more
+
There are comments .
+
+
+
+ «
+ Page 3 / 3
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html
new file mode 100644
index 00000000..0eafa913
--- /dev/null
+++ b/pelican/tests/output/custom_locale/jinja2_template.html
@@ -0,0 +1,77 @@
+
+
+
+
+ Alexis' log
+
+
+
+
+
+
+
+
+
+
+
+
+
+Some text
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html
new file mode 100644
index 00000000..77d9e2c5
--- /dev/null
+++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Trop bien !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 02 mars 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+Translations:
+ en
+
+ Et voila du contenu en français
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/override/index.html b/pelican/tests/output/custom_locale/override/index.html
new file mode 100644
index 00000000..e84d79fe
--- /dev/null
+++ b/pelican/tests/output/custom_locale/override/index.html
@@ -0,0 +1,81 @@
+
+
+
+
+ Override url/save_as
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Override url/save_as
+
+ Test page which overrides save_as and url so that this page will be generated
+at a custom location.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html
new file mode 100644
index 00000000..dced8107
--- /dev/null
+++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html
@@ -0,0 +1,81 @@
+
+
+
+
+ This is a test hidden page
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a test hidden page
+
+ This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html
new file mode 100644
index 00000000..46ea4fef
--- /dev/null
+++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html
@@ -0,0 +1,81 @@
+
+
+
+
+ This is a test page
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a test page
+
+ Just an image.
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg b/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg
new file mode 100644
index 00000000..d8a96d35
Binary files /dev/null and b/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg differ
diff --git a/pelican/tests/output/custom_locale/pictures/Sushi.jpg b/pelican/tests/output/custom_locale/pictures/Sushi.jpg
new file mode 100644
index 00000000..e49e5f0a
Binary files /dev/null and b/pelican/tests/output/custom_locale/pictures/Sushi.jpg differ
diff --git a/pelican/tests/output/custom_locale/pictures/Sushi_Macro.jpg b/pelican/tests/output/custom_locale/pictures/Sushi_Macro.jpg
new file mode 100644
index 00000000..21f935a1
Binary files /dev/null and b/pelican/tests/output/custom_locale/pictures/Sushi_Macro.jpg differ
diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html
new file mode 100644
index 00000000..b2ffdf9c
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html
@@ -0,0 +1,129 @@
+
+
+
+
+ This is a super article !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html
new file mode 100644
index 00000000..d7449ebf
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html
@@ -0,0 +1,146 @@
+
+
+
+
+ Unbelievable !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 15 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Or completely awesome. Depends the needs.
+
a root-relative link to markdown-article
+a file-relative link to markdown-article
+
+
Testing sourcecode directive
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
+
+
Testing another case
+
This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
Testing more sourcecode directives
+
8 def run ( self ): self . assert_has_content () 10 try : lexer = get_lexer_by_name ( self . arguments [ 0 ]) 12 except ValueError : # no lexer found - use the text one instead of an exception 14 lexer = TextLexer () 16 if ( 'linenos' in self . options and self . options [ 'linenos' ] not in ( 'table' , 'inline' )): 18 self . options [ 'linenos' ] = 'table' 20 for flag in ( 'nowrap' , 'nobackground' , 'anchorlinenos' ): if flag in self . options : 22 self . options [ flag ] = True 24 # noclasses should already default to False, but just in case... formatter = HtmlFormatter ( noclasses = False , ** self . options ) 26 parsed = highlight ( ' \n ' . join ( self . content ), lexer , formatter ) return [ nodes . raw ( '' , parsed , format = 'html' )]
+
Lovely.
+
+
+
Testing even more sourcecode directives
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
Lovely.
+
+
+
Testing overriding config defaults
+
Even if the default is line numbers, we can override it here
+
formatter = self . options and VARIANTS [ self . options . keys ()[ 0 ]]
+
+
Lovely.
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html
new file mode 100644
index 00000000..12c67404
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html
@@ -0,0 +1,121 @@
+
+
+
+
+ Oh yeah !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html
new file mode 100644
index 00000000..81bb6fa6
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html
@@ -0,0 +1,115 @@
+
+
+
+
+ A markdown powered article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html
new file mode 100644
index 00000000..8aa9de4a
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 1
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html
new file mode 100644
index 00000000..1d78ade5
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 2
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html
new file mode 100644
index 00000000..372090cc
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Article 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 17 février 2011
+
+
+
+ By Alexis Métaireau
+
+In cat1 .
+
+ Article 3
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html
new file mode 100644
index 00000000..812fdfd0
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Second article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html
new file mode 100644
index 00000000..de426279
--- /dev/null
+++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html
@@ -0,0 +1,114 @@
+
+
+
+
+ FILENAME_METADATA example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 30 novembre 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ Some cool stuff!
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/robots.txt b/pelican/tests/output/custom_locale/robots.txt
new file mode 100644
index 00000000..19a6e299
--- /dev/null
+++ b/pelican/tests/output/custom_locale/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /pictures
diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html
new file mode 100644
index 00000000..07a2a09c
--- /dev/null
+++ b/pelican/tests/output/custom_locale/second-article-fr.html
@@ -0,0 +1,116 @@
+
+
+
+
+ Deuxième article
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ en
+
+ Ceci est un article, en français.
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html
new file mode 100644
index 00000000..911f53af
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/bar.html
@@ -0,0 +1,160 @@
+
+
+
+
+ Alexis' log - bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html
new file mode 100644
index 00000000..1cea44d1
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/baz.html
@@ -0,0 +1,114 @@
+
+
+
+
+ The baz tag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 14 mars 2010
+
+
+
+ By Alexis Métaireau
+
+In misc .
+
+ This article overrides the listening of the articles under the baz tag.
+
+
+
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html
new file mode 100644
index 00000000..01bb8b17
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/foo.html
@@ -0,0 +1,130 @@
+
+
+
+
+ Alexis' log - foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 29 février 2012
+
+
+
+ By Alexis Métaireau
+
+In misc .
+tags: foo bar baz
Translations:
+ fr
+
+ This is some article, in english
+There are comments .
+
+
+ Other articles
+
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Multi-line metadata should be supported
+as well as inline markup .
+
+
read more
+
There are comments .
+
+
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html
new file mode 100644
index 00000000..85b2b0ed
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/foobar.html
@@ -0,0 +1,109 @@
+
+
+
+
+ Alexis' log - foobar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 02 décembre 2010
+
+
+
+ Updated: 17 novembre 2013
+
+
+
+ By Alexis Métaireau
+
+In yeah .
+tags: foo bar foobar
+ Some content here !
+
+
This is a simple title
+
And here comes the cool stuff .
+
+
+
+>>> from ipdb import set_trace
+>>> set_trace()
+
+
→ And now try with some utf8 hell: ééé
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
diff --git a/pelican/tests/output/custom_locale/tag/oh.html b/pelican/tests/output/custom_locale/tag/oh.html
new file mode 100644
index 00000000..21c8e352
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/oh.html
@@ -0,0 +1,80 @@
+
+
+
+
+ Oh Oh Oh
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Oh Oh Oh
+
+ This page overrides the listening of the articles under the oh tag.
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html
new file mode 100644
index 00000000..45789e86
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tag/yeah.html
@@ -0,0 +1,101 @@
+
+
+
+
+ Alexis' log - yeah
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Published: 20 octobre 2010
+
+
+
+ By Alexis Métaireau
+
+In bar .
+tags: oh bar yeah
Translations:
+ fr
+
+
+
Why not ?
+
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+
+There are comments .
+
+ Page 1 / 1
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/tags.html b/pelican/tests/output/custom_locale/tags.html
new file mode 100644
index 00000000..0da0d291
--- /dev/null
+++ b/pelican/tests/output/custom_locale/tags.html
@@ -0,0 +1,87 @@
+
+
+
+
+ Alexis' log - Tags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tags for Alexis' log
+
+
+
+
+
+
+
+ Proudly powered by Pelican , which takes great advantage of Python .
+
+
+ The theme is by Smashing Magazine , thanks!
+
+
+
+
+
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/theme/css/main.css b/pelican/tests/output/custom_locale/theme/css/main.css
new file mode 100644
index 00000000..2efb518d
--- /dev/null
+++ b/pelican/tests/output/custom_locale/theme/css/main.css
@@ -0,0 +1,451 @@
+/*
+ Name: Smashing HTML5
+ Date: July 2009
+ Description: Sample layout for HTML5 and CSS3 goodness.
+ Version: 1.0
+ License: MIT
+ Licensed by: Smashing Media GmbH
+ Original author: Enrique Ramírez
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("pygment.css");
+@import url("typogrify.css");
+@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
+
+/***** Global *****/
+/* Body */
+body {
+ background: #F5F4EF;
+ color: #000305;
+ font-size: 87.5%; /* Base font size: 14px */
+ font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+ line-height: 1.429;
+ margin: 0;
+ padding: 0;
+ text-align: left;
+}
+
+/* Headings */
+h1 {font-size: 2em }
+h2 {font-size: 1.571em} /* 22px */
+h3 {font-size: 1.429em} /* 20px */
+h4 {font-size: 1.286em} /* 18px */
+h5 {font-size: 1.143em} /* 16px */
+h6 {font-size: 1em} /* 14px */
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 400;
+ line-height: 1.1;
+ margin-bottom: .8em;
+ font-family: 'Yanone Kaffeesatz', arial, serif;
+}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+
+hr { border: 2px solid #EEEEEE; }
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+ color: #C74350;
+ padding: 0 1px;
+ text-decoration: underline;
+}
+a:hover, a:active {
+ background-color: #C74350;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: 1px 1px 1px #333;
+}
+
+h1 a:hover {
+ background-color: inherit
+}
+
+/* Paragraphs */
+div.line-block,
+p { margin-top: 1em;
+ margin-bottom: 1em;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+/* Lists */
+ul {
+ list-style: outside disc;
+ margin: 0em 0 0 1.5em;
+}
+
+ol {
+ list-style: outside decimal;
+ margin: 0em 0 0 1.5em;
+}
+
+li { margin-top: 0.5em;}
+
+.post-info {
+ float:right;
+ margin:10px;
+ padding:5px;
+}
+
+.post-info p{
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+.readmore { float: right }
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
+
+/* Quotes */
+blockquote {
+ margin: 20px;
+ font-style: italic;
+}
+cite {}
+
+q {}
+
+div.note {
+ float: right;
+ margin: 5px;
+ font-size: 85%;
+ max-width: 300px;
+}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+
+ /* Thead */
+ thead th {padding: .5em .4em; text-align: left;}
+ thead td {}
+
+ /* Tbody */
+ tbody td {padding: .5em .4em;}
+ tbody th {}
+
+ tbody .alt td {}
+ tbody .alt th {}
+
+ /* Tfoot */
+ tfoot th {}
+ tfoot td {}
+
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+ display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right, figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: left; margin: 0 2em 2em 0;}
+
+/*
+ Header
+*****************/
+#banner {
+ margin: 0 auto;
+ padding: 2.5em 0 0 0;
+}
+
+ /* Banner */
+ #banner h1 {font-size: 3.571em; line-height: 0;}
+ #banner h1 a:link, #banner h1 a:visited {
+ color: #000305;
+ display: block;
+ font-weight: bold;
+ margin: 0 0 .6em .2em;
+ text-decoration: none;
+ }
+ #banner h1 a:hover, #banner h1 a:active {
+ background: none;
+ color: #C74350;
+ text-shadow: none;
+ }
+
+ #banner h1 strong {font-size: 0.36em; font-weight: normal;}
+
+ /* Main Nav */
+ #banner nav {
+ background: #000305;
+ font-size: 1.143em;
+ height: 40px;
+ line-height: 30px;
+ margin: 0 auto 2em auto;
+ padding: 0;
+ text-align: center;
+ width: 800px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+ #banner nav li {float: left; display: inline; margin: 0;}
+
+ #banner nav a:link, #banner nav a:visited {
+ color: #fff;
+ display: inline-block;
+ height: 30px;
+ padding: 5px 1.5em;
+ text-decoration: none;
+ }
+ #banner nav a:hover, #banner nav a:active,
+ #banner nav .active a:link, #banner nav .active a:visited {
+ background: #C74451;
+ color: #fff;
+ text-shadow: none !important;
+ }
+
+ #banner nav li:first-child a {
+ border-top-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -webkit-border-top-left-radius: 5px;
+
+ border-bottom-left-radius: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ }
+
+/*
+ Featured
+*****************/
+#featured {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#featured figure {
+ border: 2px solid #eee;
+ float: right;
+ margin: 0.786em 2em 0 5em;
+ width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+ Body
+*****************/
+#content {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+/*
+ Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+ color: #C74350;
+ font-size: 1.429em;
+ margin-bottom: .25em;
+ padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+ color: #444;
+ display: block;
+ border-bottom: 1px solid #F4E3E3;
+ text-decoration: none;
+ padding: .3em .25em;
+}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+ /* Blogroll */
+ #extras .blogroll {
+ float: left;
+ width: 615px;
+ }
+
+ #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+
+ /* Social */
+ #extras .social {
+ float: right;
+ width: 175px;
+ }
+
+ #extras div[class='social'] a {
+ background-repeat: no-repeat;
+ background-position: 3px 6px;
+ padding-left: 25px;
+ }
+
+ /* Icons */
+ .social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
+ .social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');}
+ .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+ .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+ .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+ .social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
+ .social a[href*='github.com'],
+ .social a[href*='git.io'] {
+ background-image: url('../images/icons/github.png');
+ background-size: 16px 16px;
+ }
+ .social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
+ .social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');}
+ .social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');}
+ .social a[href*='news.ycombinator.com'],
+ .social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');}
+ .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+ .social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
+ .social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');}
+ .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
+ .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');}
+ .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');}
+ .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');}
+ .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+ .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');}
+ .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');}
+
+/*
+ About
+*****************/
+#about {
+ background: #fff;
+ font-style: normal;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ text-align: left;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+ Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+ display: block;
+ clear: both;
+ border-bottom: 1px solid #eee;
+ padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+.hentry img{display : none ;}
+.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
+.entry-title a:visited {background-color: #fff;}
+
+.hentry .post-info * {font-style: normal;}
+
+ /* Content */
+ .hentry footer {margin-bottom: 2em;}
+ .hentry footer address {display: inline;}
+ #posts-list footer address {display: block;}
+
+ /* Blog Index */
+ #posts-list {list-style: none; margin: 0;}
+ #posts-list .hentry {padding-left: 10px; position: relative;}
+
+ #posts-list footer {
+ left: 10px;
+ position: relative;
+ float: left;
+ top: 0.5em;
+ width: 190px;
+ }
+
+ /* About the Author */
+ #about-author {
+ background: #f9f9f9;
+ clear: both;
+ font-style: normal;
+ margin: 2em 0;
+ padding: 10px 20px 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #about-author strong {
+ color: #C64350;
+ clear: both;
+ display: block;
+ font-size: 1.429em;
+ }
+
+ #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+
+ /* Comments */
+ #comments-list {list-style: none; margin: 0 1em;}
+ #comments-list blockquote {
+ background: #f8f8f8;
+ clear: both;
+ font-style: normal;
+ margin: 0;
+ padding: 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+ #comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+
+ #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+
+ /* Add a Comment */
+ #add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+ #add-comment input[type='text'],
+ #add-comment input[type='email'],
+ #add-comment input[type='url'] {float: left; width: 200px;}
+
+ #add-comment textarea {float: left; height: 150px; width: 495px;}
+
+ #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+
+ #add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+ #add-comment * {margin-bottom: .5em;}
diff --git a/pelican/tests/output/custom_locale/theme/css/pygment.css b/pelican/tests/output/custom_locale/theme/css/pygment.css
new file mode 100644
index 00000000..fdd056f6
--- /dev/null
+++ b/pelican/tests/output/custom_locale/theme/css/pygment.css
@@ -0,0 +1,205 @@
+.hll {
+background-color:#eee;
+}
+.c {
+color:#408090;
+font-style:italic;
+}
+.err {
+border:1px solid #FF0000;
+}
+.k {
+color:#007020;
+font-weight:bold;
+}
+.o {
+color:#666666;
+}
+.cm {
+color:#408090;
+font-style:italic;
+}
+.cp {
+color:#007020;
+}
+.c1 {
+color:#408090;
+font-style:italic;
+}
+.cs {
+background-color:#FFF0F0;
+color:#408090;
+}
+.gd {
+color:#A00000;
+}
+.ge {
+font-style:italic;
+}
+.gr {
+color:#FF0000;
+}
+.gh {
+color:#000080;
+font-weight:bold;
+}
+.gi {
+color:#00A000;
+}
+.go {
+color:#303030;
+}
+.gp {
+color:#C65D09;
+font-weight:bold;
+}
+.gs {
+font-weight:bold;
+}
+.gu {
+color:#800080;
+font-weight:bold;
+}
+.gt {
+color:#0040D0;
+}
+.kc {
+color:#007020;
+font-weight:bold;
+}
+.kd {
+color:#007020;
+font-weight:bold;
+}
+.kn {
+color:#007020;
+font-weight:bold;
+}
+.kp {
+color:#007020;
+}
+.kr {
+color:#007020;
+font-weight:bold;
+}
+.kt {
+color:#902000;
+}
+.m {
+color:#208050;
+}
+.s {
+color:#4070A0;
+}
+.na {
+color:#4070A0;
+}
+.nb {
+color:#007020;
+}
+.nc {
+color:#0E84B5;
+font-weight:bold;
+}
+.no {
+color:#60ADD5;
+}
+.nd {
+color:#555555;
+font-weight:bold;
+}
+.ni {
+color:#D55537;
+font-weight:bold;
+}
+.ne {
+color:#007020;
+}
+.nf {
+color:#06287E;
+}
+.nl {
+color:#002070;
+font-weight:bold;
+}
+.nn {
+color:#0E84B5;
+font-weight:bold;
+}
+.nt {
+color:#062873;
+font-weight:bold;
+}
+.nv {
+color:#BB60D5;
+}
+.ow {
+color:#007020;
+font-weight:bold;
+}
+.w {
+color:#BBBBBB;
+}
+.mf {
+color:#208050;
+}
+.mh {
+color:#208050;
+}
+.mi {
+color:#208050;
+}
+.mo {
+color:#208050;
+}
+.sb {
+color:#4070A0;
+}
+.sc {
+color:#4070A0;
+}
+.sd {
+color:#4070A0;
+font-style:italic;
+}
+.s2 {
+color:#4070A0;
+}
+.se {
+color:#4070A0;
+font-weight:bold;
+}
+.sh {
+color:#4070A0;
+}
+.si {
+color:#70A0D0;
+font-style:italic;
+}
+.sx {
+color:#C65D09;
+}
+.sr {
+color:#235388;
+}
+.s1 {
+color:#4070A0;
+}
+.ss {
+color:#517918;
+}
+.bp {
+color:#007020;
+}
+.vc {
+color:#BB60D5;
+}
+.vg {
+color:#BB60D5;
+}
+.vi {
+color:#BB60D5;
+}
+.il {
+color:#208050;
+}
diff --git a/pelican/tests/output/custom_locale/theme/css/reset.css b/pelican/tests/output/custom_locale/theme/css/reset.css
new file mode 100644
index 00000000..1e217566
--- /dev/null
+++ b/pelican/tests/output/custom_locale/theme/css/reset.css
@@ -0,0 +1,52 @@
+/*
+ Name: Reset Stylesheet
+ Description: Resets browser's default CSS
+ Author: Eric Meyer
+ Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
\ No newline at end of file
diff --git a/pelican/tests/output/custom_locale/theme/css/typogrify.css b/pelican/tests/output/custom_locale/theme/css/typogrify.css
new file mode 100644
index 00000000..c9b34dc8
--- /dev/null
+++ b/pelican/tests/output/custom_locale/theme/css/typogrify.css
@@ -0,0 +1,3 @@
+.caps {font-size:.92em;}
+.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;}
+.dquo {margin-left:-.38em;}
diff --git a/pelican/tests/output/custom_locale/theme/css/wide.css b/pelican/tests/output/custom_locale/theme/css/wide.css
new file mode 100644
index 00000000..88fd59ce
--- /dev/null
+++ b/pelican/tests/output/custom_locale/theme/css/wide.css
@@ -0,0 +1,48 @@
+@import url("main.css");
+
+body {
+ font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
+}
+
+.post-info{
+ display: none;
+}
+
+#banner nav {
+ display: none;
+ -moz-border-radius: 0px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ font-size: 1em;
+ background: #F5F4EF;
+}
+
+#banner nav ul{
+ padding-right: 50px;
+}
+
+#banner nav li{
+ float: right;
+ color: #000;
+}
+
+#banner nav li a {
+ color: #000;
+}
+
+#banner h1 {
+ margin-bottom: -18px;
+}
+
+#featured, #extras {
+ padding: 50px;
+}
+
+#featured {
+ padding-top: 20px;
+}
+
+#extras {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png b/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png
new file mode 100644
index 00000000..9609df3b
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png b/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png
new file mode 100644
index 00000000..d05ba161
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/delicious.png b/pelican/tests/output/custom_locale/theme/images/icons/delicious.png
new file mode 100644
index 00000000..3dccdd84
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/delicious.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/facebook.png b/pelican/tests/output/custom_locale/theme/images/icons/facebook.png
new file mode 100644
index 00000000..74e7ad52
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/facebook.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/github.png b/pelican/tests/output/custom_locale/theme/images/icons/github.png
new file mode 100644
index 00000000..8b25551a
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/github.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png b/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png
new file mode 100644
index 00000000..3eeb3ece
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/gittip.png b/pelican/tests/output/custom_locale/theme/images/icons/gittip.png
new file mode 100644
index 00000000..af949625
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/gittip.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png b/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png
new file mode 100644
index 00000000..5de15e68
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png b/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png
new file mode 100644
index 00000000..3c6b7432
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png b/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png
new file mode 100644
index 00000000..fc7a82d4
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png b/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png
new file mode 100644
index 00000000..3a6c6262
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png b/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png
new file mode 100644
index 00000000..d29c1201
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/reddit.png b/pelican/tests/output/custom_locale/theme/images/icons/reddit.png
new file mode 100644
index 00000000..71ae1215
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/reddit.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/rss.png b/pelican/tests/output/custom_locale/theme/images/icons/rss.png
new file mode 100644
index 00000000..7862c65a
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/rss.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png b/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png
new file mode 100644
index 00000000..ecc97410
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png b/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png
new file mode 100644
index 00000000..087d0931
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png b/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png
new file mode 100644
index 00000000..f5b65e99
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/twitter.png b/pelican/tests/output/custom_locale/theme/images/icons/twitter.png
new file mode 100644
index 00000000..d0ef3cc1
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/twitter.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png b/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png
new file mode 100644
index 00000000..dba47202
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png differ
diff --git a/pelican/tests/output/custom_locale/theme/images/icons/youtube.png b/pelican/tests/output/custom_locale/theme/images/icons/youtube.png
new file mode 100644
index 00000000..ce6cbe4f
Binary files /dev/null and b/pelican/tests/output/custom_locale/theme/images/icons/youtube.png differ
diff --git a/pelican/tests/support.py b/pelican/tests/support.py
new file mode 100644
index 00000000..a4fd2c35
--- /dev/null
+++ b/pelican/tests/support.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+__all__ = ['get_article', 'unittest', ]
+
+import os
+import re
+import subprocess
+import sys
+from six import StringIO
+import logging
+from logging.handlers import BufferingHandler
+import unittest
+import locale
+
+from functools import wraps
+from contextlib import contextmanager
+from tempfile import mkdtemp
+from shutil import rmtree
+
+from pelican.contents import Article
+from pelican.settings import DEFAULT_CONFIG
+
+
+@contextmanager
+def temporary_folder():
+ """creates a temporary folder, return it and delete it afterwards.
+
+ This allows to do something like this in tests:
+
+ >>> with temporary_folder() as d:
+ # do whatever you want
+ """
+ tempdir = mkdtemp()
+ try:
+ yield tempdir
+ finally:
+ rmtree(tempdir)
+
+
+def isplit(s, sep=None):
+ """Behaves like str.split but returns a generator instead of a list.
+
+ >>> list(isplit('\tUse the force\n')) == '\tUse the force\n'.split()
+ True
+ >>> list(isplit('\tUse the force\n')) == ['Use', 'the', 'force']
+ True
+ >>> (list(isplit('\tUse the force\n', "e"))
+ == '\tUse the force\n'.split("e"))
+ True
+ >>> list(isplit('Use the force', "e")) == 'Use the force'.split("e")
+ True
+ >>> list(isplit('Use the force', "e")) == ['Us', ' th', ' forc', '']
+ True
+
+ """
+ sep, hardsep = r'\s+' if sep is None else re.escape(sep), sep is not None
+ exp, pos, l = re.compile(sep), 0, len(s)
+ while True:
+ m = exp.search(s, pos)
+ if not m:
+ if pos < l or hardsep:
+ # ^ mimic "split()": ''.split() returns []
+ yield s[pos:]
+ break
+ start = m.start()
+ if pos < start or hardsep:
+ # ^ mimic "split()": includes trailing empty string
+ yield s[pos:start]
+ pos = m.end()
+
+
+def mute(returns_output=False):
+ """Decorate a function that prints to stdout, intercepting the output.
+ If "returns_output" is True, the function will return a generator
+ yielding the printed lines instead of the return values.
+
+ The decorator litterally hijack sys.stdout during each function
+ execution, so be careful with what you apply it to.
+
+ >>> def numbers():
+ print "42"
+ print "1984"
+ ...
+ >>> numbers()
+ 42
+ 1984
+ >>> mute()(numbers)()
+ >>> list(mute(True)(numbers)())
+ ['42', '1984']
+
+ """
+
+ def decorator(func):
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+
+ saved_stdout = sys.stdout
+ sys.stdout = StringIO()
+
+ try:
+ out = func(*args, **kwargs)
+ if returns_output:
+ out = isplit(sys.stdout.getvalue().strip())
+ finally:
+ sys.stdout = saved_stdout
+
+ return out
+
+ return wrapper
+
+ return decorator
+
+
+def get_article(title, slug, content, lang, extra_metadata=None):
+ metadata = {'slug': slug, 'title': title, 'lang': lang}
+ if extra_metadata is not None:
+ metadata.update(extra_metadata)
+ return Article(content, metadata=metadata)
+
+
+def skipIfNoExecutable(executable):
+ """Skip test if `executable` is not found
+
+ Tries to run `executable` with subprocess to make sure it's in the path,
+ and skips the tests if not found (if subprocess raises a `OSError`).
+ """
+
+ with open(os.devnull, 'w') as fnull:
+ try:
+ res = subprocess.call(executable, stdout=fnull, stderr=fnull)
+ except OSError:
+ res = None
+
+ if res is None:
+ return unittest.skip('{0} executable not found'.format(executable))
+
+ return lambda func: func
+
+
+def module_exists(module_name):
+ """Test if a module is importable."""
+
+ try:
+ __import__(module_name)
+ except ImportError:
+ return False
+ else:
+ return True
+
+
+def locale_available(locale_):
+ old_locale = locale.setlocale(locale.LC_TIME)
+
+ try:
+ locale.setlocale(locale.LC_TIME, str(locale_))
+ except locale.Error:
+ return False
+ else:
+ locale.setlocale(locale.LC_TIME, old_locale)
+ return True
+
+
+def get_settings(**kwargs):
+ """Provide tweaked setting dictionaries for testing
+
+ Set keyword arguments to override specific settings.
+ """
+ settings = DEFAULT_CONFIG.copy()
+ for key,value in kwargs.items():
+ settings[key] = value
+ return settings
+
+
+class LogCountHandler(BufferingHandler):
+ """Capturing and counting logged messages."""
+
+ def __init__(self, capacity=1000):
+ logging.handlers.BufferingHandler.__init__(self, capacity)
+
+ def count_logs(self, msg=None, level=None):
+ return len([l for l in self.buffer
+ if (msg is None or re.match(msg, l.getMessage()))
+ and (level is None or l.levelno == level)
+ ])
+
+
+class LoggedTestCase(unittest.TestCase):
+ """A test case that captures log messages."""
+
+ def setUp(self):
+ super(LoggedTestCase, self).setUp()
+ self._logcount_handler = LogCountHandler()
+ logging.getLogger().addHandler(self._logcount_handler)
+
+ def tearDown(self):
+ logging.getLogger().removeHandler(self._logcount_handler)
+ super(LoggedTestCase, self).tearDown()
+
+ def assertLogCountEqual(self, count=None, msg=None, **kwargs):
+ actual = self._logcount_handler.count_logs(msg=msg, **kwargs)
+ self.assertEqual(
+ actual, count,
+ msg='expected {} occurrences of {!r}, but found {}'.format(
+ count, msg, actual))
diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py
new file mode 100644
index 00000000..e64b3804
--- /dev/null
+++ b/pelican/tests/test_contents.py
@@ -0,0 +1,428 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, absolute_import
+
+import six
+from sys import platform
+import locale
+
+from pelican.tests.support import unittest, get_settings
+
+from pelican.contents import Page, Article, URLWrapper
+from pelican.settings import DEFAULT_CONFIG
+from pelican.utils import truncate_html_words, SafeDatetime
+from pelican.signals import content_object_init
+from jinja2.utils import generate_lorem_ipsum
+
+# generate one paragraph, enclosed with
+TEST_CONTENT = str(generate_lorem_ipsum(n=1))
+TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False)
+
+
+class TestPage(unittest.TestCase):
+
+ def setUp(self):
+ super(TestPage, self).setUp()
+ self.old_locale = locale.setlocale(locale.LC_ALL)
+ locale.setlocale(locale.LC_ALL, str('C'))
+ self.page_kwargs = {
+ 'content': TEST_CONTENT,
+ 'context': {
+ 'localsiteurl': '',
+ },
+ 'metadata': {
+ 'summary': TEST_SUMMARY,
+ 'title': 'foo bar',
+ 'author': 'Blogger',
+ },
+ 'source_path': '/path/to/file/foo.ext'
+ }
+
+ def tearDown(self):
+ locale.setlocale(locale.LC_ALL, self.old_locale)
+
+ def test_use_args(self):
+ # Creating a page with arguments passed to the constructor should use
+ # them to initialise object's attributes.
+ metadata = {'foo': 'bar', 'foobar': 'baz', 'title': 'foobar', }
+ page = Page(TEST_CONTENT, metadata=metadata,
+ context={'localsiteurl': ''})
+ for key, value in metadata.items():
+ self.assertTrue(hasattr(page, key))
+ self.assertEqual(value, getattr(page, key))
+ self.assertEqual(page.content, TEST_CONTENT)
+
+ def test_mandatory_properties(self):
+ # If the title is not set, must throw an exception.
+ page = Page('content')
+ with self.assertRaises(NameError):
+ page.check_properties()
+
+ page = Page('content', metadata={'title': 'foobar'})
+ page.check_properties()
+
+ def test_summary_from_metadata(self):
+ # If a :summary: metadata is given, it should be used
+ page = Page(**self.page_kwargs)
+ self.assertEqual(page.summary, TEST_SUMMARY)
+
+ def test_summary_max_length(self):
+ # If a :SUMMARY_MAX_LENGTH: is set, and there is no other summary,
+ # generated summary should not exceed the given length.
+ page_kwargs = self._copy_page_kwargs()
+ settings = get_settings()
+ page_kwargs['settings'] = settings
+ del page_kwargs['metadata']['summary']
+ settings['SUMMARY_MAX_LENGTH'] = None
+ page = Page(**page_kwargs)
+ self.assertEqual(page.summary, TEST_CONTENT)
+ settings['SUMMARY_MAX_LENGTH'] = 10
+ page = Page(**page_kwargs)
+ self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10))
+ settings['SUMMARY_MAX_LENGTH'] = 0
+ page = Page(**page_kwargs)
+ self.assertEqual(page.summary, '')
+
+ def test_slug(self):
+ page_kwargs = self._copy_page_kwargs()
+ settings = get_settings()
+ page_kwargs['settings'] = settings
+ settings['SLUGIFY_SOURCE'] = "title"
+ page = Page(**page_kwargs)
+ self.assertEqual(page.slug, 'foo-bar')
+ settings['SLUGIFY_SOURCE'] = "basename"
+ page = Page(**page_kwargs)
+ self.assertEqual(page.slug, 'foo')
+
+ def test_defaultlang(self):
+ # If no lang is given, default to the default one.
+ page = Page(**self.page_kwargs)
+ self.assertEqual(page.lang, DEFAULT_CONFIG['DEFAULT_LANG'])
+
+ # it is possible to specify the lang in the metadata infos
+ self.page_kwargs['metadata'].update({'lang': 'fr', })
+ page = Page(**self.page_kwargs)
+ self.assertEqual(page.lang, 'fr')
+
+ def test_save_as(self):
+ # If a lang is not the default lang, save_as should be set
+ # accordingly.
+
+ # if a title is defined, save_as should be set
+ page = Page(**self.page_kwargs)
+ self.assertEqual(page.save_as, "pages/foo-bar.html")
+
+ # if a language is defined, save_as should include it accordingly
+ self.page_kwargs['metadata'].update({'lang': 'fr', })
+ page = Page(**self.page_kwargs)
+ self.assertEqual(page.save_as, "pages/foo-bar-fr.html")
+
+ def test_metadata_url_format(self):
+ # Arbitrary metadata should be passed through url_format()
+ page = Page(**self.page_kwargs)
+ self.assertIn('summary', page.url_format.keys())
+ page.metadata['directory'] = 'test-dir'
+ page.settings = get_settings(PAGE_SAVE_AS='{directory}/{slug}')
+ self.assertEqual(page.save_as, 'test-dir/foo-bar')
+
+ def test_datetime(self):
+ # If DATETIME is set to a tuple, it should be used to override LOCALE
+ dt = SafeDatetime(2015, 9, 13)
+
+ page_kwargs = self._copy_page_kwargs()
+
+ # set its date to dt
+ page_kwargs['metadata']['date'] = dt
+ page = Page(**page_kwargs)
+
+ # page.locale_date is a unicode string in both python2 and python3
+ dt_date = dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])
+ # dt_date is a byte string in python2, and a unicode string in python3
+ # Let's make sure it is a unicode string (relies on python 3.3 supporting the u prefix)
+ if type(dt_date) != type(u''):
+ # python2:
+ dt_date = unicode(dt_date, 'utf8')
+
+ self.assertEqual(page.locale_date, dt_date )
+ page_kwargs['settings'] = get_settings()
+
+ # I doubt this can work on all platforms ...
+ if platform == "win32":
+ locale = 'jpn'
+ else:
+ locale = 'ja_JP.utf8'
+ page_kwargs['settings']['DATE_FORMATS'] = {'jp': (locale,
+ '%Y-%m-%d(%a)')}
+ page_kwargs['metadata']['lang'] = 'jp'
+
+ import locale as locale_module
+ try:
+ page = Page(**page_kwargs)
+ self.assertEqual(page.locale_date, '2015-09-13(\u65e5)')
+ except locale_module.Error:
+ # The constructor of ``Page`` will try to set the locale to
+ # ``ja_JP.utf8``. But this attempt will failed when there is no
+ # such locale in the system. You can see which locales there are
+ # in your system with ``locale -a`` command.
+ #
+ # Until we find some other method to test this functionality, we
+ # will simply skip this test.
+ unittest.skip("There is no locale %s in this system." % locale)
+
+ def test_template(self):
+ # Pages default to page, metadata overwrites
+ default_page = Page(**self.page_kwargs)
+ self.assertEqual('page', default_page.template)
+ page_kwargs = self._copy_page_kwargs()
+ page_kwargs['metadata']['template'] = 'custom'
+ custom_page = Page(**page_kwargs)
+ self.assertEqual('custom', custom_page.template)
+
+ def _copy_page_kwargs(self):
+ # make a deep copy of page_kwargs
+ page_kwargs = dict([(key, self.page_kwargs[key]) for key in
+ self.page_kwargs])
+ for key in page_kwargs:
+ if not isinstance(page_kwargs[key], dict):
+ break
+ page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey])
+ for subkey in page_kwargs[key]])
+
+ return page_kwargs
+
+ def test_signal(self):
+ # If a title is given, it should be used to generate the slug.
+
+ def receiver_test_function(sender, instance):
+ pass
+
+ content_object_init.connect(receiver_test_function, sender=Page)
+ Page(**self.page_kwargs)
+ self.assertTrue(content_object_init.has_receivers_for(Page))
+
+ def test_get_content(self):
+ # Test that the content is updated with the relative links to
+ # filenames, tags and categories.
+ settings = get_settings()
+ args = self.page_kwargs.copy()
+ args['settings'] = settings
+
+ # Tag
+ args['content'] = ('A simple test, with a '
+ 'link ')
+ page = Page(**args)
+ content = page.get_content('http://notmyidea.org')
+ self.assertEqual(content, ('A simple test, with a '
+ 'link '))
+
+ # Category
+ args['content'] = ('A simple test, with a '
+ 'link ')
+ page = Page(**args)
+ content = page.get_content('http://notmyidea.org')
+ self.assertEqual(content,
+ ('A simple test, with a '
+ 'link '))
+
+ def test_intrasite_link(self):
+ # type does not take unicode in PY2 and bytes in PY3, which in
+ # combination with unicode literals leads to following insane line:
+ cls_name = '_DummyArticle' if six.PY3 else b'_DummyArticle'
+ article = type(cls_name, (object,), {'url': 'article.html'})
+
+ args = self.page_kwargs.copy()
+ args['settings'] = get_settings()
+ args['source_path'] = 'content'
+ args['context']['filenames'] = {'article.rst': article}
+
+ # Classic intrasite link via filename
+ args['content'] = (
+ 'A simple test, with a '
+ 'link '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'A simple test, with a '
+ 'link '
+ )
+
+ # fragment
+ args['content'] = (
+ 'A simple test, with a '
+ 'link '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'A simple test, with a '
+ 'link '
+ )
+
+ # query
+ args['content'] = (
+ 'A simple test, with a '
+ 'link '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'A simple test, with a '
+ 'link '
+ )
+
+ # combination
+ args['content'] = (
+ 'A simple test, with a '
+ 'link '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'A simple test, with a '
+ 'link '
+ )
+
+ def test_intrasite_link_more(self):
+ # type does not take unicode in PY2 and bytes in PY3, which in
+ # combination with unicode literals leads to following insane line:
+ cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset'
+
+ args = self.page_kwargs.copy()
+ args['settings'] = get_settings()
+ args['source_path'] = 'content'
+ args['context']['filenames'] = {
+ 'images/poster.jpg': type(cls_name, (object,), {'url': 'images/poster.jpg'}),
+ 'assets/video.mp4': type(cls_name, (object,), {'url': 'assets/video.mp4'}),
+ 'images/graph.svg': type(cls_name, (object,), {'url': 'images/graph.svg'}),
+ 'reference.rst': type(cls_name, (object,), {'url': 'reference.html'}),
+ }
+
+ # video.poster
+ args['content'] = (
+ 'There is a video with poster '
+ ''
+ ''
+ ' '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'There is a video with poster '
+ ''
+ ''
+ ' '
+ )
+
+ # object.data
+ args['content'] = (
+ 'There is a svg object '
+ ' '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'There is a svg object '
+ ' '
+ )
+
+ # blockquote.cite
+ args['content'] = (
+ 'There is a blockquote with cite attribute '
+ '
blah blah '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'There is a blockquote with cite attribute '
+ 'blah blah '
+ )
+
+ def test_intrasite_link_markdown_spaces(self):
+ # Markdown introduces %20 instead of spaces, this tests that
+ # we support markdown doing this.
+ cls_name = '_DummyArticle' if six.PY3 else b'_DummyArticle'
+ article = type(cls_name, (object,), {'url': 'article-spaces.html'})
+
+ args = self.page_kwargs.copy()
+ args['settings'] = get_settings()
+ args['source_path'] = 'content'
+ args['context']['filenames'] = {'article spaces.rst': article}
+
+ # An intrasite link via filename with %20 as a space
+ args['content'] = (
+ 'A simple test, with a '
+ 'link '
+ )
+ content = Page(**args).get_content('http://notmyidea.org')
+ self.assertEqual(
+ content,
+ 'A simple test, with a '
+ 'link '
+ )
+
+ def test_multiple_authors(self):
+ """Test article with multiple authors."""
+ args = self.page_kwargs.copy()
+ content = Page(**args)
+ assert content.authors == [content.author]
+ args['metadata'].pop('author')
+ args['metadata']['authors'] = ['First Author', 'Second Author']
+ content = Page(**args)
+ assert content.authors
+ assert content.author == content.authors[0]
+
+
+class TestArticle(TestPage):
+ def test_template(self):
+ # Articles default to article, metadata overwrites
+ default_article = Article(**self.page_kwargs)
+ self.assertEqual('article', default_article.template)
+ article_kwargs = self._copy_page_kwargs()
+ article_kwargs['metadata']['template'] = 'custom'
+ custom_article = Article(**article_kwargs)
+ self.assertEqual('custom', custom_article.template)
+
+ def test_slugify_category_author(self):
+ settings = get_settings()
+ settings['SLUG_SUBSTITUTIONS'] = [ ('C#', 'csharp') ]
+ settings['ARTICLE_URL'] = '{author}/{category}/{slug}/'
+ settings['ARTICLE_SAVE_AS'] = '{author}/{category}/{slug}/index.html'
+ article_kwargs = self._copy_page_kwargs()
+ article_kwargs['metadata']['author'] = "O'Brien"
+ article_kwargs['metadata']['category'] = 'C# & stuff'
+ article_kwargs['metadata']['title'] = 'fnord'
+ article_kwargs['settings'] = settings
+ article = Article(**article_kwargs)
+ self.assertEqual(article.url, 'obrien/csharp-stuff/fnord/')
+ self.assertEqual(article.save_as, 'obrien/csharp-stuff/fnord/index.html')
+
+
+class TestURLWrapper(unittest.TestCase):
+ def test_comparisons(self):
+ # URLWrappers are sorted by name
+ wrapper_a = URLWrapper(name='first', settings={})
+ wrapper_b = URLWrapper(name='last', settings={})
+ self.assertFalse(wrapper_a > wrapper_b)
+ self.assertFalse(wrapper_a >= wrapper_b)
+ self.assertFalse(wrapper_a == wrapper_b)
+ self.assertTrue(wrapper_a != wrapper_b)
+ self.assertTrue(wrapper_a <= wrapper_b)
+ self.assertTrue(wrapper_a < wrapper_b)
+ wrapper_b.name = 'first'
+ self.assertFalse(wrapper_a > wrapper_b)
+ self.assertTrue(wrapper_a >= wrapper_b)
+ self.assertTrue(wrapper_a == wrapper_b)
+ self.assertFalse(wrapper_a != wrapper_b)
+ self.assertTrue(wrapper_a <= wrapper_b)
+ self.assertFalse(wrapper_a < wrapper_b)
+ wrapper_a.name = 'last'
+ self.assertTrue(wrapper_a > wrapper_b)
+ self.assertTrue(wrapper_a >= wrapper_b)
+ self.assertFalse(wrapper_a == wrapper_b)
+ self.assertTrue(wrapper_a != wrapper_b)
+ self.assertFalse(wrapper_a <= wrapper_b)
+ self.assertFalse(wrapper_a < wrapper_b)
diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py
new file mode 100644
index 00000000..2f53ac95
--- /dev/null
+++ b/pelican/tests/test_generators.py
@@ -0,0 +1,626 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import os
+from codecs import open
+try:
+ from unittest.mock import MagicMock
+except ImportError:
+ from mock import MagicMock
+from operator import itemgetter
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from pelican.generators import (Generator, ArticlesGenerator, PagesGenerator,
+ StaticGenerator, TemplatePagesGenerator)
+from pelican.writers import Writer
+from pelican.tests.support import unittest, get_settings
+import locale
+
+CUR_DIR = os.path.dirname(__file__)
+CONTENT_DIR = os.path.join(CUR_DIR, 'content')
+
+
+class TestGenerator(unittest.TestCase):
+ def setUp(self):
+ self.old_locale = locale.setlocale(locale.LC_ALL)
+ locale.setlocale(locale.LC_ALL, str('C'))
+ self.settings = get_settings()
+ self.settings['READERS'] = {'asc': None}
+ self.generator = Generator(self.settings.copy(), self.settings,
+ CUR_DIR, self.settings['THEME'], None)
+
+ def tearDown(self):
+ locale.setlocale(locale.LC_ALL, self.old_locale)
+
+
+ def test_include_path(self):
+ filename = os.path.join(CUR_DIR, 'content', 'article.rst')
+ include_path = self.generator._include_path
+ self.assertTrue(include_path(filename))
+ self.assertTrue(include_path(filename, extensions=('rst',)))
+ self.assertFalse(include_path(filename, extensions=('md',)))
+
+
+class TestArticlesGenerator(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ settings = get_settings(filenames={})
+ settings['DEFAULT_CATEGORY'] = 'Default'
+ settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings['READERS'] = {'asc': None}
+ settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
+
+ cls.generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ cls.generator.generate_context()
+ cls.articles = cls.distill_articles(cls.generator.articles)
+
+ def setUp(self):
+ self.temp_cache = mkdtemp(prefix='pelican_cache.')
+
+ def tearDown(self):
+ rmtree(self.temp_cache)
+
+ @staticmethod
+ def distill_articles(articles):
+ return [[article.title, article.status, article.category.name,
+ article.template] for article in articles]
+
+ def test_generate_feeds(self):
+ settings = get_settings()
+ settings['CACHE_PATH'] = self.temp_cache
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=None, theme=settings['THEME'], output_path=None)
+ writer = MagicMock()
+ generator.generate_feeds(writer)
+ writer.write_feed.assert_called_with([], settings,
+ 'feeds/all.atom.xml')
+
+ generator = ArticlesGenerator(
+ context=settings, settings=get_settings(FEED_ALL_ATOM=None),
+ path=None, theme=settings['THEME'], output_path=None)
+ writer = MagicMock()
+ generator.generate_feeds(writer)
+ self.assertFalse(writer.write_feed.called)
+
+ def test_generate_context(self):
+
+ articles_expected = [
+ ['Article title', 'published', 'Default', 'article'],
+ ['Article with markdown and summary metadata multi', 'published',
+ 'Default', 'article'],
+ ['Article with markdown and summary metadata single', 'published',
+ 'Default', 'article'],
+ ['Article with markdown containing footnotes', 'published',
+ 'Default', 'article'],
+ ['Article with template', 'published', 'Default', 'custom'],
+ ['Rst with filename metadata', 'published', 'yeah', 'article'],
+ ['Test Markdown extensions', 'published', 'Default', 'article'],
+ ['Test markdown File', 'published', 'test', 'article'],
+ ['Test md File', 'published', 'test', 'article'],
+ ['Test mdown File', 'published', 'test', 'article'],
+ ['Test mkd File', 'published', 'test', 'article'],
+ ['This is a super article !', 'published', 'Yeah', 'article'],
+ ['This is a super article !', 'published', 'Yeah', 'article'],
+ ['Article with Nonconformant HTML meta tags', 'published', 'Default', 'article'],
+ ['This is a super article !', 'published', 'yeah', 'article'],
+ ['This is a super article !', 'published', 'yeah', 'article'],
+ ['This is a super article !', 'published', 'yeah', 'article'],
+ ['This is a super article !', 'published', 'Default', 'article'],
+ ['This is an article with category !', 'published', 'yeah',
+ 'article'],
+ ['This is an article with multiple authors!', 'published', 'Default', 'article'],
+ ['This is an article with multiple authors!', 'published', 'Default', 'article'],
+ ['This is an article without category !', 'published', 'Default',
+ 'article'],
+ ['This is an article without category !', 'published',
+ 'TestCategory', 'article'],
+ ['An Article With Code Block To Test Typogrify Ignore',
+ 'published', 'Default', 'article'],
+ ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定', 'published',
+ '指導書', 'article'],
+ ]
+ self.assertEqual(sorted(articles_expected), sorted(self.articles))
+
+ def test_generate_categories(self):
+
+ # test for name
+ # categories are grouped by slug; if two categories have the same slug
+ # but different names they will be grouped together, the first one in
+ # terms of process order will define the name for that category
+ categories = [cat.name for cat, _ in self.generator.categories]
+ categories_alternatives = (
+ sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書']),
+ sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']),
+ )
+ self.assertIn(sorted(categories), categories_alternatives)
+ # test for slug
+ categories = [cat.slug for cat, _ in self.generator.categories]
+ categories_expected = ['default', 'testcategory', 'yeah', 'test',
+ 'zhi-dao-shu']
+ self.assertEqual(sorted(categories), sorted(categories_expected))
+
+ def test_do_not_use_folder_as_category(self):
+
+ settings = get_settings(filenames={})
+ settings['DEFAULT_CATEGORY'] = 'Default'
+ settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings['USE_FOLDER_AS_CATEGORY'] = False
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['READERS'] = {'asc': None}
+ settings['filenames'] = {}
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ # test for name
+ # categories are grouped by slug; if two categories have the same slug
+ # but different names they will be grouped together, the first one in
+ # terms of process order will define the name for that category
+ categories = [cat.name for cat, _ in generator.categories]
+ categories_alternatives = (
+ sorted(['Default', 'Yeah', 'test', '指導書']),
+ sorted(['Default', 'yeah', 'test', '指導書']),
+ )
+ self.assertIn(sorted(categories), categories_alternatives)
+ # test for slug
+ categories = [cat.slug for cat, _ in generator.categories]
+ categories_expected = ['default', 'yeah', 'test', 'zhi-dao-shu']
+ self.assertEqual(sorted(categories), sorted(categories_expected))
+
+ def test_direct_templates_save_as_default(self):
+
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=None, theme=settings['THEME'], output_path=None)
+ write = MagicMock()
+ generator.generate_direct_templates(write)
+ write.assert_called_with("archives.html",
+ generator.get_template("archives"), settings,
+ blog=True, paginated={}, page_name='archives')
+
+ def test_direct_templates_save_as_modified(self):
+
+ settings = get_settings()
+ settings['DIRECT_TEMPLATES'] = ['archives']
+ settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
+ settings['CACHE_PATH'] = self.temp_cache
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=None, theme=settings['THEME'], output_path=None)
+ write = MagicMock()
+ generator.generate_direct_templates(write)
+ write.assert_called_with("archives/index.html",
+ generator.get_template("archives"), settings,
+ blog=True, paginated={},
+ page_name='archives/index')
+
+ def test_direct_templates_save_as_false(self):
+
+ settings = get_settings()
+ settings['DIRECT_TEMPLATES'] = ['archives']
+ settings['ARCHIVES_SAVE_AS'] = False
+ settings['CACHE_PATH'] = self.temp_cache
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=None, theme=settings['THEME'], output_path=None)
+ write = MagicMock()
+ generator.generate_direct_templates(write)
+ self.assertEqual(write.call_count, 0)
+
+ def test_per_article_template(self):
+ """
+ Custom template articles get the field but standard/unset are None
+ """
+ custom_template = ['Article with template', 'published', 'Default',
+ 'custom']
+ standard_template = ['This is a super article !', 'published', 'Yeah',
+ 'article']
+ self.assertIn(custom_template, self.articles)
+ self.assertIn(standard_template, self.articles)
+
+ def test_period_in_timeperiod_archive(self):
+ """
+ Test that the context of a generated period_archive is passed
+ 'period' : a tuple of year, month, day according to the time period
+ """
+ settings = get_settings(filenames={})
+
+ settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
+ settings['CACHE_PATH'] = self.temp_cache
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ write = MagicMock()
+ generator.generate_period_archives(write)
+ dates = [d for d in generator.dates if d.date.year == 1970]
+ self.assertEqual(len(dates), 1)
+ #among other things it must have at least been called with this
+ settings["period"] = (1970,)
+ write.assert_called_with("posts/1970/index.html",
+ generator.get_template("period_archives"),
+ settings,
+ blog=True, dates=dates)
+
+ del settings["period"]
+ settings['MONTH_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/index.html'
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ write = MagicMock()
+ generator.generate_period_archives(write)
+ dates = [d for d in generator.dates if d.date.year == 1970
+ and d.date.month == 1]
+ self.assertEqual(len(dates), 1)
+ settings["period"] = (1970, "January")
+ #among other things it must have at least been called with this
+ write.assert_called_with("posts/1970/Jan/index.html",
+ generator.get_template("period_archives"),
+ settings,
+ blog=True, dates=dates)
+
+ del settings["period"]
+ settings['DAY_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
+ generator = ArticlesGenerator(
+ context=settings, settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ write = MagicMock()
+ generator.generate_period_archives(write)
+ dates = [d for d in generator.dates if d.date.year == 1970
+ and d.date.month == 1
+ and d.date.day == 1]
+ self.assertEqual(len(dates), 1)
+ settings["period"] = (1970, "January", 1)
+ #among other things it must have at least been called with this
+ write.assert_called_with("posts/1970/Jan/01/index.html",
+ generator.get_template("period_archives"),
+ settings,
+ blog=True, dates=dates)
+
+ def test_generate_authors(self):
+ """Check authors generation."""
+ authors = [author.name for author, _ in self.generator.authors]
+ authors_expected = sorted(['Alexis Métaireau', 'First Author', 'Second Author'])
+ self.assertEqual(sorted(authors), authors_expected)
+ # test for slug
+ authors = [author.slug for author, _ in self.generator.authors]
+ authors_expected = ['alexis-metaireau', 'first-author', 'second-author']
+ self.assertEqual(sorted(authors), sorted(authors_expected))
+
+ def test_article_object_caching(self):
+ """Test Article objects caching at the generator level"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['CONTENT_CACHING_LAYER'] = 'generator'
+ settings['READERS'] = {'asc': None}
+
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ self.assertTrue(hasattr(generator, '_cache'))
+
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ generator.readers.read_file.assert_called_count == 0
+
+ def test_reader_content_caching(self):
+ """Test raw content caching at the reader level"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['READERS'] = {'asc': None}
+
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ self.assertTrue(hasattr(generator.readers, '_cache'))
+
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ readers = generator.readers.readers
+ for reader in readers.values():
+ reader.read = MagicMock()
+ generator.generate_context()
+ for reader in readers.values():
+ reader.read.assert_called_count == 0
+
+ def test_ignore_cache(self):
+ """Test that all the articles are read again when not loading cache
+
+ used in --ignore-cache or autoreload mode"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['READERS'] = {'asc': None}
+
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ self.assertTrue(hasattr(generator, '_cache_open'))
+ orig_call_count = generator.readers.read_file.call_count
+
+ settings['LOAD_CONTENT_CACHE'] = False
+ generator = ArticlesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ generator.readers.read_file.assert_called_count == orig_call_count
+
+
+class TestPageGenerator(unittest.TestCase):
+ # Note: Every time you want to test for a new field; Make sure the test
+ # pages in "TestPages" have all the fields Add it to distilled in
+ # distill_pages Then update the assertEqual in test_generate_context
+ # to match expected
+
+ def setUp(self):
+ self.temp_cache = mkdtemp(prefix='pelican_cache.')
+
+ def tearDown(self):
+ rmtree(self.temp_cache)
+
+ def distill_pages(self, pages):
+ return [[page.title, page.status, page.template] for page in pages]
+
+ def test_generate_context(self):
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
+ settings['DEFAULT_DATE'] = (1970, 1, 1)
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ pages = self.distill_pages(generator.pages)
+ hidden_pages = self.distill_pages(generator.hidden_pages)
+
+ pages_expected = [
+ ['This is a test page', 'published', 'page'],
+ ['This is a markdown test page', 'published', 'page'],
+ ['This is a test page with a preset template', 'published',
+ 'custom'],
+ ['A Page (Test) for sorting', 'published', 'page'],
+ ]
+ hidden_pages_expected = [
+ ['This is a test hidden page', 'hidden', 'page'],
+ ['This is a markdown test hidden page', 'hidden', 'page'],
+ ['This is a test hidden page with a custom template', 'hidden',
+ 'custom']
+ ]
+
+ self.assertEqual(sorted(pages_expected), sorted(pages))
+ self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages))
+
+ def test_page_object_caching(self):
+ """Test Page objects caching at the generator level"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['CONTENT_CACHING_LAYER'] = 'generator'
+ settings['READERS'] = {'asc': None}
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ self.assertTrue(hasattr(generator, '_cache'))
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ generator.readers.read_file.assert_called_count == 0
+
+ def test_reader_content_caching(self):
+ """Test raw content caching at the reader level"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['READERS'] = {'asc': None}
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ self.assertTrue(hasattr(generator.readers, '_cache'))
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ readers = generator.readers.readers
+ for reader in readers.values():
+ reader.read = MagicMock()
+ generator.generate_context()
+ for reader in readers.values():
+ reader.read.assert_called_count == 0
+
+ def test_ignore_cache(self):
+ """Test that all the pages are read again when not loading cache
+
+ used in --ignore_cache or autoreload mode"""
+ settings = get_settings(filenames={})
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['READERS'] = {'asc': None}
+
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ self.assertTrue(hasattr(generator, '_cache_open'))
+ orig_call_count = generator.readers.read_file.call_count
+
+ settings['LOAD_CONTENT_CACHE'] = False
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ generator.readers.read_file = MagicMock()
+ generator.generate_context()
+ generator.readers.read_file.assert_called_count == orig_call_count
+
+ def test_generate_sorted(self):
+ settings = get_settings(filenames={})
+ settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
+ settings['CACHE_PATH'] = self.temp_cache
+ settings['DEFAULT_DATE'] = (1970, 1, 1)
+
+ # default sort (filename)
+ pages_expected_sorted_by_filename = [
+ ['This is a test page', 'published', 'page'],
+ ['This is a markdown test page', 'published', 'page'],
+ ['A Page (Test) for sorting', 'published', 'page'],
+ ['This is a test page with a preset template', 'published',
+ 'custom'],
+ ]
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ pages = self.distill_pages(generator.pages)
+ self.assertEqual(pages_expected_sorted_by_filename, pages)
+
+ # sort by title
+ pages_expected_sorted_by_title = [
+ ['A Page (Test) for sorting', 'published', 'page'],
+ ['This is a markdown test page', 'published', 'page'],
+ ['This is a test page', 'published', 'page'],
+ ['This is a test page with a preset template', 'published',
+ 'custom'],
+ ]
+ settings['PAGE_ORDER_BY'] = 'title'
+ generator = PagesGenerator(
+ context=settings.copy(), settings=settings,
+ path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ generator.generate_context()
+ pages = self.distill_pages(generator.pages)
+ self.assertEqual(pages_expected_sorted_by_title, pages)
+
+
+class TestTemplatePagesGenerator(unittest.TestCase):
+
+ TEMPLATE_CONTENT = "foo: {{ foo }}"
+
+ def setUp(self):
+ self.temp_content = mkdtemp(prefix='pelicantests.')
+ self.temp_output = mkdtemp(prefix='pelicantests.')
+ self.old_locale = locale.setlocale(locale.LC_ALL)
+ locale.setlocale(locale.LC_ALL, str('C'))
+
+
+ def tearDown(self):
+ rmtree(self.temp_content)
+ rmtree(self.temp_output)
+ locale.setlocale(locale.LC_ALL, self.old_locale)
+
+ def test_generate_output(self):
+
+ settings = get_settings()
+ settings['STATIC_PATHS'] = ['static']
+ settings['TEMPLATE_PAGES'] = {
+ 'template/source.html': 'generated/file.html'
+ }
+
+ generator = TemplatePagesGenerator(
+ context={'foo': 'bar'}, settings=settings,
+ path=self.temp_content, theme='', output_path=self.temp_output)
+
+ # create a dummy template file
+ template_dir = os.path.join(self.temp_content, 'template')
+ template_path = os.path.join(template_dir, 'source.html')
+ os.makedirs(template_dir)
+ with open(template_path, 'w') as template_file:
+ template_file.write(self.TEMPLATE_CONTENT)
+
+ writer = Writer(self.temp_output, settings=settings)
+ generator.generate_output(writer)
+
+ output_path = os.path.join(self.temp_output, 'generated', 'file.html')
+
+ # output file has been generated
+ self.assertTrue(os.path.exists(output_path))
+
+ # output content is correct
+ with open(output_path, 'r') as output_file:
+ self.assertEqual(output_file.read(), 'foo: bar')
+
+
+class TestStaticGenerator(unittest.TestCase):
+
+ def setUp(self):
+ self.content_path = os.path.join(CUR_DIR, 'mixed_content')
+
+ def test_static_excludes(self):
+ """Test that StaticGenerator respects STATIC_EXCLUDES.
+ """
+ settings = get_settings(STATIC_EXCLUDES=['subdir'],
+ PATH=self.content_path, STATIC_PATHS=[''])
+ context = settings.copy()
+ context['filenames'] = {}
+
+ StaticGenerator(context=context, settings=settings,
+ path=settings['PATH'], output_path=None,
+ theme=settings['THEME']).generate_context()
+
+ staticnames = [os.path.basename(c.source_path)
+ for c in context['staticfiles']]
+
+ self.assertNotIn('subdir_fake_image.jpg', staticnames,
+ "StaticGenerator processed a file in a STATIC_EXCLUDES directory")
+ self.assertIn('fake_image.jpg', staticnames,
+ "StaticGenerator skipped a file that it should have included")
+
+ def test_static_exclude_sources(self):
+ """Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES.
+ """
+ # Test STATIC_EXCLUDE_SOURCES=True
+
+ settings = get_settings(STATIC_EXCLUDE_SOURCES=True,
+ PATH=self.content_path, PAGE_PATHS=[''], STATIC_PATHS=[''],
+ CACHE_CONTENT=False)
+ context = settings.copy()
+ context['filenames'] = {}
+
+ for generator_class in (PagesGenerator, StaticGenerator):
+ generator_class(context=context, settings=settings,
+ path=settings['PATH'], output_path=None,
+ theme=settings['THEME']).generate_context()
+
+ staticnames = [os.path.basename(c.source_path)
+ for c in context['staticfiles']]
+
+ self.assertFalse(any(name.endswith(".md") for name in staticnames),
+ "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file")
+
+ # Test STATIC_EXCLUDE_SOURCES=False
+
+ settings.update(STATIC_EXCLUDE_SOURCES=False)
+ context = settings.copy()
+ context['filenames'] = {}
+
+ for generator_class in (PagesGenerator, StaticGenerator):
+ generator_class(context=context, settings=settings,
+ path=settings['PATH'], output_path=None,
+ theme=settings['THEME']).generate_context()
+
+ staticnames = [os.path.basename(c.source_path)
+ for c in context['staticfiles']]
+
+ self.assertTrue(any(name.endswith(".md") for name in staticnames),
+ "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file")
+
diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py
new file mode 100644
index 00000000..65193bf5
--- /dev/null
+++ b/pelican/tests/test_importer.py
@@ -0,0 +1,304 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+import os
+import re
+
+import locale
+from pelican.tools.pelican_import import wp2fields, fields2pelican, decode_wp_content, build_header, build_markdown_header, get_attachments, download_attachments
+from pelican.tests.support import (unittest, temporary_folder, mute,
+ skipIfNoExecutable)
+
+from pelican.utils import slugify
+
+CUR_DIR = os.path.abspath(os.path.dirname(__file__))
+WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml')
+WORDPRESS_ENCODED_CONTENT_SAMPLE = os.path.join(CUR_DIR,
+ 'content',
+ 'wordpress_content_encoded')
+WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join(CUR_DIR,
+ 'content',
+ 'wordpress_content_decoded')
+
+try:
+ from bs4 import BeautifulSoup
+except ImportError:
+ BeautifulSoup = False # NOQA
+
+
+@skipIfNoExecutable(['pandoc', '--version'])
+@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module')
+class TestWordpressXmlImporter(unittest.TestCase):
+
+ def setUp(self):
+ self.old_locale = locale.setlocale(locale.LC_ALL)
+ locale.setlocale(locale.LC_ALL, str('C'))
+ self.posts = list(wp2fields(WORDPRESS_XML_SAMPLE))
+ self.custposts = list(wp2fields(WORDPRESS_XML_SAMPLE, True))
+
+ def tearDown(self):
+ locale.setlocale(locale.LC_ALL, self.old_locale)
+
+ def test_ignore_empty_posts(self):
+ self.assertTrue(self.posts)
+ for title, content, fname, date, author, categ, tags, kind, format in self.posts:
+ self.assertTrue(title.strip())
+
+ def test_recognise_page_kind(self):
+ """ Check that we recognise pages in wordpress, as opposed to posts """
+ self.assertTrue(self.posts)
+ # Collect (title, filename, kind) of non-empty posts recognised as page
+ pages_data = []
+ for title, content, fname, date, author, categ, tags, kind, format in self.posts:
+ if kind == 'page':
+ pages_data.append((title, fname))
+ self.assertEqual(2, len(pages_data))
+ self.assertEqual(('Page', 'contact'), pages_data[0])
+ self.assertEqual(('Empty Page', 'empty'), pages_data[1])
+
+ def test_dirpage_directive_for_page_kind(self):
+ silent_f2p = mute(True)(fields2pelican)
+ test_post = filter(lambda p: p[0].startswith("Empty Page"), self.posts)
+ with temporary_folder() as temp:
+ fname = list(silent_f2p(test_post, 'markdown', temp, dirpage=True))[0]
+ self.assertTrue(fname.endswith('pages%sempty.md' % os.path.sep))
+
+ def test_dircat(self):
+ silent_f2p = mute(True)(fields2pelican)
+ test_posts = []
+ for post in self.posts:
+ # check post kind
+ if len(post[5]) > 0: # Has a category
+ test_posts.append(post)
+ with temporary_folder() as temp:
+ fnames = list(silent_f2p(test_posts, 'markdown', temp, dircat=True))
+ index = 0
+ for post in test_posts:
+ name = post[2]
+ category = slugify(post[5][0])
+ name += '.md'
+ filename = os.path.join(category, name)
+ out_name = fnames[index]
+ self.assertTrue(out_name.endswith(filename))
+ index += 1
+
+ def test_unless_custom_post_all_items_should_be_pages_or_posts(self):
+ self.assertTrue(self.posts)
+ pages_data = []
+ for title, content, fname, date, author, categ, tags, kind, format in self.posts:
+ if kind == 'page' or kind == 'article':
+ pass
+ else:
+ pages_data.append((title, fname))
+ self.assertEqual(0, len(pages_data))
+
+ def test_recognise_custom_post_type(self):
+ self.assertTrue(self.custposts)
+ cust_data = []
+ for title, content, fname, date, author, categ, tags, kind, format in self.custposts:
+ if kind == 'article' or kind == 'page':
+ pass
+ else:
+ cust_data.append((title, kind))
+ self.assertEqual(3, len(cust_data))
+ self.assertEqual(('A custom post in category 4', 'custom1'), cust_data[0])
+ self.assertEqual(('A custom post in category 5', 'custom1'), cust_data[1])
+ self.assertEqual(('A 2nd custom post type also in category 5', 'custom2'), cust_data[2])
+
+ def test_custom_posts_put_in_own_dir(self):
+ silent_f2p = mute(True)(fields2pelican)
+ test_posts = []
+ for post in self.custposts:
+ # check post kind
+ if post[7] == 'article' or post[7] == 'page':
+ pass
+ else:
+ test_posts.append(post)
+ with temporary_folder() as temp:
+ fnames = list(silent_f2p(test_posts, 'markdown', temp, wp_custpost = True))
+ index = 0
+ for post in test_posts:
+ name = post[2]
+ kind = post[7]
+ name += '.md'
+ filename = os.path.join(kind, name)
+ out_name = fnames[index]
+ self.assertTrue(out_name.endswith(filename))
+ index += 1
+
+ def test_custom_posts_put_in_own_dir_and_catagory_sub_dir(self):
+ silent_f2p = mute(True)(fields2pelican)
+ test_posts = []
+ for post in self.custposts:
+ # check post kind
+ if post[7] == 'article' or post[7] == 'page':
+ pass
+ else:
+ test_posts.append(post)
+ with temporary_folder() as temp:
+ fnames = list(silent_f2p(test_posts, 'markdown', temp,
+ wp_custpost=True, dircat=True))
+ index = 0
+ for post in test_posts:
+ name = post[2]
+ kind = post[7]
+ category = slugify(post[5][0])
+ name += '.md'
+ filename = os.path.join(kind, category, name)
+ out_name = fnames[index]
+ self.assertTrue(out_name.endswith(filename))
+ index += 1
+
+ def test_wp_custpost_true_dirpage_false(self):
+ #pages should only be put in their own directory when dirpage = True
+ silent_f2p = mute(True)(fields2pelican)
+ test_posts = []
+ for post in self.custposts:
+ # check post kind
+ if post[7] == 'page':
+ test_posts.append(post)
+ with temporary_folder() as temp:
+ fnames = list(silent_f2p(test_posts, 'markdown', temp,
+ wp_custpost=True, dirpage=False))
+ index = 0
+ for post in test_posts:
+ name = post[2]
+ name += '.md'
+ filename = os.path.join('pages', name)
+ out_name = fnames[index]
+ self.assertFalse(out_name.endswith(filename))
+
+
+ def test_can_toggle_raw_html_code_parsing(self):
+ def r(f):
+ with open(f) as infile:
+ return infile.read()
+ silent_f2p = mute(True)(fields2pelican)
+
+ with temporary_folder() as temp:
+
+ rst_files = (r(f) for f in silent_f2p(self.posts, 'markdown', temp))
+ self.assertTrue(any('
', '')
+ content = re.sub(r'\s*(?' + allblocks + '[^>]*>)', "\\1", content)
+ content = re.sub(r'(?' + allblocks + '[^>]*>)\s*
', "\\1", content)
+ if br:
+ def _preserve_newline(match):
+ return match.group(0).replace("\n", " ")
+ content = re.sub(r'/<(script|style).*?<\/\\1>/s', _preserve_newline, content)
+ # optionally make line breaks
+ content = re.sub(r'(?)\s*\n', " \n", content)
+ content = content.replace(" ", "\n")
+ content = re.sub(r'(?' + allblocks + r'[^>]*>)\s* ', "\\1", content)
+ content = re.sub(r' (\s*?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)', '\\1', content)
+ content = re.sub(r'\n', "", content)
+
+ if pre_tags:
+ def _multi_replace(dic, string):
+ pattern = r'|'.join(map(re.escape, dic.keys()))
+ return re.sub(pattern, lambda m: dic[m.group()], string)
+ content = _multi_replace(pre_tags, content)
+
+ return content
+
+def get_items(xml):
+ """Opens a wordpress xml file and returns a list of items"""
+ try:
+ from bs4 import BeautifulSoup
+ except ImportError:
+ error = ('Missing dependency '
+ '"BeautifulSoup4" and "lxml" required to import Wordpress XML files.')
+ sys.exit(error)
+ with open(xml, encoding='utf-8') as infile:
+ xmlfile = infile.read()
+ soup = BeautifulSoup(xmlfile, "xml")
+ items = soup.rss.channel.findAll('item')
+ return items
+
+def get_filename(filename, post_id):
+ if filename is not None:
+ return filename
+ else:
+ return post_id
+
+def wp2fields(xml, wp_custpost=False):
+ """Opens a wordpress XML file, and yield Pelican fields"""
+
+ items = get_items(xml)
+ for item in items:
+
+ if item.find('status').string == "publish":
+
+ try:
+ # Use HTMLParser due to issues with BeautifulSoup 3
+ title = unescape(item.title.contents[0])
+ except IndexError:
+ title = 'No title [%s]' % item.find('post_name').string
+ logger.warning('Post "%s" is lacking a proper title', title)
+
+ filename = item.find('post_name').string
+ post_id = item.find('post_id').string
+ filename = get_filename(filename, post_id)
+
+ content = item.find('encoded').string
+ raw_date = item.find('post_date').string
+ date_object = time.strptime(raw_date, "%Y-%m-%d %H:%M:%S")
+ date = time.strftime("%Y-%m-%d %H:%M", date_object)
+ author = item.find('creator').string
+
+ categories = [cat.string for cat in item.findAll('category', {'domain' : 'category'})]
+ # caturl = [cat['nicename'] for cat in item.find(domain='category')]
+
+ tags = [tag.string for tag in item.findAll('category', {'domain' : 'post_tag'})]
+
+ kind = 'article'
+ post_type = item.find('post_type').string
+ if post_type == 'page':
+ kind = 'page'
+ elif wp_custpost:
+ if post_type == 'post':
+ pass
+ # Old behaviour was to name everything not a page as an article.
+ # Theoretically all attachments have status == inherit so
+ # no attachments should be here. But this statement is to
+ # maintain existing behaviour in case that doesn't hold true.
+ elif post_type == 'attachment':
+ pass
+ else:
+ kind = post_type
+ yield (title, content, filename, date, author, categories, tags,
+ kind, "wp-html")
+
+def dc2fields(file):
+ """Opens a Dotclear export file, and yield pelican fields"""
+ try:
+ from bs4 import BeautifulSoup
+ except ImportError:
+ error = ('Missing dependency '
+ '"BeautifulSoup4" and "lxml" required to import Dotclear files.')
+ sys.exit(error)
+
+
+ in_cat = False
+ in_post = False
+ category_list = {}
+ posts = []
+
+ with open(file, 'r', encoding='utf-8') as f:
+
+ for line in f:
+ # remove final \n
+ line = line[:-1]
+
+ if line.startswith('[category'):
+ in_cat = True
+ elif line.startswith('[post'):
+ in_post = True
+ elif in_cat:
+ fields = line.split('","')
+ if not line:
+ in_cat = False
+ else:
+ # remove 1st and last ""
+ fields[0] = fields[0][1:]
+ # fields[-1] = fields[-1][:-1]
+ category_list[fields[0]]=fields[2]
+ elif in_post:
+ if not line:
+ in_post = False
+ break
+ else:
+ posts.append(line)
+
+ print("%i posts read." % len(posts))
+
+ for post in posts:
+ fields = post.split('","')
+
+ # post_id = fields[0][1:]
+ # blog_id = fields[1]
+ # user_id = fields[2]
+ cat_id = fields[3]
+ # post_dt = fields[4]
+ # post_tz = fields[5]
+ post_creadt = fields[6]
+ # post_upddt = fields[7]
+ # post_password = fields[8]
+ # post_type = fields[9]
+ post_format = fields[10]
+ # post_url = fields[11]
+ # post_lang = fields[12]
+ post_title = fields[13]
+ post_excerpt = fields[14]
+ post_excerpt_xhtml = fields[15]
+ post_content = fields[16]
+ post_content_xhtml = fields[17]
+ # post_notes = fields[18]
+ # post_words = fields[19]
+ # post_status = fields[20]
+ # post_selected = fields[21]
+ # post_position = fields[22]
+ # post_open_comment = fields[23]
+ # post_open_tb = fields[24]
+ # nb_comment = fields[25]
+ # nb_trackback = fields[26]
+ post_meta = fields[27]
+ # redirect_url = fields[28][:-1]
+
+ # remove seconds
+ post_creadt = ':'.join(post_creadt.split(':')[0:2])
+
+ author = ""
+ categories = []
+ tags = []
+
+ if cat_id:
+ categories = [category_list[id].strip() for id in cat_id.split(',')]
+
+ # Get tags related to a post
+ tag = post_meta.replace('{', '').replace('}', '').replace('a:1:s:3:\\"tag\\";a:', '').replace('a:0:', '')
+ if len(tag) > 1:
+ if int(tag[:1]) == 1:
+ newtag = tag.split('"')[1]
+ tags.append(
+ BeautifulSoup(
+ newtag
+ , "xml"
+ )
+ # bs4 always outputs UTF-8
+ .decode('utf-8')
+ )
+ else:
+ i=1
+ j=1
+ while(i <= int(tag[:1])):
+ newtag = tag.split('"')[j].replace('\\','')
+ tags.append(
+ BeautifulSoup(
+ newtag
+ , "xml"
+ )
+ # bs4 always outputs UTF-8
+ .decode('utf-8')
+ )
+ i=i+1
+ if j < int(tag[:1])*2:
+ j=j+2
+
+ """
+ dotclear2 does not use markdown by default unless you use the markdown plugin
+ Ref: http://plugins.dotaddict.org/dc2/details/formatting-markdown
+ """
+ if post_format == "markdown":
+ content = post_excerpt + post_content
+ else:
+ content = post_excerpt_xhtml + post_content_xhtml
+ content = content.replace('\\n', '')
+ post_format = "html"
+
+ kind = 'article' # TODO: Recognise pages
+
+ yield (post_title, content, slugify(post_title), post_creadt, author,
+ categories, tags, kind, post_format)
+
+
+def posterous2fields(api_token, email, password):
+ """Imports posterous posts"""
+ import base64
+ from datetime import timedelta
+ try:
+ # py3k import
+ import json
+ except ImportError:
+ # py2 import
+ import simplejson as json
+
+ try:
+ # py3k import
+ import urllib.request as urllib_request
+ except ImportError:
+ # py2 import
+ import urllib2 as urllib_request
+
+
+ def get_posterous_posts(api_token, email, password, page = 1):
+ base64string = base64.encodestring(("%s:%s" % (email, password)).encode('utf-8')).replace(b'\n', b'')
+ url = "http://posterous.com/api/v2/users/me/sites/primary/posts?api_token=%s&page=%d" % (api_token, page)
+ request = urllib_request.Request(url)
+ request.add_header("Authorization", "Basic %s" % base64string.decode())
+ handle = urllib_request.urlopen(request)
+ posts = json.loads(handle.read().decode('utf-8'))
+ return posts
+
+ page = 1
+ posts = get_posterous_posts(api_token, email, password, page)
+ while len(posts) > 0:
+ posts = get_posterous_posts(api_token, email, password, page)
+ page += 1
+
+ for post in posts:
+ slug = post.get('slug')
+ if not slug:
+ slug = slugify(post.get('title'))
+ tags = [tag.get('name') for tag in post.get('tags')]
+ raw_date = post.get('display_date')
+ date_object = SafeDatetime.strptime(raw_date[:-6], "%Y/%m/%d %H:%M:%S")
+ offset = int(raw_date[-5:])
+ delta = timedelta(hours = offset / 100)
+ date_object -= delta
+ date = date_object.strftime("%Y-%m-%d %H:%M")
+ kind = 'article' # TODO: Recognise pages
+
+ yield (post.get('title'), post.get('body_cleaned'), slug, date,
+ post.get('user').get('display_name'), [], tags, kind, "html")
+
+
+def tumblr2fields(api_key, blogname):
+ """ Imports Tumblr posts (API v2)"""
+ from time import strftime, localtime
+ try:
+ # py3k import
+ import json
+ except ImportError:
+ # py2 import
+ import simplejson as json
+
+ try:
+ # py3k import
+ import urllib.request as urllib_request
+ except ImportError:
+ # py2 import
+ import urllib2 as urllib_request
+
+ def get_tumblr_posts(api_key, blogname, offset=0):
+ url = "http://api.tumblr.com/v2/blog/%s.tumblr.com/posts?api_key=%s&offset=%d&filter=raw" % (blogname, api_key, offset)
+ request = urllib_request.Request(url)
+ handle = urllib_request.urlopen(request)
+ posts = json.loads(handle.read().decode('utf-8'))
+ return posts.get('response').get('posts')
+
+ offset = 0
+ posts = get_tumblr_posts(api_key, blogname, offset)
+ while len(posts) > 0:
+ for post in posts:
+ title = post.get('title') or post.get('source_title') or post.get('type').capitalize()
+ slug = post.get('slug') or slugify(title)
+ tags = post.get('tags')
+ timestamp = post.get('timestamp')
+ date = strftime("%Y-%m-%d %H:%M:%S", localtime(int(timestamp)))
+ slug = strftime("%Y-%m-%d-", localtime(int(timestamp))) + slug
+ format = post.get('format')
+ content = post.get('body')
+ type = post.get('type')
+ if type == 'photo':
+ if format == 'markdown':
+ fmtstr = ''
+ else:
+ fmtstr = ' '
+ content = '\n'.join(fmtstr % (photo.get('caption'), photo.get('original_size').get('url')) for photo in post.get('photos'))
+ content += '\n\n' + post.get('caption')
+ elif type == 'quote':
+ if format == 'markdown':
+ fmtstr = '\n\n— %s'
+ else:
+ fmtstr = '— %s
'
+ content = post.get('text') + fmtstr % post.get('source')
+ elif type == 'link':
+ if format == 'markdown':
+ fmtstr = '[via](%s)\n\n'
+ else:
+ fmtstr = 'via
\n'
+ content = fmtstr % post.get('url') + post.get('description')
+ elif type == 'audio':
+ if format == 'markdown':
+ fmtstr = '[via](%s)\n\n'
+ else:
+ fmtstr = 'via
\n'
+ content = fmtstr % post.get('source_url') + post.get('caption') + post.get('player')
+ elif type == 'video':
+ if format == 'markdown':
+ fmtstr = '[via](%s)\n\n'
+ else:
+ fmtstr = 'via
\n'
+ content = fmtstr % post.get('source_url') + post.get('caption') + '\n'.join(player.get('embed_code') for player in post.get('player'))
+ elif type == 'answer':
+ title = post.get('question')
+ content = '%s : %s
\n%s' % (post.get('asking_name'), post.get('asking_url'), post.get('question'), post.get('answer'))
+
+ content = content.rstrip() + '\n'
+ kind = 'article'
+ yield (title, content, slug, date, post.get('blog_name'), [type],
+ tags, kind, format)
+
+ offset += len(posts)
+ posts = get_tumblr_posts(api_key, blogname, offset)
+
+def feed2fields(file):
+ """Read a feed and yield pelican fields"""
+ import feedparser
+ d = feedparser.parse(file)
+ for entry in d.entries:
+ date = (time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed)
+ if hasattr(entry, "updated_parsed") else None)
+ author = entry.author if hasattr(entry, "author") else None
+ tags = [e['term'] for e in entry.tags] if hasattr(entry, "tags") else None
+
+ slug = slugify(entry.title)
+ kind = 'article'
+ yield (entry.title, entry.description, slug, date, author, [], tags,
+ kind, "html")
+
+def build_header(title, date, author, categories, tags, slug, attachments=None):
+ from docutils.utils import column_width
+
+ """Build a header from a list of fields"""
+ header = '%s\n%s\n' % (title, '#' * column_width(title))
+ if date:
+ header += ':date: %s\n' % date
+ if author:
+ header += ':author: %s\n' % author
+ if categories:
+ header += ':category: %s\n' % ', '.join(categories)
+ if tags:
+ header += ':tags: %s\n' % ', '.join(tags)
+ if slug:
+ header += ':slug: %s\n' % slug
+ if attachments:
+ header += ':attachments: %s\n' % ', '.join(attachments)
+ header += '\n'
+ return header
+
+def build_markdown_header(title, date, author, categories, tags, slug,
+ attachments=None):
+ """Build a header from a list of fields"""
+ header = 'Title: %s\n' % title
+ if date:
+ header += 'Date: %s\n' % date
+ if author:
+ header += 'Author: %s\n' % author
+ if categories:
+ header += 'Category: %s\n' % ', '.join(categories)
+ if tags:
+ header += 'Tags: %s\n' % ', '.join(tags)
+ if slug:
+ header += 'Slug: %s\n' % slug
+ if attachments:
+ header += 'Attachments: %s\n' % ', '.join(attachments)
+ header += '\n'
+ return header
+
+def get_ext(out_markup, in_markup='html'):
+ if in_markup == 'markdown' or out_markup == 'markdown':
+ ext = '.md'
+ else:
+ ext = '.rst'
+ return ext
+
+def get_out_filename(output_path, filename, ext, kind,
+ dirpage, dircat, categories, wp_custpost):
+ filename = os.path.basename(filename)
+
+ # Enforce filename restrictions for various filesystems at once; see
+ # http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
+ # we do not need to filter words because an extension will be appended
+ filename = re.sub(r'[<>:"/\\|?*^% ]', '-', filename) # invalid chars
+ filename = filename.lstrip('.') # should not start with a dot
+ if not filename:
+ filename = '_'
+ filename = filename[:249] # allow for 5 extra characters
+
+ out_filename = os.path.join(output_path, filename+ext)
+ # option to put page posts in pages/ subdirectory
+ if dirpage and kind == 'page':
+ pages_dir = os.path.join(output_path, 'pages')
+ if not os.path.isdir(pages_dir):
+ os.mkdir(pages_dir)
+ out_filename = os.path.join(pages_dir, filename+ext)
+ elif not dirpage and kind == 'page':
+ pass
+ # option to put wp custom post types in directories with post type
+ # names. Custom post types can also have categories so option to
+ # create subdirectories with category names
+ elif kind != 'article':
+ if wp_custpost:
+ typename = slugify(kind)
+ else:
+ typename = ''
+ kind = 'article'
+ if dircat and (len(categories) > 0):
+ catname = slugify(categories[0])
+ else:
+ catname = ''
+ out_filename = os.path.join(output_path, typename,
+ catname, filename+ext)
+ if not os.path.isdir(os.path.join(output_path, typename, catname)):
+ os.makedirs(os.path.join(output_path, typename, catname))
+ # option to put files in directories with categories names
+ elif dircat and (len(categories) > 0):
+ catname = slugify(categories[0])
+ out_filename = os.path.join(output_path, catname, filename+ext)
+ if not os.path.isdir(os.path.join(output_path, catname)):
+ os.mkdir(os.path.join(output_path, catname))
+
+ return out_filename
+
+def get_attachments(xml):
+ """returns a dictionary of posts that have attachments with a list
+ of the attachment_urls
+ """
+ items = get_items(xml)
+ names = {}
+ attachments = []
+
+ for item in items:
+ kind = item.find('post_type').string
+ filename = item.find('post_name').string
+ post_id = item.find('post_id').string
+
+ if kind == 'attachment':
+ attachments.append((item.find('post_parent').string,
+ item.find('attachment_url').string))
+ else:
+ filename = get_filename(filename, post_id)
+ names[post_id] = filename
+ attachedposts = {}
+ for parent, url in attachments:
+ try:
+ parent_name = names[parent]
+ except KeyError:
+ #attachment's parent is not a valid post
+ parent_name = None
+
+ try:
+ attachedposts[parent_name].append(url)
+ except KeyError:
+ attachedposts[parent_name] = []
+ attachedposts[parent_name].append(url)
+ return attachedposts
+
+def download_attachments(output_path, urls):
+ """Downloads wordpress attachments and returns a list of paths to
+ attachments that can be associated with a post (relative path to output
+ directory). Files that fail to download, will not be added to posts"""
+ locations = []
+ for url in urls:
+ path = urlparse(url).path
+ #teardown path and rebuild to negate any errors with
+ #os.path.join and leading /'s
+ path = path.split('/')
+ filename = path.pop(-1)
+ localpath = ''
+ for item in path:
+ localpath = os.path.join(localpath, item)
+ full_path = os.path.join(output_path, localpath)
+ if not os.path.exists(full_path):
+ os.makedirs(full_path)
+ print('downloading {}'.format(filename))
+ try:
+ urlretrieve(url, os.path.join(full_path, filename))
+ locations.append(os.path.join(localpath, filename))
+ except (URLError, IOError) as e:
+ #Python 2.7 throws an IOError rather Than URLError
+ logger.warning("No file could be downloaded from %s\n%s", url, e)
+ return locations
+
+
+def fields2pelican(fields, out_markup, output_path,
+ dircat=False, strip_raw=False, disable_slugs=False,
+ dirpage=False, filename_template=None, filter_author=None,
+ wp_custpost=False, wp_attach=False, attachments=None):
+ for (title, content, filename, date, author, categories, tags,
+ kind, in_markup) in fields:
+ if filter_author and filter_author != author:
+ continue
+ slug = not disable_slugs and filename or None
+
+ if wp_attach and attachments:
+ try:
+ urls = attachments[filename]
+ attached_files = download_attachments(output_path, urls)
+ except KeyError:
+ attached_files = None
+ else:
+ attached_files = None
+
+ ext = get_ext(out_markup, in_markup)
+ if ext == '.md':
+ header = build_markdown_header(title, date, author, categories,
+ tags, slug, attached_files)
+ else:
+ out_markup = "rst"
+ header = build_header(title, date, author, categories,
+ tags, slug, attached_files)
+
+ out_filename = get_out_filename(output_path, filename, ext,
+ kind, dirpage, dircat, categories, wp_custpost)
+ print(out_filename)
+
+ if in_markup in ("html", "wp-html"):
+ html_filename = os.path.join(output_path, filename+'.html')
+
+ with open(html_filename, 'w', encoding='utf-8') as fp:
+ # Replace newlines with paragraphs wrapped with so
+ # HTML is valid before conversion
+ if in_markup == "wp-html":
+ new_content = decode_wp_content(content)
+ else:
+ paragraphs = content.splitlines()
+ paragraphs = ['
{0}
'.format(p) for p in paragraphs]
+ new_content = ''.join(paragraphs)
+
+ fp.write(new_content)
+
+
+ parse_raw = '--parse-raw' if not strip_raw else ''
+ cmd = ('pandoc --normalize {0} --from=html'
+ ' --to={1} -o "{2}" "{3}"').format(
+ parse_raw, out_markup, out_filename, html_filename)
+
+ try:
+ rc = subprocess.call(cmd, shell=True)
+ if rc < 0:
+ error = "Child was terminated by signal %d" % -rc
+ exit(error)
+
+ elif rc > 0:
+ error = "Please, check your Pandoc installation."
+ exit(error)
+ except OSError as e:
+ error = "Pandoc execution failed: %s" % e
+ exit(error)
+
+ os.remove(html_filename)
+
+ with open(out_filename, 'r', encoding='utf-8') as fs:
+ content = fs.read()
+ if out_markup == "markdown":
+ # In markdown, to insert a , end a line with two or more spaces & then a end-of-line
+ content = content.replace("\\\n ", " \n")
+ content = content.replace("\\\n", " \n")
+
+ with open(out_filename, 'w', encoding='utf-8') as fs:
+ fs.write(header + content)
+ if wp_attach and attachments and None in attachments:
+ print("downloading attachments that don't have a parent post")
+ urls = attachments[None]
+ orphan_galleries = download_attachments(output_path, urls)
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Transform feed, WordPress, Tumblr, Dotclear, or Posterous "
+ "files into reST (rst) or Markdown (md) files. Be sure to "
+ "have pandoc installed.",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(dest='input', help='The input file to read')
+ parser.add_argument('--wpfile', action='store_true', dest='wpfile',
+ help='Wordpress XML export')
+ parser.add_argument('--dotclear', action='store_true', dest='dotclear',
+ help='Dotclear export')
+ parser.add_argument('--posterous', action='store_true', dest='posterous',
+ help='Posterous export')
+ parser.add_argument('--tumblr', action='store_true', dest='tumblr',
+ help='Tumblr export')
+ parser.add_argument('--feed', action='store_true', dest='feed',
+ help='Feed to parse')
+ parser.add_argument('-o', '--output', dest='output', default='output',
+ help='Output path')
+ parser.add_argument('-m', '--markup', dest='markup', default='rst',
+ help='Output markup format (supports rst & markdown)')
+ parser.add_argument('--dir-cat', action='store_true', dest='dircat',
+ help='Put files in directories with categories name')
+ parser.add_argument('--dir-page', action='store_true', dest='dirpage',
+ help=('Put files recognised as pages in "pages/" sub-directory'
+ ' (wordpress import only)'))
+ parser.add_argument('--filter-author', dest='author',
+ help='Import only post from the specified author')
+ parser.add_argument('--strip-raw', action='store_true', dest='strip_raw',
+ help="Strip raw HTML code that can't be converted to "
+ "markup such as flash embeds or iframes (wordpress import only)")
+ parser.add_argument('--wp-custpost', action='store_true',
+ dest='wp_custpost',
+ help='Put wordpress custom post types in directories. If used with '
+ '--dir-cat option directories will be created as '
+ '/post_type/category/ (wordpress import only)')
+ parser.add_argument('--wp-attach', action='store_true', dest='wp_attach',
+ help='(wordpress import only) Download files uploaded to wordpress as '
+ 'attachments. Files will be added to posts as a list in the post '
+ 'header. All files will be downloaded, even if '
+ "they aren't associated with a post. Files with be downloaded "
+ 'with their original path inside the output directory. '
+ 'e.g. output/wp-uploads/date/postname/file.jpg '
+ '-- Requires an internet connection --')
+ parser.add_argument('--disable-slugs', action='store_true',
+ dest='disable_slugs',
+ help='Disable storing slugs from imported posts within output. '
+ 'With this disabled, your Pelican URLs may not be consistent '
+ 'with your original posts.')
+ parser.add_argument('-e', '--email', dest='email',
+ help="Email address (posterous import only)")
+ parser.add_argument('-p', '--password', dest='password',
+ help="Password (posterous import only)")
+ parser.add_argument('-b', '--blogname', dest='blogname',
+ help="Blog name (Tumblr import only)")
+
+ args = parser.parse_args()
+
+ input_type = None
+ if args.wpfile:
+ input_type = 'wordpress'
+ elif args.dotclear:
+ input_type = 'dotclear'
+ elif args.posterous:
+ input_type = 'posterous'
+ elif args.tumblr:
+ input_type = 'tumblr'
+ elif args.feed:
+ input_type = 'feed'
+ else:
+ error = "You must provide either --wpfile, --dotclear, --posterous, --tumblr or --feed options"
+ exit(error)
+
+ if not os.path.exists(args.output):
+ try:
+ os.mkdir(args.output)
+ except OSError:
+ error = "Unable to create the output folder: " + args.output
+ exit(error)
+
+ if args.wp_attach and input_type != 'wordpress':
+ error = "You must be importing a wordpress xml to use the --wp-attach option"
+ exit(error)
+
+ if input_type == 'wordpress':
+ fields = wp2fields(args.input, args.wp_custpost or False)
+ elif input_type == 'dotclear':
+ fields = dc2fields(args.input)
+ elif input_type == 'posterous':
+ fields = posterous2fields(args.input, args.email, args.password)
+ elif input_type == 'tumblr':
+ fields = tumblr2fields(args.input, args.blogname)
+ elif input_type == 'feed':
+ fields = feed2fields(args.input)
+
+ if args.wp_attach:
+ attachments = get_attachments(args.input)
+ else:
+ attachments = None
+
+ init() # init logging
+
+ fields2pelican(fields, args.markup, args.output,
+ dircat=args.dircat or False,
+ dirpage=args.dirpage or False,
+ strip_raw=args.strip_raw or False,
+ disable_slugs=args.disable_slugs or False,
+ filter_author=args.author,
+ wp_custpost = args.wp_custpost or False,
+ wp_attach = args.wp_attach or False,
+ attachments = attachments or None)
diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py
new file mode 100755
index 00000000..b24a508c
--- /dev/null
+++ b/pelican/tools/pelican_quickstart.py
@@ -0,0 +1,314 @@
+#!/usr/bin/env python
+
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+
+import os
+import string
+import argparse
+import sys
+import codecs
+
+from pelican import __version__
+
+_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "templates")
+
+_GITHUB_PAGES_BRANCHES = {
+ 'personal': 'master',
+ 'project': 'gh-pages'
+}
+
+CONF = {
+ 'pelican': 'pelican',
+ 'pelicanopts': '',
+ 'basedir': os.curdir,
+ 'ftp_host': 'localhost',
+ 'ftp_user': 'anonymous',
+ 'ftp_target_dir': '/',
+ 'ssh_host': 'localhost',
+ 'ssh_port': 22,
+ 'ssh_user': 'root',
+ 'ssh_target_dir': '/var/www',
+ 's3_bucket': 'my_s3_bucket',
+ 'cloudfiles_username': 'my_rackspace_username',
+ 'cloudfiles_api_key': 'my_rackspace_api_key',
+ 'cloudfiles_container': 'my_cloudfiles_container',
+ 'dropbox_dir': '~/Dropbox/Public/',
+ 'github_pages_branch': _GITHUB_PAGES_BRANCHES['project'],
+ 'default_pagination': 10,
+ 'siteurl': '',
+ 'lang': 'en'
+}
+
+def _input_compat(prompt):
+ if six.PY3:
+ r = input(prompt)
+ else:
+ r = raw_input(prompt)
+ return r
+
+if six.PY3:
+ str_compat = str
+else:
+ str_compat = unicode
+
+def decoding_strings(f):
+ def wrapper(*args, **kwargs):
+ out = f(*args, **kwargs)
+ if isinstance(out, six.string_types) and not six.PY3:
+ # todo: make encoding configurable?
+ if six.PY3:
+ return out
+ else:
+ return out.decode(sys.stdin.encoding)
+ return out
+ return wrapper
+
+
+def get_template(name, as_encoding='utf-8'):
+ template = os.path.join(_TEMPLATES_DIR, "{0}.in".format(name))
+
+ if not os.path.isfile(template):
+ raise RuntimeError("Cannot open {0}".format(template))
+
+ with codecs.open(template, 'r', as_encoding) as fd:
+ line = fd.readline()
+ while line:
+ yield line
+ line = fd.readline()
+ fd.close()
+
+
+@decoding_strings
+def ask(question, answer=str_compat, default=None, l=None):
+ if answer == str_compat:
+ r = ''
+ while True:
+ if default:
+ r = _input_compat('> {0} [{1}] '.format(question, default))
+ else:
+ r = _input_compat('> {0} '.format(question, default))
+
+ r = r.strip()
+
+ if len(r) <= 0:
+ if default:
+ r = default
+ break
+ else:
+ print('You must enter something')
+ else:
+ if l and len(r) != l:
+ print('You must enter a {0} letters long string'.format(l))
+ else:
+ break
+
+ return r
+
+ elif answer == bool:
+ r = None
+ while True:
+ if default is True:
+ r = _input_compat('> {0} (Y/n) '.format(question))
+ elif default is False:
+ r = _input_compat('> {0} (y/N) '.format(question))
+ else:
+ r = _input_compat('> {0} (y/n) '.format(question))
+
+ r = r.strip().lower()
+
+ if r in ('y', 'yes'):
+ r = True
+ break
+ elif r in ('n', 'no'):
+ r = False
+ break
+ elif not r:
+ r = default
+ break
+ else:
+ print("You must answer 'yes' or 'no'")
+ return r
+ elif answer == int:
+ r = None
+ while True:
+ if default:
+ r = _input_compat('> {0} [{1}] '.format(question, default))
+ else:
+ r = _input_compat('> {0} '.format(question))
+
+ r = r.strip()
+
+ if not r:
+ r = default
+ break
+
+ try:
+ r = int(r)
+ break
+ except:
+ print('You must enter an integer')
+ return r
+ else:
+ raise NotImplemented('Argument `answer` must be str_compat, bool, or integer')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="A kickstarter for Pelican",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('-p', '--path', default=os.curdir,
+ help="The path to generate the blog into")
+ parser.add_argument('-t', '--title', metavar="title",
+ help='Set the title of the website')
+ parser.add_argument('-a', '--author', metavar="author",
+ help='Set the author name of the website')
+ parser.add_argument('-l', '--lang', metavar="lang",
+ help='Set the default web site language')
+
+ args = parser.parse_args()
+
+ print('''Welcome to pelican-quickstart v{v}.
+
+This script will help you create a new Pelican-based website.
+
+Please answer the following questions so this script can generate the files
+needed by Pelican.
+
+ '''.format(v=__version__))
+
+ project = os.path.join(
+ os.environ.get('VIRTUAL_ENV', os.curdir), '.project')
+ if os.path.isfile(project):
+ CONF['basedir'] = open(project, 'r').read().rstrip("\n")
+ print('Using project associated with current virtual environment.'
+ 'Will save to:\n%s\n' % CONF['basedir'])
+ else:
+ CONF['basedir'] = os.path.abspath(os.path.expanduser(
+ ask('Where do you want to create your new web site?', answer=str_compat, default=args.path)))
+
+ CONF['sitename'] = ask('What will be the title of this web site?', answer=str_compat, default=args.title)
+ CONF['author'] = ask('Who will be the author of this web site?', answer=str_compat, default=args.author)
+ CONF['lang'] = ask('What will be the default language of this web site?', str_compat, args.lang or CONF['lang'], 2)
+
+ if ask('Do you want to specify a URL prefix? e.g., http://example.com ', answer=bool, default=True):
+ CONF['siteurl'] = ask('What is your URL prefix? (see above example; no trailing slash)', str_compat, CONF['siteurl'])
+
+ CONF['with_pagination'] = ask('Do you want to enable article pagination?', bool, bool(CONF['default_pagination']))
+
+ if CONF['with_pagination']:
+ CONF['default_pagination'] = ask('How many articles per page do you want?', int, CONF['default_pagination'])
+ else:
+ CONF['default_pagination'] = False
+
+ automation = ask('Do you want to generate a Fabfile/Makefile to automate generation and publishing?', bool, True)
+ develop = ask('Do you want an auto-reload & simpleHTTP script to assist with theme and site development?', bool, True)
+
+ if automation:
+ if ask('Do you want to upload your website using FTP?', answer=bool, default=False):
+ CONF['ftp_host'] = ask('What is the hostname of your FTP server?', str_compat, CONF['ftp_host'])
+ CONF['ftp_user'] = ask('What is your username on that server?', str_compat, CONF['ftp_user'])
+ CONF['ftp_target_dir'] = ask('Where do you want to put your web site on that server?', str_compat, CONF['ftp_target_dir'])
+ if ask('Do you want to upload your website using SSH?', answer=bool, default=False):
+ CONF['ssh_host'] = ask('What is the hostname of your SSH server?', str_compat, CONF['ssh_host'])
+ CONF['ssh_port'] = ask('What is the port of your SSH server?', int, CONF['ssh_port'])
+ CONF['ssh_user'] = ask('What is your username on that server?', str_compat, CONF['ssh_user'])
+ CONF['ssh_target_dir'] = ask('Where do you want to put your web site on that server?', str_compat, CONF['ssh_target_dir'])
+ if ask('Do you want to upload your website using Dropbox?', answer=bool, default=False):
+ CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', str_compat, CONF['dropbox_dir'])
+ if ask('Do you want to upload your website using S3?', answer=bool, default=False):
+ CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', str_compat, CONF['s3_bucket'])
+ if ask('Do you want to upload your website using Rackspace Cloud Files?', answer=bool, default=False):
+ CONF['cloudfiles_username'] = ask('What is your Rackspace Cloud username?', str_compat, CONF['cloudfiles_username'])
+ CONF['cloudfiles_api_key'] = ask('What is your Rackspace Cloud API key?', str_compat, CONF['cloudfiles_api_key'])
+ CONF['cloudfiles_container'] = ask('What is the name of your Cloud Files container?', str_compat, CONF['cloudfiles_container'])
+ if ask('Do you want to upload your website using GitHub Pages?', answer=bool, default=False):
+ if ask('Is this your personal page (username.github.io)?', answer=bool, default=False):
+ CONF['github_pages_branch'] = _GITHUB_PAGES_BRANCHES['personal']
+ else:
+ CONF['github_pages_branch'] = _GITHUB_PAGES_BRANCHES['project']
+
+ try:
+ os.makedirs(os.path.join(CONF['basedir'], 'content'))
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ try:
+ os.makedirs(os.path.join(CONF['basedir'], 'output'))
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ try:
+ with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w', 'utf-8') as fd:
+ conf_python = dict()
+ for key, value in CONF.items():
+ conf_python[key] = repr(value)
+
+ for line in get_template('pelicanconf.py'):
+ template = string.Template(line)
+ fd.write(template.safe_substitute(conf_python))
+ fd.close()
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ try:
+ with codecs.open(os.path.join(CONF['basedir'], 'publishconf.py'), 'w', 'utf-8') as fd:
+ for line in get_template('publishconf.py'):
+ template = string.Template(line)
+ fd.write(template.safe_substitute(CONF))
+ fd.close()
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ if automation:
+ try:
+ with codecs.open(os.path.join(CONF['basedir'], 'fabfile.py'), 'w', 'utf-8') as fd:
+ for line in get_template('fabfile.py'):
+ template = string.Template(line)
+ fd.write(template.safe_substitute(CONF))
+ fd.close()
+ except OSError as e:
+ print('Error: {0}'.format(e))
+ try:
+ with codecs.open(os.path.join(CONF['basedir'], 'Makefile'), 'w', 'utf-8') as fd:
+ mkfile_template_name = 'Makefile'
+ py_v = 'PY?=python'
+ if six.PY3:
+ py_v = 'PY?=python3'
+ template = string.Template(py_v)
+ fd.write(template.safe_substitute(CONF))
+ fd.write('\n')
+ for line in get_template(mkfile_template_name):
+ template = string.Template(line)
+ fd.write(template.safe_substitute(CONF))
+ fd.close()
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ if develop:
+ conf_shell = dict()
+ for key, value in CONF.items():
+ if isinstance(value, six.string_types) and ' ' in value:
+ value = '"' + value.replace('"', '\\"') + '"'
+ conf_shell[key] = value
+ try:
+ with codecs.open(os.path.join(CONF['basedir'], 'develop_server.sh'), 'w', 'utf-8') as fd:
+ lines = list(get_template('develop_server.sh'))
+ py_v = 'PY=${PY:-python}\n'
+ if six.PY3:
+ py_v = 'PY=${PY:-python3}\n'
+ lines = lines[:4] + [py_v] + lines[4:]
+ for line in lines:
+ template = string.Template(line)
+ fd.write(template.safe_substitute(conf_shell))
+ fd.close()
+ os.chmod((os.path.join(CONF['basedir'], 'develop_server.sh')), 493) # mode 0o755
+ except OSError as e:
+ print('Error: {0}'.format(e))
+
+ print('Done. Your new project is available at %s' % CONF['basedir'])
+
+if __name__ == "__main__":
+ main()
diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py
new file mode 100755
index 00000000..8d71535d
--- /dev/null
+++ b/pelican/tools/pelican_themes.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+
+import six
+
+import argparse
+import os
+import shutil
+import sys
+
+try:
+ import pelican
+except:
+ err('Cannot import pelican.\nYou must install Pelican in order to run this script.', -1)
+
+
+global _THEMES_PATH
+_THEMES_PATH = os.path.join(
+ os.path.dirname(
+ os.path.abspath(
+ pelican.__file__
+ )
+ ),
+ 'themes'
+)
+
+__version__ = '0.2'
+_BUILTIN_THEMES = ['simple', 'notmyidea']
+
+
+def err(msg, die=None):
+ """Print an error message and exits if an exit code is given"""
+ sys.stderr.write(msg + '\n')
+ if die:
+ sys.exit((die if type(die) is int else 1))
+
+
+def main():
+ """Main function"""
+
+ parser = argparse.ArgumentParser(description="""Install themes for Pelican""")
+
+ excl= parser.add_mutually_exclusive_group()
+ excl.add_argument('-l', '--list', dest='action', action="store_const", const='list',
+ help="Show the themes already installed and exit")
+ excl.add_argument('-p', '--path', dest='action', action="store_const", const='path',
+ help="Show the themes path and exit")
+ excl.add_argument('-V', '--version', action='version', version='pelican-themes v{0}'.format(__version__),
+ help='Print the version of this script')
+
+
+ parser.add_argument('-i', '--install', dest='to_install', nargs='+', metavar="theme path",
+ help='The themes to install')
+ parser.add_argument('-r', '--remove', dest='to_remove', nargs='+', metavar="theme name",
+ help='The themes to remove')
+ parser.add_argument('-U', '--upgrade', dest='to_upgrade', nargs='+',
+ metavar="theme path", help='The themes to upgrade')
+ parser.add_argument('-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path",
+ help="Same as `--install', but create a symbolic link instead of copying the theme. Useful for theme development")
+ parser.add_argument('-c', '--clean', dest='clean', action="store_true",
+ help="Remove the broken symbolic links of the theme path")
+
+
+ parser.add_argument('-v', '--verbose', dest='verbose', action="store_true",
+ help="Verbose output")
+
+
+ args = parser.parse_args()
+
+ to_install = args.to_install or args.to_upgrade
+ to_sym = args.to_symlink or args.clean
+
+
+ if args.action:
+ if args.action is 'list':
+ list_themes(args.verbose)
+ elif args.action is 'path':
+ print(_THEMES_PATH)
+ elif to_install or args.to_remove or to_sym:
+ if args.to_remove:
+ if args.verbose:
+ print('Removing themes...')
+
+ for i in args.to_remove:
+ remove(i, v=args.verbose)
+
+ if args.to_install:
+ if args.verbose:
+ print('Installing themes...')
+
+ for i in args.to_install:
+ install(i, v=args.verbose)
+
+ if args.to_upgrade:
+ if args.verbose:
+ print('Upgrading themes...')
+
+ for i in args.to_upgrade:
+ install(i, v=args.verbose, u=True)
+
+ if args.to_symlink:
+ if args.verbose:
+ print('Linking themes...')
+
+ for i in args.to_symlink:
+ symlink(i, v=args.verbose)
+
+ if args.clean:
+ if args.verbose:
+ print('Cleaning the themes directory...')
+
+ clean(v=args.verbose)
+ else:
+ print('No argument given... exiting.')
+
+
+def themes():
+ """Returns the list of the themes"""
+ for i in os.listdir(_THEMES_PATH):
+ e = os.path.join(_THEMES_PATH, i)
+
+ if os.path.isdir(e):
+ if os.path.islink(e):
+ yield (e, os.readlink(e))
+ else:
+ yield (e, None)
+
+
+def list_themes(v=False):
+ """Display the list of the themes"""
+ for t, l in themes():
+ if not v:
+ t = os.path.basename(t)
+ if l:
+ if v:
+ print(t + (" (symbolic link to `" + l + "')"))
+ else:
+ print(t + '@')
+ else:
+ print(t)
+
+
+def remove(theme_name, v=False):
+ """Removes a theme"""
+
+ theme_name = theme_name.replace('/','')
+ target = os.path.join(_THEMES_PATH, theme_name)
+
+ if theme_name in _BUILTIN_THEMES:
+ err(theme_name + ' is a builtin theme.\nYou cannot remove a builtin theme with this script, remove it by hand if you want.')
+ elif os.path.islink(target):
+ if v:
+ print('Removing link `' + target + "'")
+ os.remove(target)
+ elif os.path.isdir(target):
+ if v:
+ print('Removing directory `' + target + "'")
+ shutil.rmtree(target)
+ elif os.path.exists(target):
+ err(target + ' : not a valid theme')
+ else:
+ err(target + ' : no such file or directory')
+
+
+def install(path, v=False, u=False):
+ """Installs a theme"""
+ if not os.path.exists(path):
+ err(path + ' : no such file or directory')
+ elif not os.path.isdir(path):
+ err(path + ' : not a directory')
+ else:
+ theme_name = os.path.basename(os.path.normpath(path))
+ theme_path = os.path.join(_THEMES_PATH, theme_name)
+ exists = os.path.exists(theme_path)
+ if exists and not u:
+ err(path + ' : already exists')
+ elif exists and u:
+ remove(theme_name, v)
+ install(path, v)
+ else:
+ if v:
+ print("Copying `{p}' to `{t}' ...".format(p=path, t=theme_path))
+ try:
+ shutil.copytree(path, theme_path)
+
+ try:
+ if os.name == 'posix':
+ for root, dirs, files in os.walk(theme_path):
+ for d in dirs:
+ dname = os.path.join(root, d)
+ os.chmod(dname, 493) # 0o755
+ for f in files:
+ fname = os.path.join(root, f)
+ os.chmod(fname, 420) # 0o644
+ except OSError as e:
+ err("Cannot change permissions of files or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), die=False)
+ except Exception as e:
+ err("Cannot copy `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e)))
+
+
+def symlink(path, v=False):
+ """Symbolically link a theme"""
+ if not os.path.exists(path):
+ err(path + ' : no such file or directory')
+ elif not os.path.isdir(path):
+ err(path + ' : not a directory')
+ else:
+ theme_name = os.path.basename(os.path.normpath(path))
+ theme_path = os.path.join(_THEMES_PATH, theme_name)
+ if os.path.exists(theme_path):
+ err(path + ' : already exists')
+ else:
+ if v:
+ print("Linking `{p}' to `{t}' ...".format(p=path, t=theme_path))
+ try:
+ os.symlink(path, theme_path)
+ except Exception as e:
+ err("Cannot link `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e)))
+
+
+def is_broken_link(path):
+ """Returns True if the path given as is a broken symlink"""
+ path = os.readlink(path)
+ return not os.path.exists(path)
+
+
+def clean(v=False):
+ """Removes the broken symbolic links"""
+ c=0
+ for path in os.listdir(_THEMES_PATH):
+ path = os.path.join(_THEMES_PATH, path)
+ if os.path.islink(path):
+ if is_broken_link(path):
+ if v:
+ print('Removing {0}'.format(path))
+ try:
+ os.remove(path)
+ except OSError as e:
+ print('Error: cannot remove {0}'.format(path))
+ else:
+ c+=1
+
+ print("\nRemoved {0} broken links".format(c))
diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in
new file mode 100644
index 00000000..8534595e
--- /dev/null
+++ b/pelican/tools/templates/Makefile.in
@@ -0,0 +1,109 @@
+PELICAN?=$pelican
+PELICANOPTS=$pelicanopts
+
+BASEDIR=$$(CURDIR)
+INPUTDIR=$$(BASEDIR)/content
+OUTPUTDIR=$$(BASEDIR)/output
+CONFFILE=$$(BASEDIR)/pelicanconf.py
+PUBLISHCONF=$$(BASEDIR)/publishconf.py
+
+FTP_HOST=$ftp_host
+FTP_USER=$ftp_user
+FTP_TARGET_DIR=$ftp_target_dir
+
+SSH_HOST=$ssh_host
+SSH_PORT=$ssh_port
+SSH_USER=$ssh_user
+SSH_TARGET_DIR=$ssh_target_dir
+
+S3_BUCKET=$s3_bucket
+
+CLOUDFILES_USERNAME=$cloudfiles_username
+CLOUDFILES_API_KEY=$cloudfiles_api_key
+CLOUDFILES_CONTAINER=$cloudfiles_container
+
+DROPBOX_DIR=$dropbox_dir
+
+GITHUB_PAGES_BRANCH=$github_pages_branch
+
+DEBUG ?= 0
+ifeq ($(DEBUG), 1)
+ PELICANOPTS += -D
+endif
+
+help:
+ @echo 'Makefile for a pelican Web site '
+ @echo ' '
+ @echo 'Usage: '
+ @echo ' make html (re)generate the web site '
+ @echo ' make clean remove the generated files '
+ @echo ' make regenerate regenerate files upon modification '
+ @echo ' make publish generate using production settings '
+ @echo ' make serve [PORT=8000] serve site at http://localhost:8000'
+ @echo ' make devserver [PORT=8000] start/restart develop_server.sh '
+ @echo ' make stopserver stop local server '
+ @echo ' make ssh_upload upload the web site via SSH '
+ @echo ' make rsync_upload upload the web site via rsync+ssh '
+ @echo ' make dropbox_upload upload the web site via Dropbox '
+ @echo ' make ftp_upload upload the web site via FTP '
+ @echo ' make s3_upload upload the web site via S3 '
+ @echo ' make cf_upload upload the web site via Cloud Files'
+ @echo ' make github upload the web site via gh-pages '
+ @echo ' '
+ @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html'
+ @echo ' '
+
+html:
+ $$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
+
+clean:
+ [ ! -d $$(OUTPUTDIR) ] || rm -rf $$(OUTPUTDIR)
+
+regenerate:
+ $$(PELICAN) -r $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS)
+
+serve:
+ifdef PORT
+ cd $$(OUTPUTDIR) && $(PY) -m pelican.server $$(PORT)
+else
+ cd $$(OUTPUTDIR) && $(PY) -m pelican.server
+endif
+
+devserver:
+ifdef PORT
+ $$(BASEDIR)/develop_server.sh restart $$(PORT)
+else
+ $$(BASEDIR)/develop_server.sh restart
+endif
+
+stopserver:
+ kill -9 `cat pelican.pid`
+ kill -9 `cat srv.pid`
+ @echo 'Stopped Pelican and SimpleHTTPServer processes running in background.'
+
+publish:
+ $$(PELICAN) $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(PUBLISHCONF) $$(PELICANOPTS)
+
+ssh_upload: publish
+ scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR)
+
+rsync_upload: publish
+ rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude
+
+dropbox_upload: publish
+ cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR)
+
+ftp_upload: publish
+ lftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit"
+
+s3_upload: publish
+ s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type
+
+cf_upload: publish
+ cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) .
+
+github: publish
+ ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) $$(OUTPUTDIR)
+ git push origin $(GITHUB_PAGES_BRANCH)
+
+.PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github
diff --git a/pelican/tools/templates/develop_server.sh.in b/pelican/tools/templates/develop_server.sh.in
new file mode 100755
index 00000000..732069c2
--- /dev/null
+++ b/pelican/tools/templates/develop_server.sh.in
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+##
+# This section should match your Makefile
+##
+PELICAN=$${PELICAN:-$pelican}
+PELICANOPTS=$pelicanopts
+
+BASEDIR=$$(pwd)
+INPUTDIR=$$BASEDIR/content
+OUTPUTDIR=$$BASEDIR/output
+CONFFILE=$$BASEDIR/pelicanconf.py
+
+###
+# Don't change stuff below here unless you are sure
+###
+
+SRV_PID=$$BASEDIR/srv.pid
+PELICAN_PID=$$BASEDIR/pelican.pid
+
+function usage(){
+ echo "usage: $$0 (stop) (start) (restart) [port]"
+ echo "This starts Pelican in debug and reload mode and then launches"
+ echo "an HTTP server to help site development. It doesn't read"
+ echo "your Pelican settings, so if you edit any paths in your Makefile"
+ echo "you will need to edit your settings as well."
+ exit 3
+}
+
+function alive() {
+ kill -0 $$1 >/dev/null 2>&1
+}
+
+function shut_down(){
+ PID=$$(cat $$SRV_PID)
+ if [[ $$? -eq 0 ]]; then
+ if alive $PID; then
+ echo "Stopping HTTP server"
+ kill $$PID
+ else
+ echo "Stale PID, deleting"
+ fi
+ rm $$SRV_PID
+ else
+ echo "HTTP server PIDFile not found"
+ fi
+
+ PID=$$(cat $$PELICAN_PID)
+ if [[ $$? -eq 0 ]]; then
+ if alive $$PID; then
+ echo "Killing Pelican"
+ kill $$PID
+ else
+ echo "Stale PID, deleting"
+ fi
+ rm $$PELICAN_PID
+ else
+ echo "Pelican PIDFile not found"
+ fi
+}
+
+function start_up(){
+ local port=$$1
+ echo "Starting up Pelican and HTTP server"
+ shift
+ $$PELICAN --debug --autoreload -r $$INPUTDIR -o $$OUTPUTDIR -s $$CONFFILE $$PELICANOPTS &
+ pelican_pid=$$!
+ echo $$pelican_pid > $$PELICAN_PID
+ cd $$OUTPUTDIR
+ $PY -m pelican.server $$port &
+ srv_pid=$$!
+ echo $$srv_pid > $$SRV_PID
+ cd $$BASEDIR
+ sleep 1
+ if ! alive $$pelican_pid ; then
+ echo "Pelican didn't start. Is the Pelican package installed?"
+ return 1
+ elif ! alive $$srv_pid ; then
+ echo "The HTTP server didn't start. Is there another service using port" $$port "?"
+ return 1
+ fi
+ echo 'Pelican and HTTP server processes now running in background.'
+}
+
+###
+# MAIN
+###
+[[ ($$# -eq 0) || ($$# -gt 2) ]] && usage
+port=''
+[[ $$# -eq 2 ]] && port=$$2
+
+if [[ $$1 == "stop" ]]; then
+ shut_down
+elif [[ $$1 == "restart" ]]; then
+ shut_down
+ start_up $$port
+elif [[ $$1 == "start" ]]; then
+ if ! start_up $$port; then
+ shut_down
+ fi
+else
+ usage
+fi
diff --git a/pelican/tools/templates/fabfile.py.in b/pelican/tools/templates/fabfile.py.in
new file mode 100644
index 00000000..e693bb48
--- /dev/null
+++ b/pelican/tools/templates/fabfile.py.in
@@ -0,0 +1,73 @@
+from fabric.api import *
+import fabric.contrib.project as project
+import os
+import sys
+import SimpleHTTPServer
+import SocketServer
+
+# Local path configuration (can be absolute or relative to fabfile)
+env.deploy_path = 'output'
+DEPLOY_PATH = env.deploy_path
+
+# Remote server configuration
+production = '$ssh_user@$ssh_host:$ssh_port'
+dest_path = '$ssh_target_dir'
+
+# Rackspace Cloud Files configuration settings
+env.cloudfiles_username = '$cloudfiles_username'
+env.cloudfiles_api_key = '$cloudfiles_api_key'
+env.cloudfiles_container = '$cloudfiles_container'
+
+
+def clean():
+ if os.path.isdir(DEPLOY_PATH):
+ local('rm -rf {deploy_path}'.format(**env))
+ local('mkdir {deploy_path}'.format(**env))
+
+def build():
+ local('pelican -s pelicanconf.py')
+
+def rebuild():
+ clean()
+ build()
+
+def regenerate():
+ local('pelican -r -s pelicanconf.py')
+
+def serve():
+ os.chdir(env.deploy_path)
+
+ PORT = 8000
+ class AddressReuseTCPServer(SocketServer.TCPServer):
+ allow_reuse_address = True
+
+ server = AddressReuseTCPServer(('', PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
+
+ sys.stderr.write('Serving on port {0} ...\n'.format(PORT))
+ server.serve_forever()
+
+def reserve():
+ build()
+ serve()
+
+def preview():
+ local('pelican -s publishconf.py')
+
+def cf_upload():
+ rebuild()
+ local('cd {deploy_path} && '
+ 'swift -v -A https://auth.api.rackspacecloud.com/v1.0 '
+ '-U {cloudfiles_username} '
+ '-K {cloudfiles_api_key} '
+ 'upload -c {cloudfiles_container} .'.format(**env))
+
+@hosts(production)
+def publish():
+ local('pelican -s publishconf.py')
+ project.rsync_project(
+ remote_dir=dest_path,
+ exclude=".DS_Store",
+ local_dir=DEPLOY_PATH.rstrip('/') + '/',
+ delete=True,
+ extra_opts='-c',
+ )
diff --git a/pelican/tools/templates/pelicanconf.py.in b/pelican/tools/templates/pelicanconf.py.in
new file mode 100644
index 00000000..e9570c4a
--- /dev/null
+++ b/pelican/tools/templates/pelicanconf.py.in
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+from __future__ import unicode_literals
+
+AUTHOR = $author
+SITENAME = $sitename
+SITEURL = ''
+
+PATH = 'content'
+
+TIMEZONE = 'Europe/Paris'
+
+DEFAULT_LANG = $lang
+
+# Feed generation is usually not desired when developing
+FEED_ALL_ATOM = None
+CATEGORY_FEED_ATOM = None
+TRANSLATION_FEED_ATOM = None
+AUTHOR_FEED_ATOM = None
+AUTHOR_FEED_RSS = None
+
+# Blogroll
+LINKS = (('Pelican', 'http://getpelican.com/'),
+ ('Python.org', 'http://python.org/'),
+ ('Jinja2', 'http://jinja.pocoo.org/'),
+ ('You can modify those links in your config file', '#'),)
+
+# Social widget
+SOCIAL = (('You can add links in your config file', '#'),
+ ('Another social link', '#'),)
+
+DEFAULT_PAGINATION = $default_pagination
+
+# Uncomment following line if you want document-relative URLs when developing
+#RELATIVE_URLS = True
diff --git a/pelican/tools/templates/publishconf.py.in b/pelican/tools/templates/publishconf.py.in
new file mode 100755
index 00000000..d1ed994d
--- /dev/null
+++ b/pelican/tools/templates/publishconf.py.in
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+from __future__ import unicode_literals
+
+# This file is only used if you use `make publish` or
+# explicitly specify it as your config file.
+
+import os
+import sys
+sys.path.append(os.curdir)
+from pelicanconf import *
+
+SITEURL = '$siteurl'
+RELATIVE_URLS = False
+
+FEED_ALL_ATOM = 'feeds/all.atom.xml'
+CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
+
+DELETE_OUTPUT_DIRECTORY = True
+
+# Following items are often useful when publishing
+
+#DISQUS_SITENAME = ""
+#GOOGLE_ANALYTICS = ""
diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py
new file mode 100644
index 00000000..f92d1963
--- /dev/null
+++ b/pelican/urlwrappers.py
@@ -0,0 +1,97 @@
+import os
+import functools
+import logging
+
+import six
+
+from pelican.utils import (slugify, python_2_unicode_compatible)
+
+logger = logging.getLogger(__name__)
+
+
+@python_2_unicode_compatible
+@functools.total_ordering
+class URLWrapper(object):
+ def __init__(self, name, settings):
+ # next 2 lines are redundant with the setter of the name property
+ # but are here for clarity
+ self.settings = settings
+ self._name = name
+ self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ()))
+ self.name = name
+
+ @property
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, name):
+ self._name = name
+ self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ()))
+
+ def as_dict(self):
+ d = self.__dict__
+ d['name'] = self.name
+ return d
+
+ def __hash__(self):
+ return hash(self.slug)
+
+ def _key(self):
+ return self.slug
+
+ def _normalize_key(self, key):
+ subs = self.settings.get('SLUG_SUBSTITUTIONS', ())
+ return six.text_type(slugify(key, subs))
+
+ def __eq__(self, other):
+ return self._key() == self._normalize_key(other)
+
+ def __ne__(self, other):
+ return self._key() != self._normalize_key(other)
+
+ def __lt__(self, other):
+ return self._key() < self._normalize_key(other)
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return '<{} {}>'.format(type(self).__name__, str(self))
+
+ def _from_settings(self, key, get_page_name=False):
+ """Returns URL information as defined in settings.
+
+ When get_page_name=True returns URL without anything after {slug} e.g.
+ if in settings: CATEGORY_URL="cat/{slug}.html" this returns
+ "cat/{slug}" Useful for pagination.
+
+ """
+ setting = "%s_%s" % (self.__class__.__name__.upper(), key)
+ value = self.settings[setting]
+ if not isinstance(value, six.string_types):
+ logger.warning('%s is set to %s', (setting, value))
+ return value
+ else:
+ if get_page_name:
+ return os.path.splitext(value)[0].format(**self.as_dict())
+ else:
+ return value.format(**self.as_dict())
+
+ page_name = property(functools.partial(_from_settings, key='URL',
+ get_page_name=True))
+ url = property(functools.partial(_from_settings, key='URL'))
+ save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
+
+
+class Category(URLWrapper):
+ pass
+
+
+class Tag(URLWrapper):
+ def __init__(self, name, *args, **kwargs):
+ super(Tag, self).__init__(name.strip(), *args, **kwargs)
+
+
+class Author(URLWrapper):
+ pass
diff --git a/pelican/utils.py b/pelican/utils.py
new file mode 100644
index 00000000..1f8cb0a6
--- /dev/null
+++ b/pelican/utils.py
@@ -0,0 +1,753 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
+import six
+
+import codecs
+import errno
+import fnmatch
+import locale
+import logging
+import os
+import pytz
+import re
+import shutil
+import traceback
+import pickle
+import hashlib
+import datetime
+
+from collections import Hashable
+from contextlib import contextmanager
+import dateutil.parser
+from functools import partial
+from itertools import groupby
+from jinja2 import Markup
+from operator import attrgetter
+
+logger = logging.getLogger(__name__)
+
+
+def strftime(date, date_format):
+ '''
+ Replacement for built-in strftime
+
+ This is necessary because of the way Py2 handles date format strings.
+ Specifically, Py2 strftime takes a bytestring. In the case of text output
+ (e.g. %b, %a, etc), the output is encoded with an encoding defined by
+ locale.LC_TIME. Things get messy if the formatting string has chars that
+ are not valid in LC_TIME defined encoding.
+
+ This works by 'grabbing' possible format strings (those starting with %),
+ formatting them with the date, (if necessary) decoding the output and
+ replacing formatted output back.
+ '''
+
+ c89_directives = 'aAbBcdfHIjmMpSUwWxXyYzZ%'
+ strip_zeros = lambda x: x.lstrip('0') or '0'
+
+ # grab candidate format options
+ format_options = '%[-]?.'
+ candidates = re.findall(format_options, date_format)
+
+ # replace candidates with placeholders for later % formatting
+ template = re.sub(format_options, '%s', date_format)
+
+ # we need to convert formatted dates back to unicode in Py2
+ # LC_TIME determines the encoding for built-in strftime outputs
+ lang_code, enc = locale.getlocale(locale.LC_TIME)
+
+ formatted_candidates = []
+ for candidate in candidates:
+ # test for valid C89 directives only
+ if candidate[-1] in c89_directives:
+ # check for '-' prefix
+ if len(candidate) == 3:
+ # '-' prefix
+ candidate = '%{}'.format(candidate[-1])
+ conversion = strip_zeros
+ else:
+ conversion = None
+
+ # format date
+ if isinstance(date, SafeDatetime):
+ formatted = date.strftime(candidate, safe=False)
+ else:
+ formatted = date.strftime(candidate)
+
+ # convert Py2 result to unicode
+ if not six.PY3 and enc is not None:
+ formatted = formatted.decode(enc)
+
+ # strip zeros if '-' prefix is used
+ if conversion:
+ formatted = conversion(formatted)
+ else:
+ formatted = candidate
+ formatted_candidates.append(formatted)
+
+ # put formatted candidates back and return
+ return template % tuple(formatted_candidates)
+
+
+class SafeDatetime(datetime.datetime):
+ '''Subclass of datetime that works with utf-8 format strings on PY2'''
+
+ def strftime(self, fmt, safe=True):
+ '''Uses our custom strftime if supposed to be *safe*'''
+ if safe:
+ return strftime(self, fmt)
+ else:
+ return super(SafeDatetime, self).strftime(fmt)
+
+
+class DateFormatter(object):
+ '''A date formatter object used as a jinja filter
+
+ Uses the `strftime` implementation and makes sure jinja uses the locale
+ defined in LOCALE setting
+ '''
+
+ def __init__(self):
+ self.locale = locale.setlocale(locale.LC_TIME)
+
+ def __call__(self, date, date_format):
+ old_lc_time = locale.setlocale(locale.LC_TIME)
+ old_lc_ctype = locale.setlocale(locale.LC_CTYPE)
+
+ locale.setlocale(locale.LC_TIME, self.locale)
+ # on OSX, encoding from LC_CTYPE determines the unicode output in PY3
+ # make sure it's same as LC_TIME
+ locale.setlocale(locale.LC_CTYPE, self.locale)
+
+ formatted = strftime(date, date_format)
+
+ locale.setlocale(locale.LC_TIME, old_lc_time)
+ locale.setlocale(locale.LC_CTYPE, old_lc_ctype)
+ return formatted
+
+
+def python_2_unicode_compatible(klass):
+ """
+ A decorator that defines __unicode__ and __str__ methods under Python 2.
+ Under Python 3 it does nothing.
+
+ To support Python 2 and 3 with a single code base, define a __str__ method
+ returning text and apply this decorator to the class.
+
+ From django.utils.encoding.
+ """
+ if not six.PY3:
+ klass.__unicode__ = klass.__str__
+ klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
+ return klass
+
+
+class memoized(object):
+ """Function decorator to cache return values.
+
+ If called later with the same arguments, the cached value is returned
+ (not reevaluated).
+
+ """
+ def __init__(self, func):
+ self.func = func
+ self.cache = {}
+
+ def __call__(self, *args):
+ if not isinstance(args, Hashable):
+ # uncacheable. a list, for instance.
+ # better to not cache than blow up.
+ return self.func(*args)
+ if args in self.cache:
+ return self.cache[args]
+ else:
+ value = self.func(*args)
+ self.cache[args] = value
+ return value
+
+ def __repr__(self):
+ return self.func.__doc__
+
+ def __get__(self, obj, objtype):
+ '''Support instance methods.'''
+ return partial(self.__call__, obj)
+
+
+def deprecated_attribute(old, new, since=None, remove=None, doc=None):
+ """Attribute deprecation decorator for gentle upgrades
+
+ For example:
+
+ class MyClass (object):
+ @deprecated_attribute(
+ old='abc', new='xyz', since=(3, 2, 0), remove=(4, 1, 3))
+ def abc(): return None
+
+ def __init__(self):
+ xyz = 5
+
+ Note that the decorator needs a dummy method to attach to, but the
+ content of the dummy method is ignored.
+ """
+ def _warn():
+ version = '.'.join(six.text_type(x) for x in since)
+ message = ['{} has been deprecated since {}'.format(old, version)]
+ if remove:
+ version = '.'.join(six.text_type(x) for x in remove)
+ message.append(
+ ' and will be removed by version {}'.format(version))
+ message.append('. Use {} instead.'.format(new))
+ logger.warning(''.join(message))
+ logger.debug(''.join(
+ six.text_type(x) for x in traceback.format_stack()))
+
+ def fget(self):
+ _warn()
+ return getattr(self, new)
+
+ def fset(self, value):
+ _warn()
+ setattr(self, new, value)
+
+ def decorator(dummy):
+ return property(fget=fget, fset=fset, doc=doc)
+
+ return decorator
+
+
+def get_date(string):
+ """Return a datetime object from a string.
+
+ If no format matches the given date, raise a ValueError.
+ """
+ string = re.sub(' +', ' ', string)
+ default = SafeDatetime.now().replace(hour=0, minute=0,
+ second=0, microsecond=0)
+ try:
+ return dateutil.parser.parse(string, default=default)
+ except (TypeError, ValueError):
+ raise ValueError('{0!r} is not a valid date'.format(string))
+
+
+@contextmanager
+def pelican_open(filename):
+ """Open a file and return its content"""
+
+ with codecs.open(filename, encoding='utf-8') as infile:
+ content = infile.read()
+ if content[0] == codecs.BOM_UTF8.decode('utf8'):
+ content = content[1:]
+ yield content
+
+
+def slugify(value, substitutions=()):
+ """
+ Normalizes string, converts to lowercase, removes non-alpha characters,
+ and converts spaces to hyphens.
+
+ Took from Django sources.
+ """
+ # TODO Maybe steal again from current Django 1.5dev
+ value = Markup(value).striptags()
+ # value must be unicode per se
+ import unicodedata
+ from unidecode import unidecode
+ # unidecode returns str in Py2 and 3, so in Py2 we have to make
+ # it unicode again
+ value = unidecode(value)
+ if isinstance(value, six.binary_type):
+ value = value.decode('ascii')
+ # still unicode
+ value = unicodedata.normalize('NFKD', value).lower()
+ for src, dst in substitutions:
+ value = value.replace(src.lower(), dst.lower())
+ value = re.sub('[^\w\s-]', '', value).strip()
+ value = re.sub('[-\s]+', '-', value)
+ # we want only ASCII chars
+ value = value.encode('ascii', 'ignore')
+ # but Pelican should generally use only unicode
+ return value.decode('ascii')
+
+
+def copy(source, destination):
+ """Recursively copy source into destination.
+
+ If source is a file, destination has to be a file as well.
+
+ The function is able to copy either files or directories.
+
+ :param source: the source file or directory
+ :param destination: the destination file or directory
+ """
+
+ source_ = os.path.abspath(os.path.expanduser(source))
+ destination_ = os.path.abspath(os.path.expanduser(destination))
+
+ if not os.path.exists(destination_) and not os.path.isfile(source_):
+ os.makedirs(destination_)
+
+ def recurse(source, destination):
+ for entry in os.listdir(source):
+ entry_path = os.path.join(source, entry)
+ if os.path.isdir(entry_path):
+ entry_dest = os.path.join(destination, entry)
+ if os.path.exists(entry_dest):
+ if not os.path.isdir(entry_dest):
+ raise IOError('Failed to copy {0} a directory.'
+ .format(entry_dest))
+ recurse(entry_path, entry_dest)
+ else:
+ shutil.copytree(entry_path, entry_dest)
+ else:
+ shutil.copy2(entry_path, destination)
+
+
+ if os.path.isdir(source_):
+ recurse(source_, destination_)
+
+ elif os.path.isfile(source_):
+ dest_dir = os.path.dirname(destination_)
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+ shutil.copy2(source_, destination_)
+ logger.info('Copying %s to %s', source_, destination_)
+ else:
+ logger.warning('Skipped copy %s to %s', source_, destination_)
+
+
+def clean_output_dir(path, retention):
+ """Remove all files from output directory except those in retention list"""
+
+ if not os.path.exists(path):
+ logger.debug("Directory already removed: %s", path)
+ return
+
+ if not os.path.isdir(path):
+ try:
+ os.remove(path)
+ except Exception as e:
+ logger.error("Unable to delete file %s; %s", path, e)
+ return
+
+ # remove existing content from output folder unless in retention list
+ for filename in os.listdir(path):
+ file = os.path.join(path, filename)
+ if any(filename == retain for retain in retention):
+ logger.debug("Skipping deletion; %s is on retention list: %s",
+ filename, file)
+ elif os.path.isdir(file):
+ try:
+ shutil.rmtree(file)
+ logger.debug("Deleted directory %s", file)
+ except Exception as e:
+ logger.error("Unable to delete directory %s; %s",
+ file, e)
+ elif os.path.isfile(file) or os.path.islink(file):
+ try:
+ os.remove(file)
+ logger.debug("Deleted file/link %s", file)
+ except Exception as e:
+ logger.error("Unable to delete file %s; %s", file, e)
+ else:
+ logger.error("Unable to delete %s, file type unknown", file)
+
+
+def get_relative_path(path):
+ """Return the relative path from the given path to the root path."""
+ components = split_all(path)
+ if len(components) <= 1:
+ return os.curdir
+ else:
+ parents = [os.pardir] * (len(components) - 1)
+ return os.path.join(*parents)
+
+
+def path_to_url(path):
+ """Return the URL corresponding to a given path."""
+ if os.sep == '/':
+ return path
+ else:
+ return '/'.join(split_all(path))
+
+
+def truncate_html_words(s, num, end_text='...'):
+ """Truncates HTML to a certain number of words.
+
+ (not counting tags and comments). Closes opened tags if they were correctly
+ closed in the given html. Takes an optional argument of what should be used
+ to notify that the string has been truncated, defaulting to ellipsis (...).
+
+ Newlines in the HTML are preserved. (From the django framework).
+ """
+ length = int(num)
+ if length <= 0:
+ return ''
+ html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area',
+ 'hr', 'input')
+
+ # Set up regular expressions
+ re_words = re.compile(r'&.*?;|<.*?>|(\w[\w-]*)', re.U)
+ re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
+ # Count non-HTML words and keep note of open tags
+ pos = 0
+ end_text_pos = 0
+ words = 0
+ open_tags = []
+ while words <= length:
+ m = re_words.search(s, pos)
+ if not m:
+ # Checked through whole string
+ break
+ pos = m.end(0)
+ if m.group(1):
+ # It's an actual non-HTML word
+ words += 1
+ if words == length:
+ end_text_pos = pos
+ continue
+ # Check for tag
+ tag = re_tag.match(m.group(0))
+ if not tag or end_text_pos:
+ # Don't worry about non tags or tags after our truncate point
+ continue
+ closing_tag, tagname, self_closing = tag.groups()
+ tagname = tagname.lower() # Element names are always case-insensitive
+ if self_closing or tagname in html4_singlets:
+ pass
+ elif closing_tag:
+ # Check for match in open tags list
+ try:
+ i = open_tags.index(tagname)
+ except ValueError:
+ pass
+ else:
+ # SGML: An end tag closes, back to the matching start tag,
+ # all unclosed intervening start tags with omitted end tags
+ open_tags = open_tags[i + 1:]
+ else:
+ # Add it to the start of the open tags list
+ open_tags.insert(0, tagname)
+ if words <= length:
+ # Don't try to close tags if we don't need to truncate
+ return s
+ out = s[:end_text_pos]
+ if end_text:
+ out += ' ' + end_text
+ # Close any tags still open
+ for tag in open_tags:
+ out += '%s>' % tag
+ # Return string
+ return out
+
+
+def process_translations(content_list, order_by=None):
+ """ Finds translation and returns them.
+
+ Returns a tuple with two lists (index, translations). Index list includes
+ items in default language or items which have no variant in default
+ language. Items with the `translation` metadata set to something else than
+ `False` or `false` will be used as translations, unless all the items with
+ the same slug have that metadata.
+
+ For each content_list item, sets the 'translations' attribute.
+
+ order_by can be a string of an attribute or sorting function. If order_by
+ is defined, content will be ordered by that attribute or sorting function.
+ By default, content is ordered by slug.
+
+ Different content types can have default order_by attributes defined
+ in settings, e.g. PAGES_ORDER_BY='sort-order', in which case `sort-order`
+ should be a defined metadata attribute in each page.
+ """
+ content_list.sort(key=attrgetter('slug'))
+ grouped_by_slugs = groupby(content_list, attrgetter('slug'))
+ index = []
+ translations = []
+
+ for slug, items in grouped_by_slugs:
+ items = list(items)
+ # items with `translation` metadata will be used as translations…
+ default_lang_items = list(filter(
+ lambda i: i.metadata.get('translation', 'false').lower()
+ == 'false',
+ items))
+ # …unless all items with that slug are translations
+ if not default_lang_items:
+ default_lang_items = items
+
+ # display warnings if several items have the same lang
+ for lang, lang_items in groupby(items, attrgetter('lang')):
+ lang_items = list(lang_items)
+ len_ = len(lang_items)
+ if len_ > 1:
+ logger.warning('There are %s variants of "%s" with lang %s',
+ len_, slug, lang)
+ for x in lang_items:
+ logger.warning('\t%s', x.source_path)
+
+ # find items with default language
+ default_lang_items = list(filter(attrgetter('in_default_lang'),
+ default_lang_items))
+
+ # if there is no article with default language, take an other one
+ if not default_lang_items:
+ default_lang_items = items[:1]
+
+ if not slug:
+ logger.warning(
+ 'empty slug for %s. '
+ 'You can fix this by adding a title or a slug to your '
+ 'content',
+ default_lang_items[0].source_path)
+ index.extend(default_lang_items)
+ translations.extend([x for x in items if x not in default_lang_items])
+ for a in items:
+ a.translations = [x for x in items if x != a]
+
+ if order_by:
+ if callable(order_by):
+ try:
+ index.sort(key=order_by)
+ except Exception:
+ logger.error('Error sorting with function {}'.format(order_by))
+ elif order_by == 'basename':
+ index.sort(key=lambda x: os.path.basename(x.source_path or ''))
+ elif order_by != 'slug':
+ try:
+ index.sort(key=attrgetter(order_by))
+ except AttributeError:
+ error_msg = ('There is no "{}" attribute in the item metadata.'
+ 'Defaulting to slug order.')
+ logger.warning(error_msg.format(order_by))
+
+ return index, translations
+
+
+def folder_watcher(path, extensions, ignores=[]):
+ '''Generator for monitoring a folder for modifications.
+
+ Returns a boolean indicating if files are changed since last check.
+ Returns None if there are no matching files in the folder'''
+
+ def file_times(path):
+ '''Return `mtime` for each file in path'''
+
+ for root, dirs, files in os.walk(path):
+ dirs[:] = [x for x in dirs if not x.startswith(os.curdir)]
+
+ for f in files:
+ if (f.endswith(tuple(extensions)) and
+ not any(fnmatch.fnmatch(f, ignore) for ignore in ignores)):
+ try:
+ yield os.stat(os.path.join(root, f)).st_mtime
+ except OSError as e:
+ logger.warning('Caught Exception: %s', e)
+
+ LAST_MTIME = 0
+ while True:
+ try:
+ mtime = max(file_times(path))
+ if mtime > LAST_MTIME:
+ LAST_MTIME = mtime
+ yield True
+ except ValueError:
+ yield None
+ else:
+ yield False
+
+
+def file_watcher(path):
+ '''Generator for monitoring a file for modifications'''
+ LAST_MTIME = 0
+ while True:
+ if path:
+ try:
+ mtime = os.stat(path).st_mtime
+ except OSError as e:
+ logger.warning('Caught Exception: %s', e)
+ continue
+
+ if mtime > LAST_MTIME:
+ LAST_MTIME = mtime
+ yield True
+ else:
+ yield False
+ else:
+ yield None
+
+
+def set_date_tzinfo(d, tz_name=None):
+ """Set the timezone for dates that don't have tzinfo"""
+ if tz_name and not d.tzinfo:
+ tz = pytz.timezone(tz_name)
+ d = tz.localize(d)
+ return SafeDatetime(d.year, d.month, d.day, d.hour, d.minute, d.second,
+ d.microsecond, d.tzinfo)
+ return d
+
+
+def mkdir_p(path):
+ try:
+ os.makedirs(path)
+ except OSError as e:
+ if e.errno != errno.EEXIST or not os.path.isdir(path):
+ raise
+
+
+def split_all(path):
+ """Split a path into a list of components
+
+ While os.path.split() splits a single component off the back of
+ `path`, this function splits all components:
+
+ >>> split_all(os.path.join('a', 'b', 'c'))
+ ['a', 'b', 'c']
+ """
+ components = []
+ path = path.lstrip('/')
+ while path:
+ head, tail = os.path.split(path)
+ if tail:
+ components.insert(0, tail)
+ elif head == path:
+ components.insert(0, head)
+ break
+ path = head
+ return components
+
+
+class FileDataCacher(object):
+ '''Class that can cache data contained in files'''
+
+ def __init__(self, settings, cache_name, caching_policy, load_policy):
+ '''Load the specified cache within CACHE_PATH in settings
+
+ only if *load_policy* is True,
+ May use gzip if GZIP_CACHE ins settings is True.
+ Sets caching policy according to *caching_policy*.
+ '''
+ self.settings = settings
+ self._cache_path = os.path.join(self.settings['CACHE_PATH'],
+ cache_name)
+ self._cache_data_policy = caching_policy
+ if self.settings['GZIP_CACHE']:
+ import gzip
+ self._cache_open = gzip.open
+ else:
+ self._cache_open = open
+ if load_policy:
+ try:
+ with self._cache_open(self._cache_path, 'rb') as fhandle:
+ self._cache = pickle.load(fhandle)
+ except (IOError, OSError) as err:
+ logger.debug('Cannot load cache %s (this is normal on first '
+ 'run). Proceeding with empty cache.\n%s',
+ self._cache_path, err)
+ self._cache = {}
+ except Exception as err:
+ logger.warning(('Cannot unpickle cache %s, cache may be using '
+ 'an incompatible protocol (see pelican caching docs). '
+ 'Proceeding with empty cache.\n%s'),
+ self._cache_path, err)
+ self._cache = {}
+ else:
+ self._cache = {}
+
+ def cache_data(self, filename, data):
+ '''Cache data for given file'''
+ if self._cache_data_policy:
+ self._cache[filename] = data
+
+ def get_cached_data(self, filename, default=None):
+ '''Get cached data for the given file
+
+ if no data is cached, return the default object
+ '''
+ return self._cache.get(filename, default)
+
+ def save_cache(self):
+ '''Save the updated cache'''
+ if self._cache_data_policy:
+ try:
+ mkdir_p(self.settings['CACHE_PATH'])
+ with self._cache_open(self._cache_path, 'wb') as fhandle:
+ pickle.dump(self._cache, fhandle)
+ except (IOError, OSError, pickle.PicklingError) as err:
+ logger.warning('Could not save cache %s\n ... %s',
+ self._cache_path, err)
+
+
+class FileStampDataCacher(FileDataCacher):
+ '''Subclass that also caches the stamp of the file'''
+
+ def __init__(self, settings, cache_name, caching_policy, load_policy):
+ '''This sublcass additionaly sets filestamp function
+ and base path for filestamping operations
+ '''
+ super(FileStampDataCacher, self).__init__(settings, cache_name,
+ caching_policy,
+ load_policy)
+
+ method = self.settings['CHECK_MODIFIED_METHOD']
+ if method == 'mtime':
+ self._filestamp_func = os.path.getmtime
+ else:
+ try:
+ hash_func = getattr(hashlib, method)
+ def filestamp_func(filename):
+ '''return hash of file contents'''
+ with open(filename, 'rb') as fhandle:
+ return hash_func(fhandle.read()).digest()
+ self._filestamp_func = filestamp_func
+ except AttributeError as err:
+ logger.warning('Could not get hashing function\n\t%s', err)
+ self._filestamp_func = None
+
+ def cache_data(self, filename, data):
+ '''Cache stamp and data for the given file'''
+ stamp = self._get_file_stamp(filename)
+ super(FileStampDataCacher, self).cache_data(filename, (stamp, data))
+
+ def _get_file_stamp(self, filename):
+ '''Check if the given file has been modified
+ since the previous build.
+
+ depending on CHECK_MODIFIED_METHOD
+ a float may be returned for 'mtime',
+ a hash for a function name in the hashlib module
+ or an empty bytes string otherwise
+ '''
+ try:
+ return self._filestamp_func(filename)
+ except (IOError, OSError, TypeError) as err:
+ logger.warning('Cannot get modification stamp for %s\n\t%s',
+ filename, err)
+ return b''
+
+ def get_cached_data(self, filename, default=None):
+ '''Get the cached data for the given filename
+ if the file has not been modified.
+
+ If no record exists or file has been modified, return default.
+ Modification is checked by comparing the cached
+ and current file stamp.
+ '''
+ stamp, data = super(FileStampDataCacher, self).get_cached_data(
+ filename, (None, default))
+ if stamp != self._get_file_stamp(filename):
+ return default
+ return data
+
+
+def is_selected_for_writing(settings, path):
+ '''Check whether path is selected for writing
+ according to the WRITE_SELECTED list
+
+ If WRITE_SELECTED is an empty list (default),
+ any path is selected for writing.
+ '''
+ if settings['WRITE_SELECTED']:
+ return path in settings['WRITE_SELECTED']
+ else:
+ return True
+
diff --git a/pelican/writers.py b/pelican/writers.py
new file mode 100644
index 00000000..43d64b34
--- /dev/null
+++ b/pelican/writers.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+from __future__ import with_statement, unicode_literals, print_function
+import six
+
+import os
+import locale
+import logging
+
+if not six.PY3:
+ from codecs import open
+
+from feedgenerator import Atom1Feed, Rss201rev2Feed
+from jinja2 import Markup
+from six.moves.urllib.parse import urlparse
+
+from pelican.paginator import Paginator
+from pelican.utils import (get_relative_path, path_to_url, set_date_tzinfo,
+ is_selected_for_writing)
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+
+class Writer(object):
+
+ def __init__(self, output_path, settings=None):
+ self.output_path = output_path
+ self.reminder = dict()
+ self.settings = settings or {}
+ self._written_files = set()
+ self._overridden_files = set()
+
+ def _create_new_feed(self, feed_type, context):
+ feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
+ sitename = Markup(context['SITENAME']).striptags()
+ feed = feed_class(
+ title=sitename,
+ link=(self.site_url + '/'),
+ feed_url=self.feed_url,
+ description=context.get('SITESUBTITLE', ''))
+ return feed
+
+ def _add_item_to_the_feed(self, feed, item):
+
+ title = Markup(item.title).striptags()
+ link = '%s/%s' % (self.site_url, item.url)
+ feed.add_item(
+ title=title,
+ link=link,
+ unique_id='tag:%s,%s:%s' % (urlparse(link).netloc,
+ item.date.date(),
+ urlparse(link).path.lstrip('/')),
+ description=item.get_content(self.site_url),
+ categories=item.tags if hasattr(item, 'tags') else None,
+ author_name=getattr(item, 'author', ''),
+ pubdate=set_date_tzinfo(
+ item.modified if hasattr(item, 'modified') else item.date,
+ self.settings.get('TIMEZONE', None)))
+
+ def _open_w(self, filename, encoding, override=False):
+ """Open a file to write some content to it.
+
+ Exit if we have already written to that file, unless one (and no more
+ than one) of the writes has the override parameter set to True.
+ """
+ if filename in self._overridden_files:
+ if override:
+ raise RuntimeError('File %s is set to be overridden twice'
+ % filename)
+ else:
+ logger.info('Skipping %s', filename)
+ filename = os.devnull
+ elif filename in self._written_files:
+ if override:
+ logger.info('Overwriting %s', filename)
+ else:
+ raise RuntimeError('File %s is to be overwritten' % filename)
+ if override:
+ self._overridden_files.add(filename)
+ self._written_files.add(filename)
+ return open(filename, 'w', encoding=encoding)
+
+ def write_feed(self, elements, context, path=None, feed_type='atom'):
+ """Generate a feed with the list of articles provided
+
+ Return the feed. If no path or output_path is specified, just
+ return the feed object.
+
+ :param elements: the articles to put on the feed.
+ :param context: the context to get the feed metadata.
+ :param path: the path to output.
+ :param feed_type: the feed type to use (atom or rss)
+ """
+ if not is_selected_for_writing(self.settings, path):
+ return
+ old_locale = locale.setlocale(locale.LC_ALL)
+ locale.setlocale(locale.LC_ALL, str('C'))
+ try:
+ self.site_url = context.get(
+ 'SITEURL', path_to_url(get_relative_path(path)))
+
+ self.feed_domain = context.get('FEED_DOMAIN')
+ self.feed_url = '{}/{}'.format(self.feed_domain, path)
+
+ feed = self._create_new_feed(feed_type, context)
+
+ max_items = len(elements)
+ if self.settings['FEED_MAX_ITEMS']:
+ max_items = min(self.settings['FEED_MAX_ITEMS'], max_items)
+ for i in range(max_items):
+ self._add_item_to_the_feed(feed, elements[i])
+
+ if path:
+ complete_path = os.path.join(self.output_path, path)
+ try:
+ os.makedirs(os.path.dirname(complete_path))
+ except Exception:
+ pass
+
+ encoding = 'utf-8' if six.PY3 else None
+ with self._open_w(complete_path, encoding) as fp:
+ feed.write(fp, 'utf-8')
+ logger.info('Writing %s', complete_path)
+
+ signals.feed_written.send(complete_path, context=context, feed=feed)
+ return feed
+ finally:
+ locale.setlocale(locale.LC_ALL, old_locale)
+
+ def write_file(self, name, template, context, relative_urls=False,
+ paginated=None, override_output=False, **kwargs):
+ """Render the template and write the file.
+
+ :param name: name of the file to output
+ :param template: template to use to generate the content
+ :param context: dict to pass to the templates.
+ :param relative_urls: use relative urls or absolutes ones
+ :param paginated: dict of article list to paginate - must have the
+ same length (same list in different orders)
+ :param override_output: boolean telling if we can override previous
+ output with the same name (and if next files written with the same
+ name should be skipped to keep that one)
+ :param **kwargs: additional variables to pass to the templates
+ """
+
+ if name is False or name == "" or\
+ not is_selected_for_writing(self.settings,\
+ os.path.join(self.output_path, name)):
+ return
+ elif not name:
+ # other stuff, just return for now
+ return
+
+ def _write_file(template, localcontext, output_path, name, override):
+ """Render the template write the file."""
+ # set localsiteurl for context so that Contents can adjust links
+ if localcontext['localsiteurl']:
+ context['localsiteurl'] = localcontext['localsiteurl']
+ output = template.render(localcontext)
+ path = os.path.join(output_path, name)
+ try:
+ os.makedirs(os.path.dirname(path))
+ except Exception:
+ pass
+
+ with self._open_w(path, 'utf-8', override=override) as f:
+ f.write(output)
+ logger.info('Writing %s', path)
+
+ # Send a signal to say we're writing a file with some specific
+ # local context.
+ signals.content_written.send(path, context=localcontext)
+
+ def _get_localcontext(context, name, kwargs, relative_urls):
+ localcontext = context.copy()
+ localcontext['localsiteurl'] = localcontext.get('localsiteurl', None)
+ if relative_urls:
+ relative_url = path_to_url(get_relative_path(name))
+ localcontext['SITEURL'] = relative_url
+ localcontext['localsiteurl'] = relative_url
+ localcontext['output_file'] = name
+ localcontext.update(kwargs)
+ return localcontext
+
+ # pagination
+ if paginated:
+
+ # pagination needed, init paginators
+ paginators = {key: Paginator(name, val, self.settings)
+ for key, val in paginated.items()}
+
+ # generated pages, and write
+ for page_num in range(list(paginators.values())[0].num_pages):
+ paginated_kwargs = kwargs.copy()
+ for key in paginators.keys():
+ paginator = paginators[key]
+ previous_page = paginator.page(page_num) \
+ if page_num > 0 else None
+ page = paginator.page(page_num + 1)
+ next_page = paginator.page(page_num + 2) \
+ if page_num + 1 < paginator.num_pages else None
+ paginated_kwargs.update(
+ {'%s_paginator' % key: paginator,
+ '%s_page' % key: page,
+ '%s_previous_page' % key: previous_page,
+ '%s_next_page' % key: next_page})
+
+ localcontext = _get_localcontext(context, page.save_as, paginated_kwargs, relative_urls)
+ _write_file(template, localcontext, self.output_path,
+ page.save_as, override_output)
+ else:
+ # no pagination
+ localcontext = _get_localcontext(context, name, kwargs, relative_urls)
+ _write_file(template, localcontext, self.output_path, name,
+ override_output)
diff --git a/samples/content/2012-11-30_filename-metadata.rst b/samples/content/2012-11-30_filename-metadata.rst
new file mode 100644
index 00000000..b048103d
--- /dev/null
+++ b/samples/content/2012-11-30_filename-metadata.rst
@@ -0,0 +1,4 @@
+FILENAME_METADATA example
+#########################
+
+Some cool stuff!
diff --git a/samples/content/another_super_article-fr.rst b/samples/content/another_super_article-fr.rst
new file mode 100644
index 00000000..71ac9635
--- /dev/null
+++ b/samples/content/another_super_article-fr.rst
@@ -0,0 +1,7 @@
+Trop bien !
+###########
+
+:lang: fr
+:slug: oh-yeah
+
+Et voila du contenu en français
diff --git a/samples/content/another_super_article.rst b/samples/content/another_super_article.rst
new file mode 100644
index 00000000..e6e0a92c
--- /dev/null
+++ b/samples/content/another_super_article.rst
@@ -0,0 +1,20 @@
+Oh yeah !
+#########
+
+:tags: oh, bar, yeah
+:date: 2010-10-20 10:14
+:category: bar
+:author: Alexis Métaireau
+:slug: oh-yeah
+:license: WTFPL
+
+Why not ?
+=========
+
+After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+.. image:: |filename|/pictures/Sushi.jpg
+ :height: 450 px
+ :width: 600 px
+ :alt: alternate text
diff --git a/samples/content/article2-fr.rst b/samples/content/article2-fr.rst
new file mode 100644
index 00000000..31970f7e
--- /dev/null
+++ b/samples/content/article2-fr.rst
@@ -0,0 +1,9 @@
+Deuxième article
+################
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: fr
+:slug: second-article
+
+Ceci est un article, en français.
diff --git a/samples/content/article2.rst b/samples/content/article2.rst
new file mode 100644
index 00000000..66f768ea
--- /dev/null
+++ b/samples/content/article2.rst
@@ -0,0 +1,9 @@
+Second article
+##############
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: en
+:slug: second-article
+
+This is some article, in english
diff --git a/samples/content/article_tag_baz.rst b/samples/content/article_tag_baz.rst
new file mode 100644
index 00000000..9a33a8d3
--- /dev/null
+++ b/samples/content/article_tag_baz.rst
@@ -0,0 +1,8 @@
+The baz tag
+###########
+
+:date: 2010-03-14
+:url: tag/baz.html
+:save_as: tag/baz.html
+
+This article overrides the listening of the articles under the *baz* tag.
diff --git a/samples/content/cat1/article1.rst b/samples/content/cat1/article1.rst
new file mode 100644
index 00000000..1148a8f9
--- /dev/null
+++ b/samples/content/cat1/article1.rst
@@ -0,0 +1,7 @@
+Article 1
+#########
+
+:date: 2011-02-17
+:yeah: oh yeah !
+
+Article 1
diff --git a/samples/content/cat1/article2.rst b/samples/content/cat1/article2.rst
new file mode 100644
index 00000000..a4f87866
--- /dev/null
+++ b/samples/content/cat1/article2.rst
@@ -0,0 +1,6 @@
+Article 2
+#########
+
+:date: 2011-02-17
+
+Article 2
diff --git a/samples/content/cat1/article3.rst b/samples/content/cat1/article3.rst
new file mode 100644
index 00000000..53471177
--- /dev/null
+++ b/samples/content/cat1/article3.rst
@@ -0,0 +1,6 @@
+Article 3
+#########
+
+:date: 2011-02-17
+
+Article 3
diff --git a/samples/content/cat1/markdown-article.md b/samples/content/cat1/markdown-article.md
new file mode 100644
index 00000000..5307b47a
--- /dev/null
+++ b/samples/content/cat1/markdown-article.md
@@ -0,0 +1,7 @@
+Title: A markdown powered article
+Date: 2011-04-20
+
+You're mutually oblivious.
+
+[a root-relative link to unbelievable](|filename|/unbelievable.rst)
+[a file-relative link to unbelievable](|filename|../unbelievable.rst)
diff --git a/samples/content/draft_article.rst b/samples/content/draft_article.rst
new file mode 100644
index 00000000..76ce9a16
--- /dev/null
+++ b/samples/content/draft_article.rst
@@ -0,0 +1,7 @@
+A draft article
+###############
+
+:status: draft
+
+This is a draft article, it should live under the /drafts/ folder and not be
+listed anywhere else.
diff --git a/samples/content/extra/robots.txt b/samples/content/extra/robots.txt
new file mode 100644
index 00000000..19a6e299
--- /dev/null
+++ b/samples/content/extra/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /pictures
diff --git a/samples/content/pages/hidden_page.rst b/samples/content/pages/hidden_page.rst
new file mode 100644
index 00000000..ab8704ed
--- /dev/null
+++ b/samples/content/pages/hidden_page.rst
@@ -0,0 +1,9 @@
+This is a test hidden page
+##########################
+
+:category: test
+:status: hidden
+
+This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+
diff --git a/samples/content/pages/jinja2_template.html b/samples/content/pages/jinja2_template.html
new file mode 100644
index 00000000..1b0dc4e4
--- /dev/null
+++ b/samples/content/pages/jinja2_template.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% block content %}
+
+Some text
+
+{% endblock %}
diff --git a/samples/content/pages/override_tag_oh.rst b/samples/content/pages/override_tag_oh.rst
new file mode 100644
index 00000000..c3160349
--- /dev/null
+++ b/samples/content/pages/override_tag_oh.rst
@@ -0,0 +1,8 @@
+Oh Oh Oh
+########
+
+:date: 2010-03-14
+:url: tag/oh.html
+:save_as: tag/oh.html
+
+This page overrides the listening of the articles under the *oh* tag.
diff --git a/samples/content/pages/override_url_saveas.rst b/samples/content/pages/override_url_saveas.rst
new file mode 100644
index 00000000..8a515f60
--- /dev/null
+++ b/samples/content/pages/override_url_saveas.rst
@@ -0,0 +1,9 @@
+Override url/save_as
+####################
+
+:date: 2012-12-07
+:url: override/
+:save_as: override/index.html
+
+Test page which overrides save_as and url so that this page will be generated
+at a custom location.
diff --git a/samples/content/pages/test_page.rst b/samples/content/pages/test_page.rst
new file mode 100644
index 00000000..2285f17b
--- /dev/null
+++ b/samples/content/pages/test_page.rst
@@ -0,0 +1,12 @@
+This is a test page
+###################
+
+:category: test
+
+Just an image.
+
+.. image:: |filename|/pictures/Fat_Cat.jpg
+ :height: 450 px
+ :width: 600 px
+ :alt: alternate text
+
diff --git a/samples/content/pictures/Fat_Cat.jpg b/samples/content/pictures/Fat_Cat.jpg
new file mode 100644
index 00000000..d8a96d35
Binary files /dev/null and b/samples/content/pictures/Fat_Cat.jpg differ
diff --git a/samples/content/pictures/Sushi.jpg b/samples/content/pictures/Sushi.jpg
new file mode 100644
index 00000000..e49e5f0a
Binary files /dev/null and b/samples/content/pictures/Sushi.jpg differ
diff --git a/samples/content/pictures/Sushi_Macro.jpg b/samples/content/pictures/Sushi_Macro.jpg
new file mode 100644
index 00000000..21f935a1
Binary files /dev/null and b/samples/content/pictures/Sushi_Macro.jpg differ
diff --git a/samples/content/super_article.rst b/samples/content/super_article.rst
new file mode 100644
index 00000000..a96176a1
--- /dev/null
+++ b/samples/content/super_article.rst
@@ -0,0 +1,37 @@
+This is a super article !
+#########################
+
+:tags: foo, bar, foobar
+:date: 2010-12-02 10:14
+:modified: 2013-11-17 23:29
+:category: yeah
+:author: Alexis Métaireau
+:summary:
+ Multi-line metadata should be supported
+ as well as **inline markup**.
+
+Some content here !
+
+This is a simple title
+======================
+
+And here comes the cool stuff_.
+
+.. image:: |filename|/pictures/Sushi.jpg
+ :height: 450 px
+ :width: 600 px
+ :alt: alternate text
+
+.. image:: |filename|/pictures/Sushi_Macro.jpg
+ :height: 450 px
+ :width: 600 px
+ :alt: alternate text
+
+::
+
+ >>> from ipdb import set_trace
+ >>> set_trace()
+
+→ And now try with some utf8 hell: ééé
+
+.. _stuff: http://books.couchdb.org/relax/design-documents/views
diff --git a/samples/content/unbelievable.rst b/samples/content/unbelievable.rst
new file mode 100644
index 00000000..209e3557
--- /dev/null
+++ b/samples/content/unbelievable.rst
@@ -0,0 +1,98 @@
+Unbelievable !
+##############
+
+:date: 2010-10-15 20:30
+
+Or completely awesome. Depends the needs.
+
+`a root-relative link to markdown-article <|filename|/cat1/markdown-article.md>`_
+`a file-relative link to markdown-article <|filename|cat1/markdown-article.md>`_
+
+Testing sourcecode directive
+----------------------------
+
+.. sourcecode:: python
+ :linenos:
+
+ formatter = self.options and VARIANTS[self.options.keys()[0]]
+
+
+Testing another case
+--------------------
+
+This will now have a line number in 'custom' since it's the default in
+pelican.conf, it will have nothing in default.
+
+.. sourcecode:: python
+
+ formatter = self.options and VARIANTS[self.options.keys()[0]]
+
+
+Lovely.
+
+Testing more sourcecode directives
+----------------------------------
+
+.. sourcecode:: python
+ :anchorlinenos:
+ :classprefix: testing
+ :hl_lines: 10,11,12
+ :lineanchors: foo
+ :linenos: inline
+ :linenospecial: 2
+ :linenostart: 8
+ :linenostep: 2
+ :lineseparator:
+ :linespans: foo
+ :nobackground:
+
+ def run(self):
+ self.assert_has_content()
+ try:
+ lexer = get_lexer_by_name(self.arguments[0])
+ except ValueError:
+ # no lexer found - use the text one instead of an exception
+ lexer = TextLexer()
+
+ if ('linenos' in self.options and
+ self.options['linenos'] not in ('table', 'inline')):
+ self.options['linenos'] = 'table'
+
+ for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
+ if flag in self.options:
+ self.options[flag] = True
+
+ # noclasses should already default to False, but just in case...
+ formatter = HtmlFormatter(noclasses=False, **self.options)
+ parsed = highlight('\n'.join(self.content), lexer, formatter)
+ return [nodes.raw('', parsed, format='html')]
+
+
+Lovely.
+
+Testing even more sourcecode directives
+---------------------------------------
+
+.. sourcecode:: python
+ :linenos: table
+ :nowrap:
+
+
+ formatter = self.options and VARIANTS[self.options.keys()[0]]
+
+
+Lovely.
+
+Testing overriding config defaults
+----------------------------------
+
+Even if the default is line numbers, we can override it here
+
+.. sourcecode:: python
+ :linenos: none
+
+
+ formatter = self.options and VARIANTS[self.options.keys()[0]]
+
+
+Lovely.
diff --git a/samples/content/unwanted_file b/samples/content/unwanted_file
new file mode 100644
index 00000000..591255ae
--- /dev/null
+++ b/samples/content/unwanted_file
@@ -0,0 +1 @@
+not to be parsed
diff --git a/samples/kinda/exciting/new/files/zap! b/samples/kinda/exciting/new/files/zap!
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/kinda/exciting/old b/samples/kinda/exciting/old
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py
new file mode 100755
index 00000000..d6a87923
--- /dev/null
+++ b/samples/pelican.conf.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+AUTHOR = 'Alexis Métaireau'
+SITENAME = "Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+TIMEZONE = "Europe/Paris"
+
+# can be useful in development, but set to False when you're ready to publish
+RELATIVE_URLS = True
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_GENERATOR = False
+REVERSE_CATEGORY_ORDER = True
+LOCALE = "C"
+DEFAULT_PAGINATION = 4
+DEFAULT_DATE = (2012, 3, 2, 14, 1, 1)
+
+FEED_ALL_RSS = 'feeds/all.rss.xml'
+CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+ ('Filyb', "http://filyb.info/"),
+ ('Libert-fr', "http://www.libert-fr.com"),
+ ('N1k0', "http://prendreuncafe.com/blog/"),
+ ('Tarek Ziadé', "http://ziade.org/blog"),
+ ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+ ('lastfm', 'http://lastfm.com/user/akounet'),
+ ('github', 'http://github.com/ametaireau'),)
+
+# global metadata to all the contents
+DEFAULT_METADATA = (('yeah', 'it is'),)
+
+# path-specific metadata
+EXTRA_PATH_METADATA = {
+ 'extra/robots.txt': {'path': 'robots.txt'},
+ }
+
+# static paths will be copied without parsing their contents
+STATIC_PATHS = [
+ 'pictures',
+ 'extra/robots.txt',
+ ]
+
+# custom page generated with a jinja2 template
+TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'}
+
+# code blocks with line numbers
+PYGMENTS_RST_OPTIONS = {'linenos': 'table'}
+
+# foobar will not be used, because it's not in caps. All configuration keys
+# have to be in caps
+foobar = "barbaz"
diff --git a/samples/pelican.conf_FR.py b/samples/pelican.conf_FR.py
new file mode 100644
index 00000000..0c4ec60d
--- /dev/null
+++ b/samples/pelican.conf_FR.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+AUTHOR = 'Alexis Métaireau'
+SITENAME = "Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+TIMEZONE = "Europe/Paris"
+
+# can be useful in development, but set to False when you're ready to publish
+RELATIVE_URLS = True
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_GENERATOR = False
+REVERSE_CATEGORY_ORDER = True
+LOCALE = "fr_FR.UTF-8"
+DEFAULT_PAGINATION = 4
+DEFAULT_DATE = (2012, 3, 2, 14, 1, 1)
+DEFAULT_DATE_FORMAT = '%d %B %Y'
+
+ARTICLE_URL = 'posts/{date:%Y}/{date:%B}/{date:%d}/{slug}/'
+ARTICLE_SAVE_AS = ARTICLE_URL + 'index.html'
+
+FEED_ALL_RSS = 'feeds/all.rss.xml'
+CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+ ('Filyb', "http://filyb.info/"),
+ ('Libert-fr', "http://www.libert-fr.com"),
+ ('N1k0', "http://prendreuncafe.com/blog/"),
+ ('Tarek Ziadé', "http://ziade.org/blog"),
+ ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+ ('lastfm', 'http://lastfm.com/user/akounet'),
+ ('github', 'http://github.com/ametaireau'),)
+
+# global metadata to all the contents
+DEFAULT_METADATA = (('yeah', 'it is'),)
+
+# path-specific metadata
+EXTRA_PATH_METADATA = {
+ 'extra/robots.txt': {'path': 'robots.txt'},
+ }
+
+# static paths will be copied without parsing their contents
+STATIC_PATHS = [
+ 'pictures',
+ 'extra/robots.txt',
+ ]
+
+# custom page generated with a jinja2 template
+TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'}
+
+# code blocks with line numbers
+PYGMENTS_RST_OPTIONS = {'linenos': 'table'}
+
+# foobar will not be used, because it's not in caps. All configuration keys
+# have to be in caps
+foobar = "barbaz"
diff --git a/samples/theme_standard/a_stylesheet b/samples/theme_standard/a_stylesheet
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/theme_standard/a_template b/samples/theme_standard/a_template
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/very/exciting/new/files/bap! b/samples/very/exciting/new/files/bap!
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/very/exciting/new/files/boom! b/samples/very/exciting/new/files/boom!
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/very/exciting/new/files/wow! b/samples/very/exciting/new/files/wow!
new file mode 100644
index 00000000..e69de29b
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..2a9acf13
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal = 1
diff --git a/setup.py b/setup.py
new file mode 100755
index 00000000..c1af72bb
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+from setuptools import setup
+
+requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils',
+ 'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4',
+ 'python-dateutil']
+
+entry_points = {
+ 'console_scripts': [
+ 'pelican = pelican:main',
+ 'pelican-import = pelican.tools.pelican_import:main',
+ 'pelican-quickstart = pelican.tools.pelican_quickstart:main',
+ 'pelican-themes = pelican.tools.pelican_themes:main'
+ ]
+}
+
+
+README = open('README.rst').read()
+CHANGELOG = open('docs/changelog.rst').read()
+
+
+setup(
+ name="pelican",
+ version="3.4.0",
+ url='http://getpelican.com/',
+ author='Alexis Metaireau',
+ author_email='authors@getpelican.com',
+ description="A tool to generate a static blog from reStructuredText or "
+ "Markdown input files.",
+ long_description=README + '\n' + CHANGELOG,
+ packages=['pelican', 'pelican.tools'],
+ include_package_data=True,
+ install_requires=requires,
+ entry_points=entry_points,
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'License :: OSI Approved :: GNU Affero General Public License v3',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ],
+ test_suite='pelican.tests',
+)
diff --git a/static/css/in.css b/static/css/in.css
deleted file mode 100644
index 041b7b4a..00000000
--- a/static/css/in.css
+++ /dev/null
@@ -1,108 +0,0 @@
-@tailwind base; /* Preflight is injected here */
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- @font-face {
- /*https://style64.org/c64-truetype*/
- font-family: "C64 Pro Mono";
- font-weight: 400;
- src: url(/theme/font/C64_Pro_Mono-STYLE.woff2) format("woff");
- }
-
- html {
- /* text-lg == prose-lg */
- /* text-xl == prose-xl */
- @apply text-lg md:text-lg lg:text-xl font-texts text-rp-dawn-text dark:text-rp-moon-iris;
- }
- text {
- @apply text-rp-dawn-text dark:text-rp-moon-iris;
- }
- a {
- @apply underline decoration-2 underline-offset-4 decoration-rp-dawn-gold dark:decoration-rp-moon-pine;
- }
- a:hover {
- @apply underline decoration-2 underline-offset-4 decoration-rp-dawn-foam dark:decoration-rp-moon-rose;
- }
- h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
- @apply font-headings text-rp-dawn-text dark:text-rp-moon-love;
- }
- h1 {
- @apply text-5xl md:text-7xl mb-9;
- }
- h2 {
- @apply text-4xl md:text-5xl mb-9;
- }
- h3 {
- @apply text-3xl md:text-4xl;
- }
- /*for unknown reasons, h4 must be undefined to not make it bigger than h3*/
- /*h5-h6 are ignored by tailwind typography*/
- img {
- @apply drop-shadow-lg;
- }
- dd {
- @apply ml-12 md:ml-24;
- }
- ul {
- @apply pl-4 list-disc;
- }
-
- /* pelican tag_cloud */
- ul.tagcloud {
- @apply list-none p-0;
- }
- ul.tagcloud li {
- @apply inline-block p-1;
- }
-}
-
-@layer components {
- /*This does not seem to affect prismjs which is good*/
- code {
- @apply bg-rp-dawn-highlight-low dark:bg-rp-moon-highlight-low;
- }
-}
-
-/*Hide heading anchor links unless hovering over them*/
-.headerlink {
- @apply no-underline text-rp-dawn-love ml-1 md:ml-2 hover:no-underline;
-}
-
-.note {
- @apply bg-rp-dawn-highlight-med dark:bg-rp-moon-highlight-med m-8 p-4;
-}
-.warn {
- @apply bg-rp-dawn-text text-rp-dawn-base dark:bg-rp-moon-text dark:text-rp-moon-base m-8 p-4;
-}
-
-/*Attempt to only float thumbnails to the right*/
-.image-process-article-image,
-.image-process-thumb,
-.image-process-responsive {
- /*@apply lg:float-right p-2 lg:p-4;*/
- @apply lg:float-right px-1 lg:px-3;
-}
-
-#skiptocontent a {
- position: absolute;
- top: -40px;
- left: 0px;
- /*color: white;*/
- /*border-right: 1px solid white;*/
- /*border-bottom: 1px solid white;*/
- z-index: 100;
- @apply bg-rp-dawn-text text-rp-dawn-surface dark:bg-rp-moon-text dark:text-rp-moon-surface;
-}
-
-#skiptocontent a:focus {
- position: absolute;
- left: 0px;
- top: 0px;
- outline-color: transparent;
-}
diff --git a/static/css/out.css b/static/css/out.css
deleted file mode 100644
index e1870e6c..00000000
--- a/static/css/out.css
+++ /dev/null
@@ -1,2318 +0,0 @@
-*, ::before, ::after {
- --tw-border-spacing-x: 0;
- --tw-border-spacing-y: 0;
- --tw-translate-x: 0;
- --tw-translate-y: 0;
- --tw-rotate: 0;
- --tw-skew-x: 0;
- --tw-skew-y: 0;
- --tw-scale-x: 1;
- --tw-scale-y: 1;
- --tw-pan-x: ;
- --tw-pan-y: ;
- --tw-pinch-zoom: ;
- --tw-scroll-snap-strictness: proximity;
- --tw-gradient-from-position: ;
- --tw-gradient-via-position: ;
- --tw-gradient-to-position: ;
- --tw-ordinal: ;
- --tw-slashed-zero: ;
- --tw-numeric-figure: ;
- --tw-numeric-spacing: ;
- --tw-numeric-fraction: ;
- --tw-ring-inset: ;
- --tw-ring-offset-width: 0px;
- --tw-ring-offset-color: #fff;
- --tw-ring-color: rgb(59 130 246 / 0.5);
- --tw-ring-offset-shadow: 0 0 #0000;
- --tw-ring-shadow: 0 0 #0000;
- --tw-shadow: 0 0 #0000;
- --tw-shadow-colored: 0 0 #0000;
- --tw-blur: ;
- --tw-brightness: ;
- --tw-contrast: ;
- --tw-grayscale: ;
- --tw-hue-rotate: ;
- --tw-invert: ;
- --tw-saturate: ;
- --tw-sepia: ;
- --tw-drop-shadow: ;
- --tw-backdrop-blur: ;
- --tw-backdrop-brightness: ;
- --tw-backdrop-contrast: ;
- --tw-backdrop-grayscale: ;
- --tw-backdrop-hue-rotate: ;
- --tw-backdrop-invert: ;
- --tw-backdrop-opacity: ;
- --tw-backdrop-saturate: ;
- --tw-backdrop-sepia: ;
- --tw-contain-size: ;
- --tw-contain-layout: ;
- --tw-contain-paint: ;
- --tw-contain-style: ;
-}
-
-::backdrop {
- --tw-border-spacing-x: 0;
- --tw-border-spacing-y: 0;
- --tw-translate-x: 0;
- --tw-translate-y: 0;
- --tw-rotate: 0;
- --tw-skew-x: 0;
- --tw-skew-y: 0;
- --tw-scale-x: 1;
- --tw-scale-y: 1;
- --tw-pan-x: ;
- --tw-pan-y: ;
- --tw-pinch-zoom: ;
- --tw-scroll-snap-strictness: proximity;
- --tw-gradient-from-position: ;
- --tw-gradient-via-position: ;
- --tw-gradient-to-position: ;
- --tw-ordinal: ;
- --tw-slashed-zero: ;
- --tw-numeric-figure: ;
- --tw-numeric-spacing: ;
- --tw-numeric-fraction: ;
- --tw-ring-inset: ;
- --tw-ring-offset-width: 0px;
- --tw-ring-offset-color: #fff;
- --tw-ring-color: rgb(59 130 246 / 0.5);
- --tw-ring-offset-shadow: 0 0 #0000;
- --tw-ring-shadow: 0 0 #0000;
- --tw-shadow: 0 0 #0000;
- --tw-shadow-colored: 0 0 #0000;
- --tw-blur: ;
- --tw-brightness: ;
- --tw-contrast: ;
- --tw-grayscale: ;
- --tw-hue-rotate: ;
- --tw-invert: ;
- --tw-saturate: ;
- --tw-sepia: ;
- --tw-drop-shadow: ;
- --tw-backdrop-blur: ;
- --tw-backdrop-brightness: ;
- --tw-backdrop-contrast: ;
- --tw-backdrop-grayscale: ;
- --tw-backdrop-hue-rotate: ;
- --tw-backdrop-invert: ;
- --tw-backdrop-opacity: ;
- --tw-backdrop-saturate: ;
- --tw-backdrop-sepia: ;
- --tw-contain-size: ;
- --tw-contain-layout: ;
- --tw-contain-paint: ;
- --tw-contain-style: ;
-}
-
-/*
-! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com
-*/
-
-/*
-1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
-2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
-*/
-
-*,
-::before,
-::after {
- box-sizing: border-box;
- /* 1 */
- border-width: 0;
- /* 2 */
- border-style: solid;
- /* 2 */
- border-color: #e5e7eb;
- /* 2 */
-}
-
-::before,
-::after {
- --tw-content: '';
-}
-
-/*
-1. Use a consistent sensible line-height in all browsers.
-2. Prevent adjustments of font size after orientation changes in iOS.
-3. Use a more readable tab size.
-4. Use the user's configured `sans` font-family by default.
-5. Use the user's configured `sans` font-feature-settings by default.
-6. Use the user's configured `sans` font-variation-settings by default.
-7. Disable tap highlights on iOS
-*/
-
-html,
-:host {
- line-height: 1.5;
- /* 1 */
- -webkit-text-size-adjust: 100%;
- /* 2 */
- -moz-tab-size: 4;
- /* 3 */
- -o-tab-size: 4;
- tab-size: 4;
- /* 3 */
- font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
- /* 4 */
- font-feature-settings: normal;
- /* 5 */
- font-variation-settings: normal;
- /* 6 */
- -webkit-tap-highlight-color: transparent;
- /* 7 */
-}
-
-/*
-1. Remove the margin in all browsers.
-2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
-*/
-
-body {
- margin: 0;
- /* 1 */
- line-height: inherit;
- /* 2 */
-}
-
-/*
-1. Add the correct height in Firefox.
-2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
-3. Ensure horizontal rules are visible by default.
-*/
-
-hr {
- height: 0;
- /* 1 */
- color: inherit;
- /* 2 */
- border-top-width: 1px;
- /* 3 */
-}
-
-/*
-Add the correct text decoration in Chrome, Edge, and Safari.
-*/
-
-abbr:where([title]) {
- -webkit-text-decoration: underline dotted;
- text-decoration: underline dotted;
-}
-
-/*
-Remove the default font size and weight for headings.
-*/
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-size: inherit;
- font-weight: inherit;
-}
-
-/*
-Reset links to optimize for opt-in styling instead of opt-out.
-*/
-
-a {
- color: inherit;
- text-decoration: inherit;
-}
-
-/*
-Add the correct font weight in Edge and Safari.
-*/
-
-b,
-strong {
- font-weight: bolder;
-}
-
-/*
-1. Use the user's configured `mono` font-family by default.
-2. Use the user's configured `mono` font-feature-settings by default.
-3. Use the user's configured `mono` font-variation-settings by default.
-4. Correct the odd `em` font sizing in all browsers.
-*/
-
-code,
-kbd,
-samp,
-pre {
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
- /* 1 */
- font-feature-settings: normal;
- /* 2 */
- font-variation-settings: normal;
- /* 3 */
- font-size: 1em;
- /* 4 */
-}
-
-/*
-Add the correct font size in all browsers.
-*/
-
-small {
- font-size: 80%;
-}
-
-/*
-Prevent `sub` and `sup` elements from affecting the line height in all browsers.
-*/
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-sup {
- top: -0.5em;
-}
-
-/*
-1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
-2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
-3. Remove gaps between table borders by default.
-*/
-
-table {
- text-indent: 0;
- /* 1 */
- border-color: inherit;
- /* 2 */
- border-collapse: collapse;
- /* 3 */
-}
-
-/*
-1. Change the font styles in all browsers.
-2. Remove the margin in Firefox and Safari.
-3. Remove default padding in all browsers.
-*/
-
-button,
-input,
-optgroup,
-select,
-textarea {
- font-family: inherit;
- /* 1 */
- font-feature-settings: inherit;
- /* 1 */
- font-variation-settings: inherit;
- /* 1 */
- font-size: 100%;
- /* 1 */
- font-weight: inherit;
- /* 1 */
- line-height: inherit;
- /* 1 */
- letter-spacing: inherit;
- /* 1 */
- color: inherit;
- /* 1 */
- margin: 0;
- /* 2 */
- padding: 0;
- /* 3 */
-}
-
-/*
-Remove the inheritance of text transform in Edge and Firefox.
-*/
-
-button,
-select {
- text-transform: none;
-}
-
-/*
-1. Correct the inability to style clickable types in iOS and Safari.
-2. Remove default button styles.
-*/
-
-button,
-input:where([type='button']),
-input:where([type='reset']),
-input:where([type='submit']) {
- -webkit-appearance: button;
- /* 1 */
- background-color: transparent;
- /* 2 */
- background-image: none;
- /* 2 */
-}
-
-/*
-Use the modern Firefox focus style for all focusable elements.
-*/
-
-:-moz-focusring {
- outline: auto;
-}
-
-/*
-Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
-*/
-
-:-moz-ui-invalid {
- box-shadow: none;
-}
-
-/*
-Add the correct vertical alignment in Chrome and Firefox.
-*/
-
-progress {
- vertical-align: baseline;
-}
-
-/*
-Correct the cursor style of increment and decrement buttons in Safari.
-*/
-
-::-webkit-inner-spin-button,
-::-webkit-outer-spin-button {
- height: auto;
-}
-
-/*
-1. Correct the odd appearance in Chrome and Safari.
-2. Correct the outline style in Safari.
-*/
-
-[type='search'] {
- -webkit-appearance: textfield;
- /* 1 */
- outline-offset: -2px;
- /* 2 */
-}
-
-/*
-Remove the inner padding in Chrome and Safari on macOS.
-*/
-
-::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/*
-1. Correct the inability to style clickable types in iOS and Safari.
-2. Change font properties to `inherit` in Safari.
-*/
-
-::-webkit-file-upload-button {
- -webkit-appearance: button;
- /* 1 */
- font: inherit;
- /* 2 */
-}
-
-/*
-Add the correct display in Chrome and Safari.
-*/
-
-summary {
- display: list-item;
-}
-
-/*
-Removes the default spacing and border for appropriate elements.
-*/
-
-blockquote,
-dl,
-dd,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-hr,
-figure,
-p,
-pre {
- margin: 0;
-}
-
-fieldset {
- margin: 0;
- padding: 0;
-}
-
-legend {
- padding: 0;
-}
-
-ol,
-ul,
-menu {
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-/*
-Reset default styling for dialogs.
-*/
-
-dialog {
- padding: 0;
-}
-
-/*
-Prevent resizing textareas horizontally by default.
-*/
-
-textarea {
- resize: vertical;
-}
-
-/*
-1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
-2. Set the default placeholder color to the user's configured gray 400 color.
-*/
-
-input::-moz-placeholder, textarea::-moz-placeholder {
- opacity: 1;
- /* 1 */
- color: #9ca3af;
- /* 2 */
-}
-
-input::placeholder,
-textarea::placeholder {
- opacity: 1;
- /* 1 */
- color: #9ca3af;
- /* 2 */
-}
-
-/*
-Set the default cursor for buttons.
-*/
-
-button,
-[role="button"] {
- cursor: pointer;
-}
-
-/*
-Make sure disabled buttons don't get the pointer cursor.
-*/
-
-:disabled {
- cursor: default;
-}
-
-/*
-1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
-2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
- This can trigger a poorly considered lint error in some tools but is included by design.
-*/
-
-img,
-svg,
-video,
-canvas,
-audio,
-iframe,
-embed,
-object {
- display: block;
- /* 1 */
- vertical-align: middle;
- /* 2 */
-}
-
-/*
-Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
-*/
-
-img,
-video {
- max-width: 100%;
- height: auto;
-}
-
-/* Make elements with the HTML hidden attribute stay hidden by default */
-
-[hidden]:where(:not([hidden="until-found"])) {
- display: none;
-}
-
-@font-face {
- /*https://style64.org/c64-truetype*/
-
- font-family: "C64 Pro Mono";
-
- font-weight: 400;
-
- src: url(/theme/font/C64_Pro_Mono-STYLE.woff2) format("woff");
-}
-
-html {
- /* text-lg == prose-lg */
- /* text-xl == prose-xl */
- font-family: Erode, serif;
- font-size: 1.125rem;
- line-height: 1.75rem;
- --tw-text-opacity: 1;
- color: rgb(87 82 121 / var(--tw-text-opacity, 1));
-}
-
-@media (min-width: 768px) {
- html {
- font-size: 1.125rem;
- line-height: 1.75rem;
- }
-}
-
-@media (min-width: 1024px) {
- html {
- font-size: 1.25rem;
- line-height: 1.75rem;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- --tw-text-opacity: 1;
- color: rgb(196 167 231 / var(--tw-text-opacity, 1));
- }
-}
-
-text {
- --tw-text-opacity: 1;
- color: rgb(87 82 121 / var(--tw-text-opacity, 1));
-}
-
-@media (prefers-color-scheme: dark) {
- text {
- --tw-text-opacity: 1;
- color: rgb(196 167 231 / var(--tw-text-opacity, 1));
- }
-}
-
-a {
- text-decoration-line: underline;
- text-decoration-color: #ea9d34;
- text-decoration-thickness: 2px;
- text-underline-offset: 4px;
-}
-
-@media (prefers-color-scheme: dark) {
- a {
- text-decoration-color: #3e8fb0;
- }
-}
-
-a:hover {
- text-decoration-line: underline;
- text-decoration-color: #56949f;
- text-decoration-thickness: 2px;
- text-underline-offset: 4px;
-}
-
-@media (prefers-color-scheme: dark) {
- a:hover {
- text-decoration-color: #ea9a97;
- }
-}
-
-h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
- font-family: Fira Sans, sans-serif;
- --tw-text-opacity: 1;
- color: rgb(87 82 121 / var(--tw-text-opacity, 1));
-}
-
-@media (prefers-color-scheme: dark) {
- h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
- --tw-text-opacity: 1;
- color: rgb(235 111 146 / var(--tw-text-opacity, 1));
- }
-}
-
-h1 {
- margin-bottom: 2.25rem;
- font-size: 3rem;
- line-height: 1;
-}
-
-@media (min-width: 768px) {
- h1 {
- font-size: 4.5rem;
- line-height: 1;
- }
-}
-
-h2 {
- margin-bottom: 2.25rem;
- font-size: 2.25rem;
- line-height: 2.5rem;
-}
-
-@media (min-width: 768px) {
- h2 {
- font-size: 3rem;
- line-height: 1;
- }
-}
-
-h3 {
- font-size: 1.875rem;
- line-height: 2.25rem;
-}
-
-@media (min-width: 768px) {
- h3 {
- font-size: 2.25rem;
- line-height: 2.5rem;
- }
-}
-
-/*for unknown reasons, h4 must be undefined to not make it bigger than h3*/
-
-/*h5-h6 are ignored by tailwind typography*/
-
-img {
- --tw-drop-shadow: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1));
- filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
-}
-
-dd {
- margin-left: 3rem;
-}
-
-@media (min-width: 768px) {
- dd {
- margin-left: 6rem;
- }
-}
-
-ul {
- list-style-type: disc;
- padding-left: 1rem;
-}
-
-/* pelican tag_cloud */
-
-ul.tagcloud {
- list-style-type: none;
- padding: 0px;
-}
-
-ul.tagcloud li {
- display: inline-block;
- padding: 0.25rem;
-}
-
-/* Preflight is injected here */
-
-.prose {
- color: #575279;
- max-width: 80ch;
-}
-
-.prose :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
-}
-
-.prose :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-lead);
- font-size: 1.25em;
- line-height: 1.6;
- margin-top: 1.2em;
- margin-bottom: 1.2em;
-}
-
-.prose :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #575279;
- text-decoration: underline;
- font-weight: 500;
- text-decoration-color: #ea9d34;
- text-decoration-thickness: 2px;
-}
-
-.prose :where(strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #575279;
- font-weight: 800;
-}
-
-.prose :where(a strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(blockquote strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(thead th strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: decimal;
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- padding-inline-start: 1.625em;
-}
-
-.prose :where(ol[type="A"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: upper-alpha;
-}
-
-.prose :where(ol[type="a"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: lower-alpha;
-}
-
-.prose :where(ol[type="A" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: upper-alpha;
-}
-
-.prose :where(ol[type="a" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: lower-alpha;
-}
-
-.prose :where(ol[type="I"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: upper-roman;
-}
-
-.prose :where(ol[type="i"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: lower-roman;
-}
-
-.prose :where(ol[type="I" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: upper-roman;
-}
-
-.prose :where(ol[type="i" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: lower-roman;
-}
-
-.prose :where(ol[type="1"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: decimal;
-}
-
-.prose :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- list-style-type: disc;
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- padding-inline-start: 1.625em;
-}
-
-.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker {
- font-weight: 400;
- color: var(--tw-prose-counters);
-}
-
-.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker {
- color: var(--tw-prose-bullets);
-}
-
-.prose :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 600;
- margin-top: 1.25em;
-}
-
-.prose :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-color: var(--tw-prose-hr);
- border-top-width: 1px;
- margin-top: 3em;
- margin-bottom: 3em;
-}
-
-.prose :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 500;
- font-style: italic;
- color: #575279;
- border-inline-start-width: 0.25rem;
- border-inline-start-color: var(--tw-prose-quote-borders);
- quotes: "\201C""\201D""\2018""\2019";
- margin-top: 1.6em;
- margin-bottom: 1.6em;
- padding-inline-start: 1em;
- border-color: #9893a5;
- background-color: #f2e9e1;
-}
-
-.prose :where(blockquote p:first-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::before {
- content: open-quote;
-}
-
-.prose :where(blockquote p:last-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::after {
- content: close-quote;
-}
-
-.prose :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 800;
- font-size: 2.25em;
- margin-top: 0;
- margin-bottom: 0.8888889em;
- line-height: 1.1111111;
-}
-
-.prose :where(h1 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 900;
- color: inherit;
-}
-
-.prose :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 700;
- font-size: 1.5em;
- margin-top: 2em;
- margin-bottom: 1em;
- line-height: 1.3333333;
-}
-
-.prose :where(h2 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 800;
- color: inherit;
-}
-
-.prose :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 600;
- font-size: 1.25em;
- margin-top: 1.6em;
- margin-bottom: 0.6em;
- line-height: 1.6;
-}
-
-.prose :where(h3 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 700;
- color: inherit;
-}
-
-.prose :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 600;
- margin-top: 1.5em;
- margin-bottom: 0.5em;
- line-height: 1.5;
-}
-
-.prose :where(h4 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 700;
- color: inherit;
-}
-
-.prose :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
-}
-
-.prose :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- display: block;
- margin-top: 2em;
- margin-bottom: 2em;
-}
-
-.prose :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
-}
-
-.prose :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-weight: 500;
- font-family: inherit;
- color: var(--tw-prose-kbd);
- box-shadow: 0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%), 0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%);
- font-size: 0.875em;
- border-radius: 0.3125rem;
- padding-top: 0.1875em;
- padding-inline-end: 0.375em;
- padding-bottom: 0.1875em;
- padding-inline-start: 0.375em;
-}
-
-.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before {
- content: "";
-}
-
-.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after {
- content: "";
-}
-
-.prose :where(a code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(h1 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
- font-size: 0.875em;
-}
-
-.prose :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
- font-size: 0.9em;
-}
-
-.prose :where(h4 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(blockquote code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(thead th code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: inherit;
-}
-
-.prose :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- width: 100%;
- table-layout: auto;
- margin-top: 2em;
- margin-bottom: 2em;
- font-size: 0.875em;
- line-height: 1.7142857;
-}
-
-.prose :where(thead):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-bottom-width: 1px;
- border-bottom-color: var(--tw-prose-th-borders);
-}
-
-.prose :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-headings);
- font-weight: 600;
- vertical-align: bottom;
- padding-inline-end: 0.5714286em;
- padding-bottom: 0.5714286em;
- padding-inline-start: 0.5714286em;
-}
-
-.prose :where(tbody tr):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-bottom-width: 1px;
- border-bottom-color: var(--tw-prose-td-borders);
-}
-
-.prose :where(tbody tr:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-bottom-width: 0;
-}
-
-.prose :where(tbody td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- vertical-align: baseline;
-}
-
-.prose :where(tfoot):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-top-width: 1px;
- border-top-color: var(--tw-prose-th-borders);
-}
-
-.prose :where(tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- vertical-align: top;
-}
-
-.prose :where(th, td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- text-align: start;
-}
-
-.prose :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-.prose :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: var(--tw-prose-captions);
- font-size: 0.875em;
- line-height: 1.4285714;
- margin-top: 0.8571429em;
-}
-
-.prose {
- --tw-prose-body: #374151;
- --tw-prose-headings: #111827;
- --tw-prose-lead: #4b5563;
- --tw-prose-links: #111827;
- --tw-prose-bold: #111827;
- --tw-prose-counters: #6b7280;
- --tw-prose-bullets: #d1d5db;
- --tw-prose-hr: #e5e7eb;
- --tw-prose-quotes: #111827;
- --tw-prose-quote-borders: #e5e7eb;
- --tw-prose-captions: #6b7280;
- --tw-prose-kbd: #111827;
- --tw-prose-kbd-shadows: 17 24 39;
- --tw-prose-code: #111827;
- --tw-prose-pre-code: #e5e7eb;
- --tw-prose-pre-bg: #1f2937;
- --tw-prose-th-borders: #d1d5db;
- --tw-prose-td-borders: #e5e7eb;
- --tw-prose-invert-body: #d1d5db;
- --tw-prose-invert-headings: #fff;
- --tw-prose-invert-lead: #9ca3af;
- --tw-prose-invert-links: #fff;
- --tw-prose-invert-bold: #fff;
- --tw-prose-invert-counters: #9ca3af;
- --tw-prose-invert-bullets: #4b5563;
- --tw-prose-invert-hr: #374151;
- --tw-prose-invert-quotes: #f3f4f6;
- --tw-prose-invert-quote-borders: #374151;
- --tw-prose-invert-captions: #9ca3af;
- --tw-prose-invert-kbd: #fff;
- --tw-prose-invert-kbd-shadows: 255 255 255;
- --tw-prose-invert-code: #fff;
- --tw-prose-invert-pre-code: #d1d5db;
- --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);
- --tw-prose-invert-th-borders: #4b5563;
- --tw-prose-invert-td-borders: #374151;
- font-size: 1rem;
- line-height: 1.75;
-}
-
-.prose :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-.prose :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.5em;
- margin-bottom: 0.5em;
-}
-
-.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.375em;
-}
-
-.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.375em;
-}
-
-.prose :where(.prose > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.75em;
- margin-bottom: 0.75em;
-}
-
-.prose :where(.prose > ul > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
-}
-
-.prose :where(.prose > ul > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.25em;
-}
-
-.prose :where(.prose > ol > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
-}
-
-.prose :where(.prose > ol > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.25em;
-}
-
-.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.75em;
- margin-bottom: 0.75em;
-}
-
-.prose :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
-}
-
-.prose :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.5em;
- padding-inline-start: 1.625em;
-}
-
-.prose :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
-}
-
-.prose :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
-}
-
-.prose :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
-}
-
-.prose :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
-}
-
-.prose :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
-}
-
-.prose :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
-}
-
-.prose :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-top: 0.5714286em;
- padding-inline-end: 0.5714286em;
- padding-bottom: 0.5714286em;
- padding-inline-start: 0.5714286em;
-}
-
-.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
-}
-
-.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
-}
-
-.prose :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
-}
-
-.prose :where(.prose > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
-}
-
-.prose :where(.prose > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 0;
-}
-
-.prose :where(h1, h2, h3, h4, h5, h6):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #575279;
-}
-
-.prose :where(th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #575279;
-}
-
-/*This does not seem to affect prismjs which is good*/
-
-code {
- --tw-bg-opacity: 1;
- background-color: rgb(244 237 232 / var(--tw-bg-opacity, 1));
-}
-
-@media (prefers-color-scheme: dark) {
- code {
- --tw-bg-opacity: 1;
- background-color: rgb(42 40 62 / var(--tw-bg-opacity, 1));
- }
-}
-
-.absolute {
- position: absolute;
-}
-
-.relative {
- position: relative;
-}
-
-.z-10 {
- z-index: 10;
-}
-
-.z-20 {
- z-index: 20;
-}
-
-.col-span-1 {
- grid-column: span 1 / span 1;
-}
-
-.col-span-2 {
- grid-column: span 2 / span 2;
-}
-
-.col-span-3 {
- grid-column: span 3 / span 3;
-}
-
-.col-span-9 {
- grid-column: span 9 / span 9;
-}
-
-.m-10 {
- margin: 2.5rem;
-}
-
-.m-4 {
- margin: 1rem;
-}
-
-.-mt-11 {
- margin-top: -2.75rem;
-}
-
-.mb-2 {
- margin-bottom: 0.5rem;
-}
-
-.mb-3 {
- margin-bottom: 0.75rem;
-}
-
-.ml-14 {
- margin-left: 3.5rem;
-}
-
-.mt-6 {
- margin-top: 1.5rem;
-}
-
-.block {
- display: block;
-}
-
-.inline-block {
- display: inline-block;
-}
-
-.flex {
- display: flex;
-}
-
-.grid {
- display: grid;
-}
-
-.inline-grid {
- display: inline-grid;
-}
-
-.size-5 {
- width: 1.25rem;
- height: 1.25rem;
-}
-
-.w-12 {
- width: 3rem;
-}
-
-.w-8 {
- width: 2rem;
-}
-
-.-rotate-3 {
- --tw-rotate: -3deg;
- transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
-}
-
-.list-outside {
- list-style-position: outside;
-}
-
-.list-disc {
- list-style-type: disc;
-}
-
-.list-none {
- list-style-type: none;
-}
-
-.grid-cols-1 {
- grid-template-columns: repeat(1, minmax(0, 1fr));
-}
-
-.grid-cols-3 {
- grid-template-columns: repeat(3, minmax(0, 1fr));
-}
-
-.grid-cols-9 {
- grid-template-columns: repeat(9, minmax(0, 1fr));
-}
-
-.grid-cols-\[max-content_max-content_max-content_max-content_max-content\] {
- grid-template-columns: max-content max-content max-content max-content max-content;
-}
-
-.grid-cols-\[max-content_max-content_max-content_max-content_max-content_max-content\] {
- grid-template-columns: max-content max-content max-content max-content max-content max-content;
-}
-
-.items-center {
- align-items: center;
-}
-
-.justify-center {
- justify-content: center;
-}
-
-.gap-8 {
- gap: 2rem;
-}
-
-.hyphens-auto {
- -webkit-hyphens: auto;
- hyphens: auto;
-}
-
-.rounded-full {
- border-radius: 9999px;
-}
-
-.border-b-2 {
- border-bottom-width: 2px;
-}
-
-.border-t-2 {
- border-top-width: 2px;
-}
-
-.border-rp-dawn-gold {
- --tw-border-opacity: 1;
- border-color: rgb(234 157 52 / var(--tw-border-opacity, 1));
-}
-
-.border-rp-dawn-overlay {
- --tw-border-opacity: 1;
- border-color: rgb(242 233 225 / var(--tw-border-opacity, 1));
-}
-
-.bg-rp-dawn-base {
- --tw-bg-opacity: 1;
- background-color: rgb(250 244 237 / var(--tw-bg-opacity, 1));
-}
-
-.bg-rp-dawn-gold {
- --tw-bg-opacity: 1;
- background-color: rgb(234 157 52 / var(--tw-bg-opacity, 1));
-}
-
-.bg-rp-dawn-overlay {
- --tw-bg-opacity: 1;
- background-color: rgb(242 233 225 / var(--tw-bg-opacity, 1));
-}
-
-.bg-rp-dawn-surface {
- --tw-bg-opacity: 1;
- background-color: rgb(255 250 243 / var(--tw-bg-opacity, 1));
-}
-
-.bg-gradient-to-r {
- background-image: linear-gradient(to right, var(--tw-gradient-stops));
-}
-
-.from-rp-dawn-gold {
- --tw-gradient-from: #ea9d34 var(--tw-gradient-from-position);
- --tw-gradient-to: rgb(234 157 52 / 0) var(--tw-gradient-to-position);
- --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
-}
-
-.from-10\% {
- --tw-gradient-from-position: 10%;
-}
-
-.to-rp-dawn-pine {
- --tw-gradient-to: #286983 var(--tw-gradient-to-position);
-}
-
-.to-90\% {
- --tw-gradient-to-position: 90%;
-}
-
-.p-1 {
- padding: 0.25rem;
-}
-
-.p-3 {
- padding: 0.75rem;
-}
-
-.p-4 {
- padding: 1rem;
-}
-
-.p-8 {
- padding: 2rem;
-}
-
-.px-2 {
- padding-left: 0.5rem;
- padding-right: 0.5rem;
-}
-
-.pb-4 {
- padding-bottom: 1rem;
-}
-
-.pb-60 {
- padding-bottom: 15rem;
-}
-
-.pl-4 {
- padding-left: 1rem;
-}
-
-.pt-20 {
- padding-top: 5rem;
-}
-
-.text-center {
- text-align: center;
-}
-
-.align-top {
- vertical-align: top;
-}
-
-.text-2xl {
- font-size: 1.5rem;
- line-height: 2rem;
-}
-
-.text-3xl {
- font-size: 1.875rem;
- line-height: 2.25rem;
-}
-
-.text-4xl {
- font-size: 2.25rem;
- line-height: 2.5rem;
-}
-
-.text-base {
- font-size: 1rem;
- line-height: 1.5rem;
-}
-
-.text-sm {
- font-size: 0.875rem;
- line-height: 1.25rem;
-}
-
-.text-xl {
- font-size: 1.25rem;
- line-height: 1.75rem;
-}
-
-.font-bold {
- font-weight: 700;
-}
-
-.uppercase {
- text-transform: uppercase;
-}
-
-.capitalize {
- text-transform: capitalize;
-}
-
-.italic {
- font-style: italic;
-}
-
-.text-rp-dawn-foam {
- --tw-text-opacity: 1;
- color: rgb(86 148 159 / var(--tw-text-opacity, 1));
-}
-
-.text-rp-dawn-overlay {
- --tw-text-opacity: 1;
- color: rgb(242 233 225 / var(--tw-text-opacity, 1));
-}
-
-.text-rp-dawn-pine {
- --tw-text-opacity: 1;
- color: rgb(40 105 131 / var(--tw-text-opacity, 1));
-}
-
-.text-rp-dawn-text {
- --tw-text-opacity: 1;
- color: rgb(87 82 121 / var(--tw-text-opacity, 1));
-}
-
-.no-underline {
- text-decoration-line: none;
-}
-
-.shadow {
- --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
-}
-
-/*Hide heading anchor links unless hovering over them*/
-
-.headerlink {
- margin-left: 0.25rem;
- --tw-text-opacity: 1;
- color: rgb(180 99 122 / var(--tw-text-opacity, 1));
- text-decoration-line: none;
-}
-
-.headerlink:hover {
- text-decoration-line: none;
-}
-
-@media (min-width: 768px) {
- .headerlink {
- margin-left: 0.5rem;
- }
-}
-
-.note {
- margin: 2rem;
- --tw-bg-opacity: 1;
- background-color: rgb(223 218 217 / var(--tw-bg-opacity, 1));
- padding: 1rem;
-}
-
-@media (prefers-color-scheme: dark) {
- .note {
- --tw-bg-opacity: 1;
- background-color: rgb(68 65 90 / var(--tw-bg-opacity, 1));
- }
-}
-
-.warn {
- margin: 2rem;
- --tw-bg-opacity: 1;
- background-color: rgb(87 82 121 / var(--tw-bg-opacity, 1));
- padding: 1rem;
- --tw-text-opacity: 1;
- color: rgb(250 244 237 / var(--tw-text-opacity, 1));
-}
-
-@media (prefers-color-scheme: dark) {
- .warn {
- --tw-bg-opacity: 1;
- background-color: rgb(224 222 244 / var(--tw-bg-opacity, 1));
- --tw-text-opacity: 1;
- color: rgb(35 33 54 / var(--tw-text-opacity, 1));
- }
-}
-
-/*Attempt to only float thumbnails to the right*/
-
-.image-process-article-image,
-.image-process-thumb,
-.image-process-responsive {
- /*@apply lg:float-right p-2 lg:p-4;*/
- padding-left: 0.25rem;
- padding-right: 0.25rem;
-}
-
-@media (min-width: 1024px) {
- .image-process-article-image,
-.image-process-thumb,
-.image-process-responsive {
- float: right;
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- }
-}
-
-#skiptocontent a {
- position: absolute;
- top: -40px;
- left: 0px;
- /*color: white;*/
- /*border-right: 1px solid white;*/
- /*border-bottom: 1px solid white;*/
- z-index: 100;
- --tw-bg-opacity: 1;
- background-color: rgb(87 82 121 / var(--tw-bg-opacity, 1));
- --tw-text-opacity: 1;
- color: rgb(255 250 243 / var(--tw-text-opacity, 1));
-}
-
-@media (prefers-color-scheme: dark) {
- #skiptocontent a {
- --tw-bg-opacity: 1;
- background-color: rgb(224 222 244 / var(--tw-bg-opacity, 1));
- --tw-text-opacity: 1;
- color: rgb(42 39 63 / var(--tw-text-opacity, 1));
- }
-}
-
-#skiptocontent a:focus {
- position: absolute;
- left: 0px;
- top: 0px;
- outline-color: transparent;
-}
-
-@media (min-width: 768px) {
- .md\:prose-base {
- font-size: 1rem;
- line-height: 1.75;
- }
-
- .md\:prose-base :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- }
-
- .md\:prose-base :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.25em;
- line-height: 1.6;
- margin-top: 1.2em;
- margin-bottom: 1.2em;
- }
-
- .md\:prose-base :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.6em;
- margin-bottom: 1.6em;
- padding-inline-start: 1em;
- }
-
- .md\:prose-base :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 2.25em;
- margin-top: 0;
- margin-bottom: 0.8888889em;
- line-height: 1.1111111;
- }
-
- .md\:prose-base :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.5em;
- margin-top: 2em;
- margin-bottom: 1em;
- line-height: 1.3333333;
- }
-
- .md\:prose-base :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.25em;
- margin-top: 1.6em;
- margin-bottom: 0.6em;
- line-height: 1.6;
- }
-
- .md\:prose-base :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.5em;
- margin-bottom: 0.5em;
- line-height: 1.5;
- }
-
- .md\:prose-base :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
- }
-
- .md\:prose-base :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
- }
-
- .md\:prose-base :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .md\:prose-base :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
- }
-
- .md\:prose-base :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- border-radius: 0.3125rem;
- padding-top: 0.1875em;
- padding-inline-end: 0.375em;
- padding-bottom: 0.1875em;
- padding-inline-start: 0.375em;
- }
-
- .md\:prose-base :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- }
-
- .md\:prose-base :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- }
-
- .md\:prose-base :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.9em;
- }
-
- .md\:prose-base :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- line-height: 1.7142857;
- margin-top: 1.7142857em;
- margin-bottom: 1.7142857em;
- border-radius: 0.375rem;
- padding-top: 0.8571429em;
- padding-inline-end: 1.1428571em;
- padding-bottom: 0.8571429em;
- padding-inline-start: 1.1428571em;
- }
-
- .md\:prose-base :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- padding-inline-start: 1.625em;
- }
-
- .md\:prose-base :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- padding-inline-start: 1.625em;
- }
-
- .md\:prose-base :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.5em;
- margin-bottom: 0.5em;
- }
-
- .md\:prose-base :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.375em;
- }
-
- .md\:prose-base :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.375em;
- }
-
- .md\:prose-base :where(.md\:prose-base > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.75em;
- margin-bottom: 0.75em;
- }
-
- .md\:prose-base :where(.md\:prose-base > ul > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- }
-
- .md\:prose-base :where(.md\:prose-base > ul > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.25em;
- }
-
- .md\:prose-base :where(.md\:prose-base > ol > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- }
-
- .md\:prose-base :where(.md\:prose-base > ol > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.25em;
- }
-
- .md\:prose-base :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.75em;
- margin-bottom: 0.75em;
- }
-
- .md\:prose-base :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- margin-bottom: 1.25em;
- }
-
- .md\:prose-base :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.25em;
- }
-
- .md\:prose-base :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.5em;
- padding-inline-start: 1.625em;
- }
-
- .md\:prose-base :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 3em;
- margin-bottom: 3em;
- }
-
- .md\:prose-base :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .md\:prose-base :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .md\:prose-base :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .md\:prose-base :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .md\:prose-base :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- line-height: 1.7142857;
- }
-
- .md\:prose-base :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0.5714286em;
- padding-bottom: 0.5714286em;
- padding-inline-start: 0.5714286em;
- }
-
- .md\:prose-base :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
- }
-
- .md\:prose-base :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
- }
-
- .md\:prose-base :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-top: 0.5714286em;
- padding-inline-end: 0.5714286em;
- padding-bottom: 0.5714286em;
- padding-inline-start: 0.5714286em;
- }
-
- .md\:prose-base :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
- }
-
- .md\:prose-base :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
- }
-
- .md\:prose-base :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 2em;
- margin-bottom: 2em;
- }
-
- .md\:prose-base :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .md\:prose-base :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- line-height: 1.4285714;
- margin-top: 0.8571429em;
- }
-
- .md\:prose-base :where(.md\:prose-base > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .md\:prose-base :where(.md\:prose-base > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 0;
- }
-}
-
-@media (min-width: 1024px) {
- .lg\:prose-lg {
- font-size: 1.125rem;
- line-height: 1.7777778;
- }
-
- .lg\:prose-lg :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- margin-bottom: 1.3333333em;
- }
-
- .lg\:prose-lg :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.2222222em;
- line-height: 1.4545455;
- margin-top: 1.0909091em;
- margin-bottom: 1.0909091em;
- }
-
- .lg\:prose-lg :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.6666667em;
- margin-bottom: 1.6666667em;
- padding-inline-start: 1em;
- }
-
- .lg\:prose-lg :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 2.6666667em;
- margin-top: 0;
- margin-bottom: 0.8333333em;
- line-height: 1;
- }
-
- .lg\:prose-lg :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.6666667em;
- margin-top: 1.8666667em;
- margin-bottom: 1.0666667em;
- line-height: 1.3333333;
- }
-
- .lg\:prose-lg :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 1.3333333em;
- margin-top: 1.6666667em;
- margin-bottom: 0.6666667em;
- line-height: 1.5;
- }
-
- .lg\:prose-lg :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.7777778em;
- margin-bottom: 0.4444444em;
- line-height: 1.5555556;
- }
-
- .lg\:prose-lg :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.7777778em;
- margin-bottom: 1.7777778em;
- }
-
- .lg\:prose-lg :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.7777778em;
- margin-bottom: 1.7777778em;
- }
-
- .lg\:prose-lg :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .lg\:prose-lg :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.7777778em;
- margin-bottom: 1.7777778em;
- }
-
- .lg\:prose-lg :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8888889em;
- border-radius: 0.3125rem;
- padding-top: 0.2222222em;
- padding-inline-end: 0.4444444em;
- padding-bottom: 0.2222222em;
- padding-inline-start: 0.4444444em;
- }
-
- .lg\:prose-lg :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8888889em;
- }
-
- .lg\:prose-lg :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8666667em;
- }
-
- .lg\:prose-lg :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.875em;
- }
-
- .lg\:prose-lg :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8888889em;
- line-height: 1.75;
- margin-top: 2em;
- margin-bottom: 2em;
- border-radius: 0.375rem;
- padding-top: 1em;
- padding-inline-end: 1.5em;
- padding-bottom: 1em;
- padding-inline-start: 1.5em;
- }
-
- .lg\:prose-lg :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- margin-bottom: 1.3333333em;
- padding-inline-start: 1.5555556em;
- }
-
- .lg\:prose-lg :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- margin-bottom: 1.3333333em;
- padding-inline-start: 1.5555556em;
- }
-
- .lg\:prose-lg :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.6666667em;
- margin-bottom: 0.6666667em;
- }
-
- .lg\:prose-lg :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.4444444em;
- }
-
- .lg\:prose-lg :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0.4444444em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.8888889em;
- margin-bottom: 0.8888889em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > ul > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > ul > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.3333333em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > ol > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > ol > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 1.3333333em;
- }
-
- .lg\:prose-lg :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.8888889em;
- margin-bottom: 0.8888889em;
- }
-
- .lg\:prose-lg :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- margin-bottom: 1.3333333em;
- }
-
- .lg\:prose-lg :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.3333333em;
- }
-
- .lg\:prose-lg :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0.6666667em;
- padding-inline-start: 1.5555556em;
- }
-
- .lg\:prose-lg :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 3.1111111em;
- margin-bottom: 3.1111111em;
- }
-
- .lg\:prose-lg :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .lg\:prose-lg :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .lg\:prose-lg :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .lg\:prose-lg :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .lg\:prose-lg :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8888889em;
- line-height: 1.5;
- }
-
- .lg\:prose-lg :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0.75em;
- padding-bottom: 0.75em;
- padding-inline-start: 0.75em;
- }
-
- .lg\:prose-lg :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
- }
-
- .lg\:prose-lg :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
- }
-
- .lg\:prose-lg :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-top: 0.75em;
- padding-inline-end: 0.75em;
- padding-bottom: 0.75em;
- padding-inline-start: 0.75em;
- }
-
- .lg\:prose-lg :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-start: 0;
- }
-
- .lg\:prose-lg :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- padding-inline-end: 0;
- }
-
- .lg\:prose-lg :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 1.7777778em;
- margin-bottom: 1.7777778em;
- }
-
- .lg\:prose-lg :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .lg\:prose-lg :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- font-size: 0.8888889em;
- line-height: 1.5;
- margin-top: 1em;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-top: 0;
- }
-
- .lg\:prose-lg :where(.lg\:prose-lg > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- margin-bottom: 0;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .dark\:prose-dark {
- color: #c4a7e7;
- }
-
- .dark\:prose-dark :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #c4a7e7;
- text-decoration-color: #3e8fb0;
- text-decoration-thickness: 2px;
- }
-
- .dark\:prose-dark :where(h1, h2, h3, h4, h5, h6):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #eb6f92;
- }
-
- .dark\:prose-dark :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- border-color: #393552;
- background-color: #2a273f;
- color: #e0def4;
- }
-
- .dark\:prose-dark :where(strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #c4a7e7;
- font-weight: 800;
- }
-
- .dark\:prose-dark :where(th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
- color: #c4a7e7;
- }
-}
-
-.hover\:no-underline:hover {
- text-decoration-line: none;
-}
-
-.prose-img\:mx-auto :is(:where(img):not(:where([class~="not-prose"],[class~="not-prose"] *))) {
- margin-left: auto;
- margin-right: auto;
-}
-
-.prose-img\:rounded :is(:where(img):not(:where([class~="not-prose"],[class~="not-prose"] *))) {
- border-radius: 0.25rem;
-}
-
-@media (min-width: 768px) {
- .md\:col-span-3 {
- grid-column: span 3 / span 3;
- }
-
- .md\:col-span-6 {
- grid-column: span 6 / span 6;
- }
-
- .md\:col-span-7 {
- grid-column: span 7 / span 7;
- }
-
- .md\:grid-cols-6 {
- grid-template-columns: repeat(6, minmax(0, 1fr));
- }
-
- .md\:border-l-4 {
- border-left-width: 4px;
- }
-
- .md\:text-3xl {
- font-size: 1.875rem;
- line-height: 2.25rem;
- }
-
- .md\:text-5xl {
- font-size: 3rem;
- line-height: 1;
- }
-
- .md\:text-7xl {
- font-size: 4.5rem;
- line-height: 1;
- }
-
- .md\:text-base {
- font-size: 1rem;
- line-height: 1.5rem;
- }
-
- .md\:text-xl {
- font-size: 1.25rem;
- line-height: 1.75rem;
- }
-}
-
-@media (min-width: 1024px) {
- .lg\:grid-cols-2 {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-
- .lg\:text-lg {
- font-size: 1.125rem;
- line-height: 1.75rem;
- }
-}
-
-@media (min-width: 1280px) {
- .xl\:grid-cols-3 {
- grid-template-columns: repeat(3, minmax(0, 1fr));
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .dark\:border-rp-moon-overlay {
- --tw-border-opacity: 1;
- border-color: rgb(57 53 82 / var(--tw-border-opacity, 1));
- }
-
- .dark\:bg-rp-moon-base {
- --tw-bg-opacity: 1;
- background-color: rgb(35 33 54 / var(--tw-bg-opacity, 1));
- }
-
- .dark\:bg-rp-moon-overlay {
- --tw-bg-opacity: 1;
- background-color: rgb(57 53 82 / var(--tw-bg-opacity, 1));
- }
-
- .dark\:bg-rp-moon-pine {
- --tw-bg-opacity: 1;
- background-color: rgb(62 143 176 / var(--tw-bg-opacity, 1));
- }
-
- .dark\:bg-rp-moon-surface {
- --tw-bg-opacity: 1;
- background-color: rgb(42 39 63 / var(--tw-bg-opacity, 1));
- }
-
- .dark\:text-rp-moon-foam {
- --tw-text-opacity: 1;
- color: rgb(156 207 216 / var(--tw-text-opacity, 1));
- }
-
- .dark\:text-rp-moon-highlight-med {
- --tw-text-opacity: 1;
- color: rgb(68 65 90 / var(--tw-text-opacity, 1));
- }
-
- .dark\:text-rp-moon-iris {
- --tw-text-opacity: 1;
- color: rgb(196 167 231 / var(--tw-text-opacity, 1));
- }
-
- .dark\:text-rp-moon-rose {
- --tw-text-opacity: 1;
- color: rgb(234 154 151 / var(--tw-text-opacity, 1));
- }
-
- .dark\:text-rp-moon-surface {
- --tw-text-opacity: 1;
- color: rgb(42 39 63 / var(--tw-text-opacity, 1));
- }
-
- .dark\:text-rp-moon-text {
- --tw-text-opacity: 1;
- color: rgb(224 222 244 / var(--tw-text-opacity, 1));
- }
-}
diff --git a/static/css/prism-rose-pine-moon-alt.css b/static/css/prism-rose-pine-moon-alt.css
deleted file mode 100644
index 6ceaebae..00000000
--- a/static/css/prism-rose-pine-moon-alt.css
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * MIT License
- * Rosé Pine Theme
- * https://github.com/rose-pine
- * Ported for PrismJS by fvrests [@fvrests]
- */
-
-code[class*="language-"],
-pre[class*="language-"] {
- color: #e0def4;
- background: #2a273f;
- font-family: "Cartograph CF", ui-monospace, SFMono-Regular, Menlo, Monaco,
- Consolas, "Liberation Mono", "Courier New", monospace;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-
- @media print {
- text-shadow: none;
- }
-}
-
-/* Selection */
-code[class*="language-"]::-moz-selection,
-pre[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection,
-pre[class*="language-"] ::-moz-selection {
- background: #44415a;
-}
-
-code[class*="language-"]::selection,
-pre[class*="language-"]::selection,
-code[class*="language-"] ::selection,
-pre[class*="language-"] ::selection {
- background: #44415a;
-}
-
-/* Code (block & inline) */
-:not(pre) > code[class*="language-"],
-pre[class*="language-"] {
- color: #e0def4;
- background: #2a273f;
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: 0.5em 0;
- overflow: auto;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
- padding: 0.1em;
- border-radius: 0.3em;
- white-space: normal;
- color: #e0def4;
- background: #2a273f;
-}
-
-/* Text style & opacity */
-.token.entity {
- cursor: help;
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic,
-.token.selector,
-.token.doctype,
-.token.attr-name,
-.token.inserted,
-.token.deleted,
-.token.comment,
-.token.prolog,
-.token.cdata,
-.token.constant,
-.token.parameter,
-.token.url {
- font-style: italic;
-}
-
-.token.url {
- text-decoration: underline;
-}
-
-.namespace {
- opacity: 0.7;
-}
-
-/* Syntax highlighting */
-.token.constant {
- color: #e0def4;
-}
-
-.token.comment,
-.token.prolog,
-.token.cdata,
-.token.punctuation {
- color: #908caa;
-}
-
-.token.delimiter,
-.token.important,
-.token.atrule,
-.token.operator,
-.token.keyword {
- color: #3e8fb0;
-}
-
-.token.tag,
-.token.tag .punctuation,
-.token.doctype,
-.token.variable,
-.token.regex,
-.token.class-name,
-.token.selector,
-.token.inserted {
- color: #9ccfd8;
-}
-
-.token.boolean,
-.token.entity,
-.token.number,
-.token.symbol,
-.token.function {
- color: #ea9a97;
-}
-
-.token.string,
-.token.char,
-.token.property,
-.token.attr-value,
-.token.attr-value .punctuation {
- color: #f6c177;
-}
-
-.token.parameter,
-.token.url,
-.token.name,
-.token.attr-name,
-.token.builtin {
- color: #c4a7e7;
-}
-
-.token.deleted {
- color: #eb6f92;
-}
-
-/* Insertions & deletions */
-.token.inserted {
- background: rgba(156 207 216 0.12);
-}
-
-.token.deleted {
- background: rgba(235 111 146 0.12);
-}
-
-/* Line highlighting */
-pre[data-line] {
- position: relative;
-}
-
-pre[class*="language-"] > code[class*="language-"] {
- position: relative;
- z-index: 1;
-}
-
-.line-highlight,
-.highlight-lines .highlighted {
- position: absolute;
- left: 0;
- right: 0;
- padding: inherit 0;
- margin-top: 1em;
-
- background: #44415a;
- box-shadow: inset 5px 0 0 #e0def4;
-
- z-index: 0;
-
- pointer-events: none;
-
- line-height: inherit;
- white-space: pre;
-}
diff --git a/static/css/prism-rose-pine-moon.css b/static/css/prism-rose-pine-moon.css
deleted file mode 100644
index fd4353cc..00000000
--- a/static/css/prism-rose-pine-moon.css
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * MIT License
- * Rosé Pine Theme
- * https://github.com/rose-pine
- * Ported for PrismJS by fvrests [@fvrests]
- */
-
-code[class*="language-"],
-pre[class*="language-"] {
- color: #e0def4;
- background: #232136;
- font-family: "Cartograph CF", ui-monospace, SFMono-Regular, Menlo, Monaco,
- Consolas, "Liberation Mono", "Courier New", monospace;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-
- @media print {
- text-shadow: none;
- }
-}
-
-/* Selection */
-code[class*="language-"]::-moz-selection,
-pre[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection,
-pre[class*="language-"] ::-moz-selection {
- background: #44415a;
-}
-
-code[class*="language-"]::selection,
-pre[class*="language-"]::selection,
-code[class*="language-"] ::selection,
-pre[class*="language-"] ::selection {
- background: #44415a;
-}
-
-/* Code (block & inline) */
-:not(pre) > code[class*="language-"],
-pre[class*="language-"] {
- color: #e0def4;
- background: #232136;
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: 0.5em 0;
- overflow: auto;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
- padding: 0.1em;
- border-radius: 0.3em;
- white-space: normal;
- color: #e0def4;
- background: #232136;
-}
-
-/* Text style & opacity */
-.token.entity {
- cursor: help;
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic,
-.token.selector,
-.token.doctype,
-.token.attr-name,
-.token.inserted,
-.token.deleted,
-.token.comment,
-.token.prolog,
-.token.cdata,
-.token.constant,
-.token.parameter,
-.token.url {
- font-style: italic;
-}
-
-.token.url {
- text-decoration: underline;
-}
-
-.namespace {
- opacity: 0.7;
-}
-
-/* Syntax highlighting */
-.token.constant {
- color: #e0def4;
-}
-
-.token.comment,
-.token.prolog,
-.token.cdata,
-.token.punctuation {
- color: #908caa;
-}
-
-.token.delimiter,
-.token.important,
-.token.atrule,
-.token.operator,
-.token.keyword {
- color: #3e8fb0;
-}
-
-.token.tag,
-.token.tag .punctuation,
-.token.doctype,
-.token.variable,
-.token.regex,
-.token.class-name,
-.token.selector,
-.token.inserted {
- color: #9ccfd8;
-}
-
-.token.boolean,
-.token.entity,
-.token.number,
-.token.symbol,
-.token.function {
- color: #ea9a97;
-}
-
-.token.string,
-.token.char,
-.token.property,
-.token.attr-value,
-.token.attr-value .punctuation {
- color: #f6c177;
-}
-
-.token.parameter,
-.token.url,
-.token.name,
-.token.attr-name,
-.token.builtin {
- color: #c4a7e7;
-}
-
-.token.deleted {
- color: #eb6f92;
-}
-
-/* Insertions & deletions */
-.token.inserted {
- background: rgba(156 207 216 0.12);
-}
-
-.token.deleted {
- background: rgba(235 111 146 0.12);
-}
-
-/* Line highlighting */
-pre[data-line] {
- position: relative;
-}
-
-pre[class*="language-"] > code[class*="language-"] {
- position: relative;
- z-index: 1;
-}
-
-.line-highlight,
-.highlight-lines .highlighted {
- position: absolute;
- left: 0;
- right: 0;
- padding: inherit 0;
- margin-top: 1em;
-
- background: #44415a;
- box-shadow: inset 5px 0 0 #e0def4;
-
- z-index: 0;
-
- pointer-events: none;
-
- line-height: inherit;
- white-space: pre;
-}
diff --git a/static/css/prism.css b/static/css/prism.css
deleted file mode 100644
index 6d73b1fb..00000000
--- a/static/css/prism.css
+++ /dev/null
@@ -1,3 +0,0 @@
-/* PrismJS 1.29.0
-https://prismjs.com/download.html?themes#themes=prism&languages=markup+css+clike+javascript+bash+diff+django+dns-zone-file+docker+dot+elixir+erlang+git+go+go-module+hcl+ini+jq+lua+makefile+markdown+markup-templating+nginx+properties+python+r+rest+sql+toml+vim+yaml */
-code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
diff --git a/static/font/C64_Pro_Mono-STYLE.woff2 b/static/font/C64_Pro_Mono-STYLE.woff2
deleted file mode 100644
index a1b72c55..00000000
Binary files a/static/font/C64_Pro_Mono-STYLE.woff2 and /dev/null differ
diff --git a/static/js/prism.js b/static/js/prism.js
deleted file mode 100644
index f65c9412..00000000
--- a/static/js/prism.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* PrismJS 1.29.0
-https://prismjs.com/download.html?themes#themes=prism&languages=markup+css+clike+javascript+bash+diff+django+dns-zone-file+docker+dot+elixir+erlang+git+go+go-module+hcl+ini+jq+lua+makefile+markdown+markup-templating+nginx+properties+python+r+rest+sql+toml+vim+yaml */
-var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
-Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
-!function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
-Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
-Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
-!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i",unchanged:" ",diff:"!"};Object.keys(n).forEach((function(a){var i=n[a],r=[];/^\w+$/.test(a)||r.push(/\w+/.exec(a)[0]),"diff"===a&&r.push("bold"),e.languages.diff[a]={pattern:RegExp("^(?:["+i+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:r,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:n})}(Prism);
-!function(e){function n(e,n){return"___"+e.toUpperCase()+n+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(t,a,r,o){if(t.language===a){var c=t.tokenStack=[];t.code=t.code.replace(r,(function(e){if("function"==typeof o&&!o(e))return e;for(var r,i=c.length;-1!==t.code.indexOf(r=n(a,i));)++i;return c[i]=e,r})),t.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(t,a){if(t.language===a&&t.tokenStack){t.grammar=e.languages[a];var r=0,o=Object.keys(t.tokenStack);!function c(i){for(var u=0;u=o.length);u++){var g=i[u];if("string"==typeof g||g.content&&"string"==typeof g.content){var l=o[r],s=t.tokenStack[l],f="string"==typeof g?g:g.content,p=n(a,l),k=f.indexOf(p);if(k>-1){++r;var m=f.substring(0,k),d=new e.Token(a,e.tokenize(s,t.grammar),"language-"+a,s),h=f.substring(k+p.length),v=[];m&&v.push.apply(v,c([m])),v.push(d),h&&v.push.apply(v,c([h])),"string"==typeof g?i.splice.apply(i,[u,1].concat(v)):g.content=v}}else g.content&&c(g.content)}return i}(t.tokens)}}}})}(Prism);
-!function(e){e.languages.django={comment:/^\{#[\s\S]*?#\}$/,tag:{pattern:/(^\{%[+-]?\s*)\w+/,lookbehind:!0,alias:"keyword"},delimiter:{pattern:/^\{[{%][+-]?|[+-]?[}%]\}$/,alias:"punctuation"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},filter:{pattern:/(\|)\w+/,lookbehind:!0,alias:"function"},test:{pattern:/(\bis\s+(?:not\s+)?)(?!not\b)\w+/,lookbehind:!0,alias:"function"},function:/\b[a-z_]\w+(?=\s*\()/i,keyword:/\b(?:and|as|by|else|for|if|import|in|is|loop|not|or|recursive|with|without)\b/,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,number:/\b\d+(?:\.\d+)?\b/,boolean:/[Ff]alse|[Nn]one|[Tt]rue/,variable:/\b\w+\b/,punctuation:/[{}[\](),.:;]/};var n=/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/g,o=e.languages["markup-templating"];e.hooks.add("before-tokenize",(function(e){o.buildPlaceholders(e,"django",n)})),e.hooks.add("after-tokenize",(function(e){o.tokenizePlaceholders(e,"django")})),e.languages.jinja2=e.languages.django,e.hooks.add("before-tokenize",(function(e){o.buildPlaceholders(e,"jinja2",n)})),e.hooks.add("after-tokenize",(function(e){o.tokenizePlaceholders(e,"jinja2")}))}(Prism);
-Prism.languages["dns-zone-file"]={comment:/;.*/,string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},variable:[{pattern:/(^\$ORIGIN[ \t]+)\S+/m,lookbehind:!0},{pattern:/(^|\s)@(?=\s|$)/,lookbehind:!0}],keyword:/^\$(?:INCLUDE|ORIGIN|TTL)(?=\s|$)/m,class:{pattern:/(^|\s)(?:CH|CS|HS|IN)(?=\s|$)/,lookbehind:!0,alias:"keyword"},type:{pattern:/(^|\s)(?:A|A6|AAAA|AFSDB|APL|ATMA|CAA|CDNSKEY|CDS|CERT|CNAME|DHCID|DLV|DNAME|DNSKEY|DS|EID|GID|GPOS|HINFO|HIP|IPSECKEY|ISDN|KEY|KX|LOC|MAILA|MAILB|MB|MD|MF|MG|MINFO|MR|MX|NAPTR|NB|NBSTAT|NIMLOC|NINFO|NS|NSAP|NSAP-PTR|NSEC|NSEC3|NSEC3PARAM|NULL|NXT|OPENPGPKEY|PTR|PX|RKEY|RP|RRSIG|RT|SIG|SINK|SMIMEA|SOA|SPF|SRV|SSHFP|TA|TKEY|TLSA|TSIG|TXT|UID|UINFO|UNSPEC|URI|WKS|X25)(?=\s|$)/,lookbehind:!0,alias:"keyword"},punctuation:/[()]/},Prism.languages["dns-zone"]=Prism.languages["dns-zone-file"];
-!function(e){var n="(?:[ \t]+(?![ \t])(?:)?|)".replace(//g,(function(){return"\\\\[\r\n](?:\\s|\\\\[\r\n]|#.*(?!.))*(?![\\s#]|\\\\[\r\n])"})),r="\"(?:[^\"\\\\\r\n]|\\\\(?:\r\n|[^]))*\"|'(?:[^'\\\\\r\n]|\\\\(?:\r\n|[^]))*'",t="--[\\w-]+=(?:|(?![\"'])(?:[^\\s\\\\]|\\\\.)+)".replace(//g,(function(){return r})),o={pattern:RegExp(r),greedy:!0},i={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function a(e,r){return e=e.replace(//g,(function(){return t})).replace(//g,(function(){return n})),RegExp(e,r)}e.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:a("(^(?:ONBUILD)?\\w+)(?:)*","i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[o,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:a("(^(?:ONBUILD)?HEALTHCHECK(?:)*)(?:CMD|NONE)\\b","i"),lookbehind:!0,greedy:!0},{pattern:a("(^(?:ONBUILD)?FROM(?:)*(?!--)[^ \t\\\\]+)AS","i"),lookbehind:!0,greedy:!0},{pattern:a("(^ONBUILD)\\w+","i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:i,string:o,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,operator:/\\$/m}},comment:i},e.languages.dockerfile=e.languages.docker}(Prism);
-!function(e){var a="(?:"+["[a-zA-Z_\\x80-\\uFFFF][\\w\\x80-\\uFFFF]*","-?(?:\\.\\d+|\\d+(?:\\.\\d*)?)",'"[^"\\\\]*(?:\\\\[^][^"\\\\]*)*"',"<(?:[^<>]|(?!\x3c!--)<(?:[^<>\"']|\"[^\"]*\"|'[^']*')+>|\x3c!--(?:[^-]|-(?!->))*--\x3e)*>"].join("|")+")",n={markup:{pattern:/(^<)[\s\S]+(?=>$)/,lookbehind:!0,alias:["language-markup","language-html","language-xml"],inside:e.languages.markup}};function r(e,n){return RegExp(e.replace(//g,(function(){return a})),n)}e.languages.dot={comment:{pattern:/\/\/.*|\/\*[\s\S]*?\*\/|^#.*/m,greedy:!0},"graph-name":{pattern:r("(\\b(?:digraph|graph|subgraph)[ \t\r\n]+)","i"),lookbehind:!0,greedy:!0,alias:"class-name",inside:n},"attr-value":{pattern:r("(=[ \t\r\n]*)"),lookbehind:!0,greedy:!0,inside:n},"attr-name":{pattern:r("([\\[;, \t\r\n])(?=[ \t\r\n]*=)"),lookbehind:!0,greedy:!0,inside:n},keyword:/\b(?:digraph|edge|graph|node|strict|subgraph)\b/i,"compass-point":{pattern:/(:[ \t\r\n]*)(?:[ewc_]|[ns][ew]?)(?![\w\x80-\uFFFF])/,lookbehind:!0,alias:"builtin"},node:{pattern:r("(^|[^-.\\w\\x80-\\uFFFF\\\\])"),lookbehind:!0,greedy:!0,inside:n},operator:/[=:]|-[->]/,punctuation:/[\[\]{};,]/},e.languages.gv=e.languages.dot}(Prism);
-Prism.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\b\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|delegate|exception|impl|macro|module|n|np|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|quote|raise|require|rescue|try|unless|unquote|use|when)\b/,boolean:/\b(?:false|nil|true)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach((function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}}));
-Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:false|true)\b/,keyword:/\b(?:after|begin|case|catch|end|fun|if|of|receive|try|when)\b/,number:[/\$\\?./,/\b\d+#[a-z0-9]+/i,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:and|andalso|band|bnot|bor|bsl|bsr|bxor|div|not|or|orelse|rem|xor)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/};
-Prism.languages.git={comment:/^#.*/m,deleted:/^[-–].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m};
-Prism.languages.go=Prism.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),Prism.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete Prism.languages.go["class-name"];
-Prism.languages["go-mod"]=Prism.languages["go-module"]={comment:{pattern:/\/\/.*/,greedy:!0},version:{pattern:/(^|[\s()[\],])v\d+\.\d+\.\d+(?:[+-][-+.\w]*)?(?![^\s()[\],])/,lookbehind:!0,alias:"number"},"go-version":{pattern:/((?:^|\s)go\s+)\d+(?:\.\d+){1,2}/,lookbehind:!0,alias:"number"},keyword:{pattern:/^([ \t]*)(?:exclude|go|module|replace|require|retract)\b/m,lookbehind:!0},operator:/=>/,punctuation:/[()[\],]/};
-Prism.languages.hcl={comment:/(?:\/\/|#).*|\/\*[\s\S]*?(?:\*\/|$)/,heredoc:{pattern:/<<-?(\w+\b)[\s\S]*?^[ \t]*\1/m,greedy:!0,alias:"string"},keyword:[{pattern:/(?:data|resource)\s+(?:"(?:\\[\s\S]|[^\\"])*")(?=\s+"[\w-]+"\s+\{)/i,inside:{type:{pattern:/(resource|data|\s+)(?:"(?:\\[\s\S]|[^\\"])*")/i,lookbehind:!0,alias:"variable"}}},{pattern:/(?:backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+(?=\{)/i,inside:{type:{pattern:/(backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+/i,lookbehind:!0,alias:"variable"}}},/[\w-]+(?=\s+\{)/],property:[/[-\w\.]+(?=\s*=(?!=))/,/"(?:\\[\s\S]|[^\\"])+"(?=\s*[:=])/],string:{pattern:/"(?:[^\\$"]|\\[\s\S]|\$(?:(?=")|\$+(?!\$)|[^"${])|\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\})*"/,greedy:!0,inside:{interpolation:{pattern:/(^|[^$])\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\}/,lookbehind:!0,inside:{type:{pattern:/(\b(?:count|data|local|module|path|self|terraform|var)\b\.)[\w\*]+/i,lookbehind:!0,alias:"variable"},keyword:/\b(?:count|data|local|module|path|self|terraform|var)\b/i,function:/\w+(?=\()/,string:{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,punctuation:/[!\$#%&'()*+,.\/;<=>@\[\\\]^`{|}~?:]/}}}},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,boolean:/\b(?:false|true)\b/i,punctuation:/[=\[\]{}]/};
-Prism.languages.ini={comment:{pattern:/(^[ \f\t\v]*)[#;][^\n\r]*/m,lookbehind:!0},section:{pattern:/(^[ \f\t\v]*)\[[^\n\r\]]*\]?/m,lookbehind:!0,inside:{"section-name":{pattern:/(^\[[ \f\t\v]*)[^ \f\t\v\]]+(?:[ \f\t\v]+[^ \f\t\v\]]+)*/,lookbehind:!0,alias:"selector"},punctuation:/\[|\]/}},key:{pattern:/(^[ \f\t\v]*)[^ \f\n\r\t\v=]+(?:[ \f\t\v]+[^ \f\n\r\t\v=]+)*(?=[ \f\t\v]*=)/m,lookbehind:!0,alias:"attr-name"},value:{pattern:/(=[ \f\t\v]*)[^ \f\n\r\t\v]+(?:[ \f\t\v]+[^ \f\n\r\t\v]+)*/,lookbehind:!0,alias:"attr-value",inside:{"inner-value":{pattern:/^("|').+(?=\1$)/,lookbehind:!0}}},punctuation:/=/};
-!function(e){var n="\\\\\\((?:[^()]|\\([^()]*\\))*\\)",t=RegExp('(^|[^\\\\])"(?:[^"\r\n\\\\]|\\\\[^\r\n(]|__)*"'.replace(/__/g,(function(){return n}))),i={interpolation:{pattern:RegExp("((?:^|[^\\\\])(?:\\\\{2})*)"+n),lookbehind:!0,inside:{content:{pattern:/^(\\\()[\s\S]+(?=\)$)/,lookbehind:!0,inside:null},punctuation:/^\\\(|\)$/}}},a=e.languages.jq={comment:/#.*/,property:{pattern:RegExp(t.source+"(?=\\s*:(?!:))"),lookbehind:!0,greedy:!0,inside:i},string:{pattern:t,lookbehind:!0,greedy:!0,inside:i},function:{pattern:/(\bdef\s+)[a-z_]\w+/i,lookbehind:!0},variable:/\B\$\w+/,"property-literal":{pattern:/\b[a-z_]\w*(?=\s*:(?!:))/i,alias:"property"},keyword:/\b(?:as|break|catch|def|elif|else|end|foreach|if|import|include|label|module|modulemeta|null|reduce|then|try|while)\b/,boolean:/\b(?:false|true)\b/,number:/(?:\b\d+\.|\B\.)?\b\d+(?:[eE][+-]?\d+)?\b/,operator:[{pattern:/\|=?/,alias:"pipe"},/\.\.|[!=<>]?=|\?\/\/|\/\/=?|[-+*/%]=?|[<>?]|\b(?:and|not|or)\b/],"c-style-function":{pattern:/\b[a-z_]\w*(?=\s*\()/i,alias:"function"},punctuation:/::|[()\[\]{},:;]|\.(?=\s*[\[\w$])/,dot:{pattern:/\./,alias:"important"}};i.interpolation.inside.content.inside=a}(Prism);
-Prism.languages.lua={comment:/^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[^z]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,greedy:!0},number:/\b0x[a-f\d]+(?:\.[a-f\d]*)?(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|(?:\.\d*)?(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,keyword:/\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,function:/(?!\d)\w+(?=\s*(?:[({]))/,operator:[/[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,{pattern:/(^|[^.])\.\.(?!\.)/,lookbehind:!0}],punctuation:/[\[\](){},;]|\.+|:+/};
-Prism.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/};
-!function(n){function e(n){return n=n.replace(//g,(function(){return"(?:\\\\.|[^\\\\\n\r]|(?:\n|\r\n?)(?![\r\n]))"})),RegExp("((?:^|[^\\\\])(?:\\\\{2})*)(?:"+n+")")}var t="(?:\\\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\\\|\r\n`])+",a="\\|?__(?:\\|__)+\\|?(?:(?:\n|\r\n?)|(?![^]))".replace(/__/g,(function(){return t})),i="\\|?[ \t]*:?-{3,}:?[ \t]*(?:\\|[ \t]*:?-{3,}:?[ \t]*)+\\|?(?:\n|\r\n?)";n.languages.markdown=n.languages.extend("markup",{}),n.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:n.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+i+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+i+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(t),inside:n.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+i+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(t),alias:"important",inside:n.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:e("\\b__(?:(?!_)|_(?:(?!_))+_)+__\\b|\\*\\*(?:(?!\\*)|\\*(?:(?!\\*))+\\*)+\\*\\*"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:e("\\b_(?:(?!_)|__(?:(?!_))+__)+_\\b|\\*(?:(?!\\*)|\\*\\*(?:(?!\\*))+\\*\\*)+\\*"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:e("(~~?)(?:(?!~))+\\2"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:e('!?\\[(?:(?!\\]))+\\](?:\\([^\\s)]+(?:[\t ]+"(?:\\\\.|[^"\\\\])*")?\\)|[ \t]?\\[(?:(?!\\]))+\\])'),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(e){["url","bold","italic","strike","code-snippet"].forEach((function(t){e!==t&&(n.languages.markdown[e].inside.content.inside[t]=n.languages.markdown[t])}))})),n.hooks.add("after-tokenize",(function(n){"markdown"!==n.language&&"md"!==n.language||function n(e){if(e&&"string"!=typeof e)for(var t=0,a=e.length;t",quot:'"'},l=String.fromCodePoint||String.fromCharCode;n.languages.md=n.languages.markdown}(Prism);
-!function(e){var n=/\$(?:\w[a-z\d]*(?:_[^\x00-\x1F\s"'\\()$]*)?|\{[^}\s"'\\]+\})/i;e.languages.nginx={comment:{pattern:/(^|[\s{};])#.*/,lookbehind:!0,greedy:!0},directive:{pattern:/(^|\s)\w(?:[^;{}"'\\\s]|\\.|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\s+(?:#.*(?!.)|(?![#\s])))*?(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:{string:{pattern:/((?:^|[^\\])(?:\\\\)*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,lookbehind:!0,greedy:!0,inside:{escape:{pattern:/\\["'\\nrt]/,alias:"entity"},variable:n}},comment:{pattern:/(\s)#.*/,lookbehind:!0,greedy:!0},keyword:{pattern:/^\S+/,greedy:!0},boolean:{pattern:/(\s)(?:off|on)(?!\S)/,lookbehind:!0},number:{pattern:/(\s)\d+[a-z]*(?!\S)/i,lookbehind:!0},variable:n}},punctuation:/[{};]/}}(Prism);
-Prism.languages.properties={comment:/^[ \t]*[#!].*$/m,value:{pattern:/(^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?: *[=:] *(?! )| ))(?:\\(?:\r\n|[\s\S])|[^\\\r\n])+/m,lookbehind:!0,alias:"attr-value"},key:{pattern:/^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?= *[=:]| )/m,alias:"attr-name"},punctuation:/[=:]/};
-Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern://,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
-Prism.languages.r={comment:/#.*/,string:{pattern:/(['"])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},"percent-operator":{pattern:/%[^%\s]*%/,alias:"operator"},boolean:/\b(?:FALSE|TRUE)\b/,ellipsis:/\.\.(?:\.|\d+)/,number:[/\b(?:Inf|NaN)\b/,/(?:\b0x[\dA-Fa-f]+(?:\.\d*)?|\b\d+(?:\.\d*)?|\B\.\d+)(?:[EePp][+-]?\d+)?[iL]?/],keyword:/\b(?:NA|NA_character_|NA_complex_|NA_integer_|NA_real_|NULL|break|else|for|function|if|in|next|repeat|while)\b/,operator:/->?>?|<(?:=|-)?|[>=!]=?|::?|&&?|\|\|?|[+*\/^$@~]/,punctuation:/[(){}\[\],;]/};
-Prism.languages.rest={table:[{pattern:/(^[\t ]*)(?:\+[=-]+)+\+(?:\r?\n|\r)(?:\1[+|].+[+|](?:\r?\n|\r))+\1(?:\+[=-]+)+\+/m,lookbehind:!0,inside:{punctuation:/\||(?:\+[=-]+)+\+/}},{pattern:/(^[\t ]*)=+ [ =]*=(?:(?:\r?\n|\r)\1.+)+(?:\r?\n|\r)\1=+ [ =]*=(?=(?:\r?\n|\r){2}|\s*$)/m,lookbehind:!0,inside:{punctuation:/[=-]+/}}],"substitution-def":{pattern:/(^[\t ]*\.\. )\|(?:[^|\s](?:[^|]*[^|\s])?)\| [^:]+::/m,lookbehind:!0,inside:{substitution:{pattern:/^\|(?:[^|\s]|[^|\s][^|]*[^|\s])\|/,alias:"attr-value",inside:{punctuation:/^\||\|$/}},directive:{pattern:/( )(?! )[^:]+::/,lookbehind:!0,alias:"function",inside:{punctuation:/::$/}}}},"link-target":[{pattern:/(^[\t ]*\.\. )\[[^\]]+\]/m,lookbehind:!0,alias:"string",inside:{punctuation:/^\[|\]$/}},{pattern:/(^[\t ]*\.\. )_(?:`[^`]+`|(?:[^:\\]|\\.)+):/m,lookbehind:!0,alias:"string",inside:{punctuation:/^_|:$/}}],directive:{pattern:/(^[\t ]*\.\. )[^:]+::/m,lookbehind:!0,alias:"function",inside:{punctuation:/::$/}},comment:{pattern:/(^[\t ]*\.\.)(?:(?: .+)?(?:(?:\r?\n|\r).+)+| .+)(?=(?:\r?\n|\r){2}|$)/m,lookbehind:!0},title:[{pattern:/^(([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2+)(?:\r?\n|\r).+(?:\r?\n|\r)\1$/m,inside:{punctuation:/^[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+|[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+$/,important:/.+/}},{pattern:/(^|(?:\r?\n|\r){2}).+(?:\r?\n|\r)([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2+(?=\r?\n|\r|$)/,lookbehind:!0,inside:{punctuation:/[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+$/,important:/.+/}}],hr:{pattern:/((?:\r?\n|\r){2})([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2{3,}(?=(?:\r?\n|\r){2})/,lookbehind:!0,alias:"punctuation"},field:{pattern:/(^[\t ]*):[^:\r\n]+:(?= )/m,lookbehind:!0,alias:"attr-name"},"command-line-option":{pattern:/(^[\t ]*)(?:[+-][a-z\d]|(?:--|\/)[a-z\d-]+)(?:[ =](?:[a-z][\w-]*|<[^<>]+>))?(?:, (?:[+-][a-z\d]|(?:--|\/)[a-z\d-]+)(?:[ =](?:[a-z][\w-]*|<[^<>]+>))?)*(?=(?:\r?\n|\r)? {2,}\S)/im,lookbehind:!0,alias:"symbol"},"literal-block":{pattern:/::(?:\r?\n|\r){2}([ \t]+)(?![ \t]).+(?:(?:\r?\n|\r)\1.+)*/,inside:{"literal-block-punctuation":{pattern:/^::/,alias:"punctuation"}}},"quoted-literal-block":{pattern:/::(?:\r?\n|\r){2}([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]).*(?:(?:\r?\n|\r)\1.*)*/,inside:{"literal-block-punctuation":{pattern:/^(?:::|([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\1*)/m,alias:"punctuation"}}},"list-bullet":{pattern:/(^[\t ]*)(?:[*+\-•‣⁃]|\(?(?:\d+|[a-z]|[ivxdclm]+)\)|(?:\d+|[a-z]|[ivxdclm]+)\.)(?= )/im,lookbehind:!0,alias:"punctuation"},"doctest-block":{pattern:/(^[\t ]*)>>> .+(?:(?:\r?\n|\r).+)*/m,lookbehind:!0,inside:{punctuation:/^>>>/}},inline:[{pattern:/(^|[\s\-:\/'"<(\[{])(?::[^:]+:`.*?`|`.*?`:[^:]+:|(\*\*?|``?|\|)(?!\s)(?:(?!\2).)*\S\2(?=[\s\-.,:;!?\\\/'")\]}]|$))/m,lookbehind:!0,inside:{bold:{pattern:/(^\*\*).+(?=\*\*$)/,lookbehind:!0},italic:{pattern:/(^\*).+(?=\*$)/,lookbehind:!0},"inline-literal":{pattern:/(^``).+(?=``$)/,lookbehind:!0,alias:"symbol"},role:{pattern:/^:[^:]+:|:[^:]+:$/,alias:"function",inside:{punctuation:/^:|:$/}},"interpreted-text":{pattern:/(^`).+(?=`$)/,lookbehind:!0,alias:"attr-value"},substitution:{pattern:/(^\|).+(?=\|$)/,lookbehind:!0,alias:"attr-value"},punctuation:/\*\*?|``?|\|/}}],link:[{pattern:/\[[^\[\]]+\]_(?=[\s\-.,:;!?\\\/'")\]}]|$)/,alias:"string",inside:{punctuation:/^\[|\]_$/}},{pattern:/(?:\b[a-z\d]+(?:[_.:+][a-z\d]+)*_?_|`[^`]+`_?_|_`[^`]+`)(?=[\s\-.,:;!?\\\/'")\]}]|$)/i,alias:"string",inside:{punctuation:/^_?`|`$|`?_?_$/}}],punctuation:{pattern:/(^[\t ]*)(?:\|(?= |$)|(?:---?|—|\.\.|__)(?= )|\.\.$)/m,lookbehind:!0}};
-Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/};
-!function(e){function n(e){return e.replace(/__/g,(function(){return"(?:[\\w-]+|'[^'\n\r]*'|\"(?:\\\\.|[^\\\\\"\r\n])*\")"}))}e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(n("(^[\t ]*\\[\\s*(?:\\[\\s*)?)__(?:\\s*\\.\\s*__)*(?=\\s*\\])"),"m"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp(n("(^[\t ]*|[{,]\\s*)__(?:\\s*\\.\\s*__)*(?=\\s*=)"),"m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?\b/i,alias:"number"},{pattern:/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?\b(?:inf|nan)\b/,boolean:/\b(?:false|true)\b/,punctuation:/[.,=[\]{}]/}}(Prism);
-Prism.languages.vim={string:/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\r\n]|'')*'/,comment:/".*/,function:/\b\w+(?=\()/,keyword:/\b(?:N|Next|P|Print|X|XMLent|XMLns|ab|abbreviate|abc|abclear|abo|aboveleft|al|all|ar|arga|argadd|argd|argdelete|argdo|arge|argedit|argg|argglobal|argl|arglocal|args|argu|argument|as|ascii|b|bN|bNext|ba|bad|badd|ball|bd|bdelete|be|bel|belowright|bf|bfirst|bl|blast|bm|bmodified|bn|bnext|bo|botright|bp|bprevious|br|brea|break|breaka|breakadd|breakd|breakdel|breakl|breaklist|brewind|bro|browse|bufdo|buffer|buffers|bun|bunload|bw|bwipeout|c|cN|cNext|cNfcNfile|ca|cabbrev|cabc|cabclear|cad|caddb|caddbuffer|caddexpr|caddf|caddfile|cal|call|cat|catch|cb|cbuffer|cc|ccl|cclose|cd|ce|center|cex|cexpr|cf|cfile|cfir|cfirst|cg|cgetb|cgetbuffer|cgete|cgetexpr|cgetfile|change|changes|chd|chdir|che|checkpath|checkt|checktime|cl|cla|clast|clist|clo|close|cmapc|cmapclear|cn|cnew|cnewer|cnext|cnf|cnfile|cnorea|cnoreabbrev|co|col|colder|colo|colorscheme|comc|comclear|comp|compiler|con|conf|confirm|continue|cope|copen|copy|cp|cpf|cpfile|cprevious|cq|cquit|cr|crewind|cu|cuna|cunabbrev|cunmap|cw|cwindow|d|debugg|debuggreedy|delc|delcommand|delete|delf|delfunction|delm|delmarks|di|diffg|diffget|diffoff|diffpatch|diffpu|diffput|diffsplit|diffthis|diffu|diffupdate|dig|digraphs|display|dj|djump|dl|dlist|dr|drop|ds|dsearch|dsp|dsplit|e|earlier|echoe|echoerr|echom|echomsg|echon|edit|el|else|elsei|elseif|em|emenu|en|endf|endfo|endfor|endfun|endfunction|endif|endt|endtry|endw|endwhile|ene|enew|ex|exi|exit|exu|exusage|f|file|files|filetype|fin|fina|finally|find|fini|finish|fir|first|fix|fixdel|fo|fold|foldc|foldclose|foldd|folddoc|folddoclosed|folddoopen|foldo|foldopen|for|fu|fun|function|go|goto|gr|grep|grepa|grepadd|h|ha|hardcopy|help|helpf|helpfind|helpg|helpgrep|helpt|helptags|hid|hide|his|history|ia|iabbrev|iabc|iabclear|if|ij|ijump|il|ilist|imapc|imapclear|in|inorea|inoreabbrev|isearch|isp|isplit|iu|iuna|iunabbrev|iunmap|j|join|ju|jumps|k|kee|keepalt|keepj|keepjumps|keepmarks|l|lN|lNext|lNf|lNfile|la|lad|laddb|laddbuffer|laddexpr|laddf|laddfile|lan|language|last|later|lb|lbuffer|lc|lcd|lch|lchdir|lcl|lclose|left|lefta|leftabove|let|lex|lexpr|lf|lfile|lfir|lfirst|lg|lgetb|lgetbuffer|lgete|lgetexpr|lgetfile|lgr|lgrep|lgrepa|lgrepadd|lh|lhelpgrep|list|ll|lla|llast|lli|llist|lm|lmak|lmake|lmap|lmapc|lmapclear|ln|lne|lnew|lnewer|lnext|lnf|lnfile|lnoremap|lo|loadview|loc|lockmarks|lockv|lockvar|lol|lolder|lop|lopen|lp|lpf|lpfile|lprevious|lr|lrewind|ls|lt|ltag|lu|lunmap|lv|lvimgrep|lvimgrepa|lvimgrepadd|lw|lwindow|m|ma|mak|make|mark|marks|mat|match|menut|menutranslate|mk|mkexrc|mks|mksession|mksp|mkspell|mkv|mkvie|mkview|mkvimrc|mod|mode|move|mz|mzf|mzfile|mzscheme|n|nbkey|new|next|nmapc|nmapclear|noh|nohlsearch|norea|noreabbrev|nu|number|nun|nunmap|o|omapc|omapclear|on|only|open|opt|options|ou|ounmap|p|pc|pclose|pe|ped|pedit|perl|perld|perldo|po|pop|popu|popup|pp|ppop|pre|preserve|prev|previous|print|prof|profd|profdel|profile|promptf|promptfind|promptr|promptrepl|ps|psearch|ptN|ptNext|pta|ptag|ptf|ptfirst|ptj|ptjump|ptl|ptlast|ptn|ptnext|ptp|ptprevious|ptr|ptrewind|pts|ptselect|pu|put|pw|pwd|py|pyf|pyfile|python|q|qa|qall|quit|quita|quitall|r|read|rec|recover|red|redi|redir|redo|redr|redraw|redraws|redrawstatus|reg|registers|res|resize|ret|retab|retu|return|rew|rewind|ri|right|rightb|rightbelow|ru|rub|ruby|rubyd|rubydo|rubyf|rubyfile|runtime|rv|rviminfo|sN|sNext|sa|sal|sall|san|sandbox|sargument|sav|saveas|sb|sbN|sbNext|sba|sball|sbf|sbfirst|sbl|sblast|sbm|sbmodified|sbn|sbnext|sbp|sbprevious|sbr|sbrewind|sbuffer|scrip|scripte|scriptencoding|scriptnames|se|set|setf|setfiletype|setg|setglobal|setl|setlocal|sf|sfind|sfir|sfirst|sh|shell|sign|sil|silent|sim|simalt|sl|sla|slast|sleep|sm|smagic|smap|smapc|smapclear|sme|smenu|sn|snext|sni|sniff|sno|snomagic|snor|snoremap|snoreme|snoremenu|so|sor|sort|source|sp|spe|spelld|spelldump|spellgood|spelli|spellinfo|spellr|spellrepall|spellu|spellundo|spellw|spellwrong|split|spr|sprevious|sre|srewind|st|sta|stag|star|startg|startgreplace|startinsert|startr|startreplace|stj|stjump|stop|stopi|stopinsert|sts|stselect|sun|sunhide|sunm|sunmap|sus|suspend|sv|sview|syncbind|t|tN|tNext|ta|tab|tabN|tabNext|tabc|tabclose|tabd|tabdo|tabe|tabedit|tabf|tabfind|tabfir|tabfirst|tabl|tablast|tabm|tabmove|tabn|tabnew|tabnext|tabo|tabonly|tabp|tabprevious|tabr|tabrewind|tabs|tag|tags|tc|tcl|tcld|tcldo|tclf|tclfile|te|tearoff|tf|tfirst|th|throw|tj|tjump|tl|tlast|tm|tmenu|tn|tnext|to|topleft|tp|tprevious|tr|trewind|try|ts|tselect|tu|tunmenu|u|una|unabbreviate|undo|undoj|undojoin|undol|undolist|unh|unhide|unlet|unlo|unlockvar|unm|unmap|up|update|ve|verb|verbose|version|vert|vertical|vi|vie|view|vim|vimgrep|vimgrepa|vimgrepadd|visual|viu|viusage|vmapc|vmapclear|vne|vnew|vs|vsplit|vu|vunmap|w|wN|wNext|wa|wall|wh|while|win|winc|wincmd|windo|winp|winpos|winsize|wn|wnext|wp|wprevious|wq|wqa|wqall|write|ws|wsverb|wv|wviminfo|x|xa|xall|xit|xm|xmap|xmapc|xmapclear|xme|xmenu|xn|xnoremap|xnoreme|xnoremenu|xu|xunmap|y|yank)\b/,builtin:/\b(?:acd|ai|akm|aleph|allowrevins|altkeymap|ambiwidth|ambw|anti|antialias|arab|arabic|arabicshape|ari|arshape|autochdir|autocmd|autoindent|autoread|autowrite|autowriteall|aw|awa|background|backspace|backup|backupcopy|backupdir|backupext|backupskip|balloondelay|ballooneval|balloonexpr|bdir|bdlay|beval|bex|bexpr|bg|bh|bin|binary|biosk|bioskey|bk|bkc|bomb|breakat|brk|browsedir|bs|bsdir|bsk|bt|bufhidden|buflisted|buftype|casemap|ccv|cdpath|cedit|cfu|ch|charconvert|ci|cin|cindent|cink|cinkeys|cino|cinoptions|cinw|cinwords|clipboard|cmdheight|cmdwinheight|cmp|cms|columns|com|comments|commentstring|compatible|complete|completefunc|completeopt|consk|conskey|copyindent|cot|cpo|cpoptions|cpt|cscopepathcomp|cscopeprg|cscopequickfix|cscopetag|cscopetagorder|cscopeverbose|cspc|csprg|csqf|cst|csto|csverb|cuc|cul|cursorcolumn|cursorline|cwh|debug|deco|def|define|delcombine|dex|dg|dict|dictionary|diff|diffexpr|diffopt|digraph|dip|dir|directory|dy|ea|ead|eadirection|eb|ed|edcompatible|ef|efm|ei|ek|enc|encoding|endofline|eol|ep|equalalways|equalprg|errorbells|errorfile|errorformat|esckeys|et|eventignore|expandtab|exrc|fcl|fcs|fdc|fde|fdi|fdl|fdls|fdm|fdn|fdo|fdt|fen|fenc|fencs|fex|ff|ffs|fileencoding|fileencodings|fileformat|fileformats|fillchars|fk|fkmap|flp|fml|fmr|foldcolumn|foldenable|foldexpr|foldignore|foldlevel|foldlevelstart|foldmarker|foldmethod|foldminlines|foldnestmax|foldtext|formatexpr|formatlistpat|formatoptions|formatprg|fp|fs|fsync|ft|gcr|gd|gdefault|gfm|gfn|gfs|gfw|ghr|gp|grepformat|grepprg|gtl|gtt|guicursor|guifont|guifontset|guifontwide|guiheadroom|guioptions|guipty|guitablabel|guitabtooltip|helpfile|helpheight|helplang|hf|hh|hi|hidden|highlight|hk|hkmap|hkmapp|hkp|hl|hlg|hls|hlsearch|ic|icon|iconstring|ignorecase|im|imactivatekey|imak|imc|imcmdline|imd|imdisable|imi|iminsert|ims|imsearch|inc|include|includeexpr|incsearch|inde|indentexpr|indentkeys|indk|inex|inf|infercase|insertmode|invacd|invai|invakm|invallowrevins|invaltkeymap|invanti|invantialias|invar|invarab|invarabic|invarabicshape|invari|invarshape|invautochdir|invautoindent|invautoread|invautowrite|invautowriteall|invaw|invawa|invbackup|invballooneval|invbeval|invbin|invbinary|invbiosk|invbioskey|invbk|invbl|invbomb|invbuflisted|invcf|invci|invcin|invcindent|invcompatible|invconfirm|invconsk|invconskey|invcopyindent|invcp|invcscopetag|invcscopeverbose|invcst|invcsverb|invcuc|invcul|invcursorcolumn|invcursorline|invdeco|invdelcombine|invdg|invdiff|invdigraph|invdisable|invea|inveb|inved|invedcompatible|invek|invendofline|inveol|invequalalways|inverrorbells|invesckeys|invet|invex|invexpandtab|invexrc|invfen|invfk|invfkmap|invfoldenable|invgd|invgdefault|invguipty|invhid|invhidden|invhk|invhkmap|invhkmapp|invhkp|invhls|invhlsearch|invic|invicon|invignorecase|invim|invimc|invimcmdline|invimd|invincsearch|invinf|invinfercase|invinsertmode|invis|invjoinspaces|invjs|invlazyredraw|invlbr|invlinebreak|invlisp|invlist|invloadplugins|invlpl|invlz|invma|invmacatsui|invmagic|invmh|invml|invmod|invmodeline|invmodifiable|invmodified|invmore|invmousef|invmousefocus|invmousehide|invnu|invnumber|invodev|invopendevice|invpaste|invpi|invpreserveindent|invpreviewwindow|invprompt|invpvw|invreadonly|invremap|invrestorescreen|invrevins|invri|invrightleft|invrightleftcmd|invrl|invrlc|invro|invrs|invru|invruler|invsb|invsc|invscb|invscrollbind|invscs|invsecure|invsft|invshellslash|invshelltemp|invshiftround|invshortname|invshowcmd|invshowfulltag|invshowmatch|invshowmode|invsi|invsm|invsmartcase|invsmartindent|invsmarttab|invsmd|invsn|invsol|invspell|invsplitbelow|invsplitright|invspr|invsr|invssl|invsta|invstartofline|invstmp|invswapfile|invswf|invta|invtagbsearch|invtagrelative|invtagstack|invtbi|invtbidi|invtbs|invtermbidi|invterse|invtextauto|invtextmode|invtf|invtgst|invtildeop|invtimeout|invtitle|invto|invtop|invtr|invttimeout|invttybuiltin|invttyfast|invtx|invvb|invvisualbell|invwa|invwarn|invwb|invweirdinvert|invwfh|invwfw|invwildmenu|invwinfixheight|invwinfixwidth|invwiv|invwmnu|invwrap|invwrapscan|invwrite|invwriteany|invwritebackup|invws|isf|isfname|isi|isident|isk|iskeyword|isprint|joinspaces|js|key|keymap|keymodel|keywordprg|km|kmp|kp|langmap|langmenu|laststatus|lazyredraw|lbr|lcs|linebreak|lines|linespace|lisp|lispwords|listchars|loadplugins|lpl|lsp|lz|macatsui|magic|makeef|makeprg|matchpairs|matchtime|maxcombine|maxfuncdepth|maxmapdepth|maxmem|maxmempattern|maxmemtot|mco|mef|menuitems|mfd|mh|mis|mkspellmem|ml|mls|mm|mmd|mmp|mmt|modeline|modelines|modifiable|modified|more|mouse|mousef|mousefocus|mousehide|mousem|mousemodel|mouses|mouseshape|mouset|mousetime|mp|mps|msm|mzq|mzquantum|nf|noacd|noai|noakm|noallowrevins|noaltkeymap|noanti|noantialias|noar|noarab|noarabic|noarabicshape|noari|noarshape|noautochdir|noautoindent|noautoread|noautowrite|noautowriteall|noaw|noawa|nobackup|noballooneval|nobeval|nobin|nobinary|nobiosk|nobioskey|nobk|nobl|nobomb|nobuflisted|nocf|noci|nocin|nocindent|nocompatible|noconfirm|noconsk|noconskey|nocopyindent|nocp|nocscopetag|nocscopeverbose|nocst|nocsverb|nocuc|nocul|nocursorcolumn|nocursorline|nodeco|nodelcombine|nodg|nodiff|nodigraph|nodisable|noea|noeb|noed|noedcompatible|noek|noendofline|noeol|noequalalways|noerrorbells|noesckeys|noet|noex|noexpandtab|noexrc|nofen|nofk|nofkmap|nofoldenable|nogd|nogdefault|noguipty|nohid|nohidden|nohk|nohkmap|nohkmapp|nohkp|nohls|noic|noicon|noignorecase|noim|noimc|noimcmdline|noimd|noincsearch|noinf|noinfercase|noinsertmode|nois|nojoinspaces|nojs|nolazyredraw|nolbr|nolinebreak|nolisp|nolist|noloadplugins|nolpl|nolz|noma|nomacatsui|nomagic|nomh|noml|nomod|nomodeline|nomodifiable|nomodified|nomore|nomousef|nomousefocus|nomousehide|nonu|nonumber|noodev|noopendevice|nopaste|nopi|nopreserveindent|nopreviewwindow|noprompt|nopvw|noreadonly|noremap|norestorescreen|norevins|nori|norightleft|norightleftcmd|norl|norlc|noro|nors|noru|noruler|nosb|nosc|noscb|noscrollbind|noscs|nosecure|nosft|noshellslash|noshelltemp|noshiftround|noshortname|noshowcmd|noshowfulltag|noshowmatch|noshowmode|nosi|nosm|nosmartcase|nosmartindent|nosmarttab|nosmd|nosn|nosol|nospell|nosplitbelow|nosplitright|nospr|nosr|nossl|nosta|nostartofline|nostmp|noswapfile|noswf|nota|notagbsearch|notagrelative|notagstack|notbi|notbidi|notbs|notermbidi|noterse|notextauto|notextmode|notf|notgst|notildeop|notimeout|notitle|noto|notop|notr|nottimeout|nottybuiltin|nottyfast|notx|novb|novisualbell|nowa|nowarn|nowb|noweirdinvert|nowfh|nowfw|nowildmenu|nowinfixheight|nowinfixwidth|nowiv|nowmnu|nowrap|nowrapscan|nowrite|nowriteany|nowritebackup|nows|nrformats|numberwidth|nuw|odev|oft|ofu|omnifunc|opendevice|operatorfunc|opfunc|osfiletype|pa|para|paragraphs|paste|pastetoggle|patchexpr|patchmode|path|pdev|penc|pex|pexpr|pfn|ph|pheader|pi|pm|pmbcs|pmbfn|popt|preserveindent|previewheight|previewwindow|printdevice|printencoding|printexpr|printfont|printheader|printmbcharset|printmbfont|printoptions|prompt|pt|pumheight|pvh|pvw|qe|quoteescape|readonly|remap|report|restorescreen|revins|rightleft|rightleftcmd|rl|rlc|ro|rs|rtp|ruf|ruler|rulerformat|runtimepath|sbo|sc|scb|scr|scroll|scrollbind|scrolljump|scrolloff|scrollopt|scs|sect|sections|secure|sel|selection|selectmode|sessionoptions|sft|shcf|shellcmdflag|shellpipe|shellquote|shellredir|shellslash|shelltemp|shelltype|shellxquote|shiftround|shiftwidth|shm|shortmess|shortname|showbreak|showcmd|showfulltag|showmatch|showmode|showtabline|shq|si|sidescroll|sidescrolloff|siso|sj|slm|smartcase|smartindent|smarttab|smc|smd|softtabstop|sol|spc|spell|spellcapcheck|spellfile|spelllang|spellsuggest|spf|spl|splitbelow|splitright|sps|sr|srr|ss|ssl|ssop|stal|startofline|statusline|stl|stmp|su|sua|suffixes|suffixesadd|sw|swapfile|swapsync|swb|swf|switchbuf|sws|sxq|syn|synmaxcol|syntax|t_AB|t_AF|t_AL|t_CS|t_CV|t_Ce|t_Co|t_Cs|t_DL|t_EI|t_F1|t_F2|t_F3|t_F4|t_F5|t_F6|t_F7|t_F8|t_F9|t_IE|t_IS|t_K1|t_K3|t_K4|t_K5|t_K6|t_K7|t_K8|t_K9|t_KA|t_KB|t_KC|t_KD|t_KE|t_KF|t_KG|t_KH|t_KI|t_KJ|t_KK|t_KL|t_RI|t_RV|t_SI|t_Sb|t_Sf|t_WP|t_WS|t_ZH|t_ZR|t_al|t_bc|t_cd|t_ce|t_cl|t_cm|t_cs|t_da|t_db|t_dl|t_fs|t_k1|t_k2|t_k3|t_k4|t_k5|t_k6|t_k7|t_k8|t_k9|t_kB|t_kD|t_kI|t_kN|t_kP|t_kb|t_kd|t_ke|t_kh|t_kl|t_kr|t_ks|t_ku|t_le|t_mb|t_md|t_me|t_mr|t_ms|t_nd|t_op|t_se|t_so|t_sr|t_te|t_ti|t_ts|t_ue|t_us|t_ut|t_vb|t_ve|t_vi|t_vs|t_xs|tabline|tabpagemax|tabstop|tagbsearch|taglength|tagrelative|tagstack|tal|tb|tbi|tbidi|tbis|tbs|tenc|term|termbidi|termencoding|terse|textauto|textmode|textwidth|tgst|thesaurus|tildeop|timeout|timeoutlen|title|titlelen|titleold|titlestring|toolbar|toolbariconsize|top|tpm|tsl|tsr|ttimeout|ttimeoutlen|ttm|tty|ttybuiltin|ttyfast|ttym|ttymouse|ttyscroll|ttytype|tw|tx|uc|ul|undolevels|updatecount|updatetime|ut|vb|vbs|vdir|verbosefile|vfile|viewdir|viewoptions|viminfo|virtualedit|visualbell|vop|wak|warn|wb|wc|wcm|wd|weirdinvert|wfh|wfw|whichwrap|wi|wig|wildchar|wildcharm|wildignore|wildmenu|wildmode|wildoptions|wim|winaltkeys|window|winfixheight|winfixwidth|winheight|winminheight|winminwidth|winwidth|wiv|wiw|wm|wmh|wmnu|wmw|wop|wrap|wrapmargin|wrapscan|writeany|writebackup|writedelay|ww)\b/,number:/\b(?:0x[\da-f]+|\d+(?:\.\d+)?)\b/i,operator:/\|\||&&|[-+.]=?|[=!](?:[=~][#?]?)?|[<>]=?[#?]?|[*\/%?]|\b(?:is(?:not)?)\b/,punctuation:/[{}[\](),;:]/};
-!function(e){var n=/[*&][^\s[\]{},]+/,r=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,t="(?:"+r.source+"(?:[ \t]+"+n.source+")?|"+n.source+"(?:[ \t]+"+r.source+")?)",a="(?:[^\\s\\x00-\\x08\\x0e-\\x1f!\"#%&'*,\\-:>?@[\\]`{|}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*".replace(//g,(function(){return"[^\\s\\x00-\\x08\\x0e-\\x1f,[\\]{}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]"})),d="\"(?:[^\"\\\\\r\n]|\\\\.)*\"|'(?:[^'\\\\\r\n]|\\\\.)*'";function o(e,n){n=(n||"").replace(/m/g,"")+"m";var r="([:\\-,[{]\\s*(?:\\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\\]|\\}|(?:[\r\n]\\s*)?#))".replace(/<>/g,(function(){return t})).replace(/<>/g,(function(){return e}));return RegExp(r,n)}e.languages.yaml={scalar:{pattern:RegExp("([\\-:]\\s*(?:\\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\\S[^\r\n]*(?:\\2[^\r\n]+)*)".replace(/<>/g,(function(){return t}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp("((?:^|[:\\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\\s*:\\s)".replace(/<>/g,(function(){return t})).replace(/<>/g,(function(){return"(?:"+a+"|"+d+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:o("\\d{4}-\\d\\d?-\\d\\d?(?:[tT]|[ \t]+)\\d\\d?:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[ \t]*(?:Z|[-+]\\d\\d?(?::\\d{2})?))?|\\d{4}-\\d{2}-\\d{2}|\\d\\d?:\\d{2}(?::\\d{2}(?:\\.\\d*)?)?"),lookbehind:!0,alias:"number"},boolean:{pattern:o("false|true","i"),lookbehind:!0,alias:"important"},null:{pattern:o("null|~","i"),lookbehind:!0,alias:"important"},string:{pattern:o(d),lookbehind:!0,greedy:!0},number:{pattern:o("[+-]?(?:0x[\\da-f]+|0o[0-7]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?|\\.inf|\\.nan)","i"),lookbehind:!0},tag:r,important:n,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(Prism);
diff --git a/tailwind.config.js b/tailwind.config.js
deleted file mode 100644
index b7b9c40d..00000000
--- a/tailwind.config.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-module.exports = {
- content: ["./templates/*.html"],
- plugins: [require("@tailwindcss/typography")],
- variants: {
- typography: ["dark"],
- },
- theme: {
- fontFamily: {
- texts: ["Erode", "serif"],
- headings: ["Fira Sans", "sans-serif"],
- },
- extend: {
- // NOTE: this is for non-prose (so not Markdown rendered with typography plugin)
- colors: {
- // https://rosepinetheme.com/palette/
- "rp-dawn-base": "#faf4ed", // main background
- "rp-dawn-surface": "#fffaf3", // navigation background
- "rp-dawn-overlay": "#f2e9e1", // content background
- "rp-dawn-muted": "#9893a5",
- "rp-dawn-subtle": "#797593",
- "rp-dawn-text": "#575279", // text
- "rp-dawn-love": "#b4637a",
- "rp-dawn-gold": "#ea9d34",
- "rp-dawn-rose": "#d7827e",
- "rp-dawn-pine": "#286983",
- "rp-dawn-foam": "#56949f", // main site title
- "rp-dawn-iris": "#907aa9", // links
- "rp-dawn-highlight-low": "#f4ede8",
- "rp-dawn-highlight-med": "#dfdad9", // footer
- "rp-dawn-highlight-high": "#cecacd",
- "rp-moon-base": "#232136", // main background
- "rp-moon-surface": "#2a273f", // navigation background
- "rp-moon-overlay": "#393552", // content background
- "rp-moon-muted": "#6e6a86",
- "rp-moon-subtle": "#908caa",
- "rp-moon-text": "#e0def4",
- "rp-moon-love": "#eb6f92",
- "rp-moon-gold": "#f6c177",
- "rp-moon-rose": "#ea9a97",
- "rp-moon-pine": "#3e8fb0",
- "rp-moon-foam": "#9ccfd8", // main site title
- "rp-moon-iris": "#c4a7e7", // text,
- "rp-moon-highlight-low": "#2a283e",
- "rp-moon-highlight-med": "#44415a", // footer
- "rp-moon-highlight-high": "#56526e",
- },
- typography: (theme) => ({
- // NOTE: This is for prose (Markdown) in LIGHT mode
- DEFAULT: {
- css: {
- maxWidth: "80ch",
- pre: null,
- code: null,
- "code::before": null,
- "code::after": null,
- "pre code": null,
- "pre code::before": null,
- "pre code::after": null,
- // remove backticks from typography for inline code
- "code::before": {
- content: '""',
- },
- "code::after": {
- content: '""',
- },
- color: theme("colors.rp-dawn-text"), // main text
- a: {
- color: theme("colors.rp-dawn-text"), // align w/ in.css
- "text-decoration-color": theme("colors.rp-dawn-gold"), // align w/ in.css
- "text-decoration-thickness": "2px", // align w/ in.css
- },
- "h1, h2, h3, h4, h5, h6": {
- color: theme("colors.rp-dawn-text"),
- },
- blockquote: {
- "border-color": theme("colors.rp-dawn-muted"),
- "background-color": theme("colors.rp-dawn-overlay"),
- color: theme("colors.rp-dawn-text"),
- },
- strong: {
- color: theme("colors.rp-dawn-text"), // align w/ main text color
- fontWeight: "800",
- },
- th: {
- color: theme("colors.rp-dawn-text"),
- },
- },
- },
- // NOTE: This is for prose (Markdown) in DARK mode
- dark: {
- css: {
- pre: null,
- code: null,
- "pre code": null,
- color: theme("colors.rp-moon-iris"), // main text
- a: {
- color: theme("colors.rp-moon-iris"), // align w/ in.css
- "text-decoration-color": theme("colors.rp-moon-pine"), // align w/ in.css
- "text-decoration-thickness": "2px", // align w/ in.css
- },
- "h1, h2, h3, h4, h5, h6": {
- color: theme("colors.rp-moon-love"),
- },
- blockquote: {
- "border-color": theme("colors.rp-moon-overlay"),
- "background-color": theme("colors.rp-moon-surface"),
- color: theme("colors.rp-moon-text"),
- },
- strong: {
- color: theme("colors.rp-moon-iris"), // align w/ main text color
- fontWeight: "800",
- },
- th: {
- color: theme("colors.rp-moon-iris"),
- },
- },
- },
- }),
- },
- },
- corePlugins: {
- // preflight: false,
- divideStyle: true,
- },
-};
diff --git a/templates/archives.html b/templates/archives.html
deleted file mode 100644
index 9b15a164..00000000
--- a/templates/archives.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Blog archive - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content %}
-
-
Blog post archive
-
- {% for article in dates %}
- {% set year = article.date.strftime('%Y') %}
- {% if loop.first %}
-
- {% else %}
- {% set prevyear = loop.previtem.date.strftime('%Y') %}
- {% if prevyear != year %}
-
- {% endif %}
- {% endif %}
- {% set month = article.date.strftime('%m') %}
- {% set day = article.date.strftime('%d') %}
- {{ article.locale_date }}: {{ article.title }}
- {%if article.subtitle %}
- {{ article.subtitle }}
- {% endif %}
- {% endfor %}
-
-
-{% endblock %}
diff --git a/templates/article.html b/templates/article.html
deleted file mode 100644
index c4dc973d..00000000
--- a/templates/article.html
+++ /dev/null
@@ -1,139 +0,0 @@
-{% extends "base.html" %}
-{% block html_lang %}{{ article.lang }}{% endblock %}
-
-{% block title %}{{ article.title|striptags }} - {{ SITENAME|striptags }}{% endblock %}
-
-{% block head %}
- {{ super() }}
-
- {% if article.description %}
-
- {% endif %}
-
- {% for tag in article.tags %}
-
- {% endfor %}
-
-{% endblock %}
-
-{% block content %}
-
-
- {{ article.content }}
-
-
-
- {% if article.category %}
-
- Category
-
- {% endif %}
- {% if article.tags %}
-
- Tags
-
- {% endif %}
-
-
- {% if article.category %}
-
- {{ article.category }}
-
- {% endif %}
- {% if article.tags %}
-
- {% for tag in article.tags %}
- {{ tag }} {{"," if not loop.last }}
- {% endfor %}
-
- {% endif %}
-
-
- {% if article.share_post and article.status != 'draft' %}
-
- {% endif %}
-
-
-{% endblock %}
diff --git a/templates/author.html b/templates/author.html
deleted file mode 100644
index 1a5a8903..00000000
--- a/templates/author.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "index.html" %}
-
-{% block title %}{{ SITENAME|striptags }} - Articles by {{ author }}{% endblock %}
-
-{% block content_title %}
- Articles by {{ author }}
-{% endblock %}
diff --git a/templates/authors.html b/templates/authors.html
deleted file mode 100644
index 548421fb..00000000
--- a/templates/authors.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Authors - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content %}
- Authors
- It's all me, silly you.
-
-
-
-
-
-{% endblock %}
diff --git a/templates/base.html b/templates/base.html
deleted file mode 100644
index ad7df453..00000000
--- a/templates/base.html
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
- {% block head %}
- {% block title %}{{ SITENAME|striptags }}{% endblock title %}
-
-
-
- {% if SITESUBTITLE %}
-
- {% endif %}
-
- {% if STYLESHEET_URL %}
-
- {% endif %}
- {% if FEED_ALL_ATOM %}
-
- {% endif %}
- {% if FEED_ALL_RSS %}
-
- {% endif %}
- {% if FEED_ATOM %}
-
- {% endif %}
- {% if FEED_RSS %}
-
- {% endif %}
- {% if CATEGORY_FEED_ATOM and category %}
-
- {% endif %}
- {% if CATEGORY_FEED_RSS and category %}
-
- {% endif %}
- {% if TAG_FEED_ATOM and tag %}
-
- {% endif %}
- {% if TAG_FEED_RSS and tag %}
-
- {% endif %}
-
-
-
-
- {% endblock head %}
-
-
-
-
-
-
-
-
-
- {% if SITESUBTITLE %}
- {{ SITESUBTITLE }}
- {% endif %}
-
-
-
-
-
-
- {% if DISPLAY_PAGES_ON_MENU %}
-
- {% endif %}
- {% for title, link in MENUITEMS %}
- {{ title }}
- {% endfor %}
-
- {% for title, link in LINKS %}
-
{{ title }}
- {% endfor %}
-
- {% if DISPLAY_CATEGORIES_ON_MENU %}
-
- {% for cat, null in categories %}
-
{{ cat}}
- {% endfor %}
-
- {% endif %}
-
-
-
-
-
-
-
-
-
- {% block content %}
- {% endblock %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/templates/categories.html b/templates/categories.html
deleted file mode 100644
index 0f298e8c..00000000
--- a/templates/categories.html
+++ /dev/null
@@ -1,14 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Categories - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content %}
-
-
Blog categories
-
- {% for category, articles in categories|sort %}
- {{ category }} ({{ articles|count }})
- {% endfor %}
-
-
-{% endblock %}
diff --git a/templates/category.html b/templates/category.html
deleted file mode 100644
index 8edc3bc1..00000000
--- a/templates/category.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "index.html" %}
-
-{% block title %}{{ category }} - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content_title %}
-Tales from the {{ category }} department
-{% endblock %}
diff --git a/templates/index.html b/templates/index.html
deleted file mode 100644
index dcdd4cbc..00000000
--- a/templates/index.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
- {% block content_title %}
- Recent blog posts
- {% endblock %}
-
-
- {% for article in articles_page.object_list %}
-
-
-
- {{ article.locale_date }}
-
-
-
-
-
- {% endfor %}
-
-
- {% if articles_page.has_other_pages() %}
- {% include 'pagination.html' %}
- {% endif %}
-{% endblock content %}
diff --git a/templates/page.html b/templates/page.html
deleted file mode 100644
index fbc1985d..00000000
--- a/templates/page.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "base.html" %}
-{% block html_lang %}{{ page.lang }}{% endblock %}
-{% block title %}{{ page.title|striptags }} - {{ SITENAME|striptags }} {%endblock%}
-
-{% block head %}
- {{ super() }}
-{% endblock %}
-
-{% block content %}
-
-
-
{{ page.title }}
- {{ page.content }}
-
-
-{% endblock %}
diff --git a/templates/pagination.html b/templates/pagination.html
deleted file mode 100644
index 933904be..00000000
--- a/templates/pagination.html
+++ /dev/null
@@ -1,60 +0,0 @@
-{% if DEFAULT_PAGINATION %}
-{% set first_page = articles_paginator.page(1) %}
-{% set last_page = articles_paginator.page(articles_paginator.num_pages) %}
-
- {% if articles_page.has_previous() %}
-
-
-
- «
-
-
- {% else %}
-
-
- {% endif %}
-
-
-
- {{ articles_page.number }}
-
-
- {% if articles_page.has_next() %}
-
-
- {{ articles_page.next_page_number() }}
-
-
-
- …
-
-
-
- {{ articles_paginator.num_pages }}
-
-
-
-
- »
-
-
- {% endif %}
-
-
-{% endif %}
diff --git a/templates/tag.html b/templates/tag.html
deleted file mode 100644
index 0696ceb2..00000000
--- a/templates/tag.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "index.html" %}
-
-{% block title %} {{ tag|capitalize }} - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content_title %}
-Tales tagged with {{ tag }}
-{% endblock %}
diff --git a/templates/tags.html b/templates/tags.html
deleted file mode 100644
index 3280842f..00000000
--- a/templates/tags.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Tags - {{ SITENAME|striptags }}{% endblock %}
-
-{% block content %}
- Article tags
-
-
- {% for tag in tag_cloud %}
-
-
- {{ tag.0 }}
-
- {% if TAG_CLOUD_BADGE %}
- {{ tag.2 }}
- {% endif %}
-
- {% endfor %}
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..5dd36c36
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,11 @@
+# This tests the unified codebase (py27, py33) of Pelican.
+# depends on some external libraries that aren't released yet.
+
+[tox]
+envlist = py27,py33,py34
+
+[testenv]
+commands =
+ python -m unittest discover
+deps =
+ -rdev_requirements.txt