1
0
Fork 0
forked from github/pelican

Compare commits

..

1 commit

Author SHA1 Message Date
Alexis Métaireau
0550c6ef29 add multiple authors 2013-07-05 01:08:45 +02:00
349 changed files with 22225 additions and 5136 deletions

View file

@ -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

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

16
.gitignore vendored
View file

@ -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

9
.hgignore Normal file
View file

@ -0,0 +1,9 @@
syntax: glob
output/*
*.pyc
MANIFEST
build
dist
docs/_build
Paste-*
*.egg-info

29
.hgtags Normal file
View file

@ -0,0 +1,29 @@
7acafbf7e47b1287525026ad8b4f1efe443d5403 1.2
7acafbf7e47b1287525026ad8b4f1efe443d5403 1.2
ae850ab0fd62a98a98da7ce74ac794319c6a5066 1.2
54a0309f79d6c5b54d8e1e3b5e3f744856b68a73 1.1
8f5e0eb037768351eb08840e588a4364266a69b3 1.1.1
bb986ed591734ca469f726753cbc48ebbfce0dcc 1.2.1
8a3dad99cbfa6bb5d0ef073213d0d86e0b4c5dba 1.2.2
4a20105a242ab154f6202aa6651979bfbb4cf95e 1.2.3
803aa0976cca3dd737777c640722988b1f3769fe 1.2.4
703c4511105fd9c8b85afda951a294c194e7cf3e 1.2.5
6e46a40aaa850a979f5d09dd95d02791ec7ab0ef 2.0
bf14d1a5c1fae9475447698f0f9b8d35c551f732 2.1
da86343ebd543e5865050e47ecb0937755528d13 2.1.1
760187f048bb23979402f950ecb5d3c5493995b1 2.2
20aa16fe4daa3b70f6c063f170edc916b49837ed 2.3
f9c1d94081504f21f5b2ba147a38099e45db1769 2.4
e65199a0b2706d2fb48f7a3c015e869716e0bec1 2.4.1
89dbd7b6f114508eae62fc821326f4797dfc8b23 2.4.2
979b4473af56a191a278c83058bc9c8fa1fde30e 2.4.3
26a444fbb78becae358afa0a5b47587db8739b21 2.4.4
3542b65fd1963ae7065b6a3bc912fbb6c150e98c 2.4.5
87745dfdd51b96bf18eaaf6c402effa902c1b856 2.5.0
294a2830a393d5a97671dc211dbdb5254a15e604 2.5.1
294a2830a393d5a97671dc211dbdb5254a15e604 2.5.1
92b31e41134cb2c1a156ce623338cf634d2ebc3e 2.5.1
7d728f8e771cbbc802ce81e424e08a8eecbd48dc 2.5.2
7d728f8e771cbbc802ce81e424e08a8eecbd48dc 2.5.2
6d368a1739a4ce48d2d04b00db04fa538e2bf90a 2.5.2
1f9dd44b546425216b1fa35fd88d3d532da8916b 2.5.3

24
.mailmap Normal file
View file

@ -0,0 +1,24 @@
Alexis Métaireau <alexis@notmyidea.org>
Alexis Métaireau <alexis@notmyidea.org> <alexis, notmyidea, org>
Alexis Métaireau <alexis@notmyidea.org> <ametaireau@gmail.com>
Axel Haustant <noirbizarre@gmail.com> <axel.haustant.ext@mappy.com>
Axel Haustant <noirbizarre@gmail.com> <axel.haustant@valtech.fr>
Dave Mankoff <mankyd@gmail.com>
Feth Arezki <feth@tuttu.info>
Guillaume <guillaume@lame.homelinux.com>
Guillaume <guillaume@lame.homelinux.com> <guillaume@mint.(none)>
Guillaume B <guitreize@gmail.com>
Guillermo López <guilan70@hotmail.com>
Guillermo López <guilan70@hotmail.com> <guillermo.lopez@outlook.com>
Jomel Imperio <jimperio@gmail.com>
Justin Mayer <entrop@gmail.com>
Justin Mayer <entrop@gmail.com> <entroP@gmail.com>
Marco Milanesi <kpanic@gnufunk.org> <marcom@openquake.org>
Massimo Santini <santini@dsi.unimi.it> <santini@spillane.docenti.dsi.unimi.it>
Rémy HUBSCHER <hubscher.remy@gmail.com> <remy.hubscher@ionyse.com>
Simon Conseil <contact@saimon.org>
Simon Liedtke <liedtke.simon@googlemail.com>
Skami18 <skami@skami-laptop.dyndns.org>
Stuart Colville <muffinresearchlabs@gmail.com> <muffinresearch@gmail.com>
Stéphane Bunel <stephane@lutetium.(none)>
tBunnyMan <WagThatTail@Me.com>

14
.travis.yml Normal file
View file

@ -0,0 +1,14 @@
language: python
python:
- "2.7"
- "3.3"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq --no-install-recommends asciidoc
- sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8
install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi
- pip install mock
- pip install .
- pip install Markdown
script: python -m unittest discover

40
CONTRIBUTING.rst Normal file
View file

@ -0,0 +1,40 @@
Contribution submission guidelines
==================================
* Consider whether your new feature might be better suited as a plugin_. Folks
are usually available in the `#pelican IRC channel`_ if help is needed to
make that determination.
* `Create a new git branch`_ specific to your change (as opposed to making
your commits in the master branch).
* **Don't put multiple 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.
* Adhere to PEP8 coding standards whenever possible.
* Check for unnecessary whitespace via ``git diff --check`` before committing.
* **Add docs and tests for your changes**.
* `Run all the tests`_ **on both Python 2.7 and 3.3** to ensure nothing was
accidentally broken.
* 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).
* 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 <https://github.com/defunkt/hub/#installation>`_ and then run
`hub pull-request <https://github.com/defunkt/hub/#git-pull-request>`_ to
turn your GitHub issue into a pull request containing your code.
Check out our `Git Tips`_ page or ask on the `#pelican IRC channel`_ 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
.. _`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

673
LICENSE
View file

@ -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. <http://fsf.org/>
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.

3
MANIFEST.in Normal file
View file

@ -0,0 +1,3 @@
include *.rst
recursive-include pelican *.html *.css *png *.in *.rst *.md *.mkd *.xml *.py
include LICENSE THANKS docs/changelog.rst

View file

@ -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 `<footer>` 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
```

68
README.rst Normal file
View file

@ -0,0 +1,68 @@
Pelican
=======
.. image:: https://secure.travis-ci.org/getpelican/pelican.png?branch=master
:target: http://travis-ci.org/getpelican/pelican
:alt: Travis-ci: continuous integration 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)
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`_.
Feedback / Contact us
---------------------
If you want to see new features in Pelican, don't hesitate to offer
suggestions, clone the repository, etc. There are many ways to contribute_.
That's open source, dude!
Send a message to "authors at getpelican dot com" with any requests/feedback! You
can also join the team at `#pelican on Freenode`_ (or if you don't have an IRC
client handy, use the webchat_ for quick feedback.
.. 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 on Freenode`: irc://irc.freenode.net/pelican
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4
.. _contribute: http://docs.getpelican.com/en/latest/contribute.html

157
THANKS Normal file
View file

@ -0,0 +1,157 @@
Pelican is a project originally created by Alexis Métaireau
<http://notmyidea.org/>, 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:
<http://docs.getpelican.com/en/latest/contribute.html>
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
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

8
dev_requirements.txt Normal file
View file

@ -0,0 +1,8 @@
# Tests
mock
# Optional Packages
Markdown
BeautifulSoup4
lxml
typogrify

130
docs/Makefile Normal file
View file

@ -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 <target>' where <target> 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."

BIN
docs/_static/overall.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/_static/pelican.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/_static/pelican.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
docs/_static/theme-basic.zip vendored Normal file

Binary file not shown.

BIN
docs/_static/uml.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

3
docs/_themes/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.pyc
*.pyo
.DS_Store

22
docs/_themes/pelican/layout.html vendored Normal file
View file

@ -0,0 +1,22 @@
{% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
</div>
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}

View file

@ -0,0 +1,254 @@
/*
* pelican.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- pelican theme, based on the nature theme
*
* :copyright: Copyright 2011 by Alexis Metaireau.
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Arial, sans-serif;
font-size: 100%;
background-color: white;
color: #555;
margin: 0;
padding: 0;
}
div.documentwrapper {
width: 70%;
margin: auto;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
hr {
border: 1px solid #B1B4B6;
}
div.document {
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
font-size: 0.9em;
}
div.footer {
color: #555;
width: 100%;
padding: 13px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #444;
text-decoration: underline;
}
div.related {
background-color: #6BA81E;
line-height: 32px;
color: #fff;
text-shadow: 0px 1px 0 #444;
font-size: 0.9em;
}
div.related a {
color: #E2F3CC;
}
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
}
div.sphinxsidebarwrapper{
padding: 20px 0;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Arial, sans-serif;
color: #222;
font-size: 1.2em;
font-weight: normal;
margin: 0;
padding: 5px 10px;
background-color: #ddd;
text-shadow: 1px 1px 0 white
}
div.sphinxsidebar h4{
font-size: 1.1em;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
div.sphinxsidebar p.topless {
}
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #000;
}
div.sphinxsidebar a {
color: #444;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar input[type=text]{
margin-left: 20px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #005B81;
text-decoration: none;
}
a:hover {
color: #E32E00;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Arial, sans-serif;
font-weight: normal;
color: #212224;
margin: 30px 0px 10px 0px;
padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h1 {
border-top: 20px solid white;
margin-top: 0;
font-size: 250%;
text-align: center;
}
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li {
line-height: 1.5em;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.highlight{
background-color: #111;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 10px;
background-color: #111;
color: #fff;
line-height: 1.2em;
border: 1px solid #C6C9CB;
font-size: 1.1em;
margin: 1.5em 0 1.5em 0;
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
font-size: 1.1em;
font-family: monospace;
}
.viewcode-back {
font-family: Arial, sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

10
docs/_themes/pelican/theme.conf vendored Normal file
View file

@ -0,0 +1,10 @@
[theme]
inherit = basic
stylesheet = pelican.css
nosidebar = true
pygments_style = fruity
[options]
index_logo_height = 120px
index_logo =
github_fork =

190
docs/changelog.rst Normal file
View file

@ -0,0 +1,190 @@
Release history
###############
3.3 (XXXX-XX-XX)
================
* 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``
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

50
docs/conf.py Normal file
View file

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys, os
sys.path.append(os.path.abspath(os.pardir))
from pelican import __version__, __major__
# -- General configuration -----------------------------------------------------
templates_path = ['_templates']
extensions = ['sphinx.ext.autodoc',]
source_suffix = '.rst'
master_doc = 'index'
project = 'Pelican'
copyright = '2010, Alexis Metaireau and contributors'
exclude_patterns = ['_build']
version = __version__
release = __major__
# -- Options for HTML output ---------------------------------------------------
html_theme_path = ['_themes']
html_theme = 'pelican'
html_theme_options = {
'nosidebar': True,
'index_logo': 'pelican.png',
'github_fork': 'getpelican/pelican',
}
html_static_path = ['_static']
# Output file base name for HTML help builder.
htmlhelp_basename = 'Pelicandoc'
# -- 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)
]

144
docs/contribute.rst Normal file
View file

@ -0,0 +1,144 @@
How to contribute
#################
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 <https://github.com/getpelican/pelican/issues>`_.
Don't hesitate to fork Pelican and submit a 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 <http://www.virtualenv.org/>`_. 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 .
Coding standards
================
Try to respect what is described in the `PEP8 specification
<http://www.python.org/dev/peps/pep-0008/>`_ when making contributions. This
can be eased via the `pep8 <http://pypi.python.org/pypi/pep8>`_ or `flake8
<http://pypi.python.org/pypi/flake8/>`_ tools, the latter of which in
particular will give you some useful hints about ways in which the
code/formatting can be improved.
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::
$ pelican -o pelican/tests/output/custom/ -s samples/pelican.conf.py \
samples/content/
$ 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.

189
docs/faq.rst Normal file
View file

@ -0,0 +1,189 @@
Frequently Asked Questions (FAQ)
################################
Here are some frequently asked questions about Pelican.
What's the best way to communicate a problem, question, or suggestion?
======================================================================
If you have a problem, question, or suggestion, please start by striking up a
conversation on `#pelican on Freenode <irc://irc.freenode.net/pelican>`_.
Those who don't have an IRC client handy can jump in immediately via
`IRC webchat <http://webchat.freenode.net/?channels=pelican&uio=d4>`_. 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're unable to resolve your issue or if you have a feature request, please
refer to the `issue tracker <https://github.com/getpelican/pelican/issues>`_.
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
<https://github.com/getpelican/pelican/issues>`_. If submitting an issue
report, please check the existing issue list first in order to avoid submitting
a duplicate issue.
If you want to contribute, please fork `the git repository
<https://github.com/getpelican/pelican/>`_, 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 <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 <http://pygments.org/demo/15101/>`_.
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.
===========================================
Markdown is not a hard dependency for Pelican, so you will need to explicitly
install it. You can do so by typing the following command, prepending ``sudo``
if permissions require it::
pip install markdown
If you don't have ``pip`` installed, consider installing it via::
easy_install pip
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 the template::
{% if article.modified %}
Last modified: {{ article.modified }}
{% endif %}
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!
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
Please note that ``None`` and ``''`` are not the same thing. The word ``None``
should not be surrounded by quotes.
I'm getting a warning about feeds generated without SITEURL being set properly
==============================================================================
`RSS and Atom feeds require all URL links to be absolute
<http://validator.w3.org/feed/docs/rss2.html#comments>`_.
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.

20
docs/fr/astuces.rst Normal file
View file

@ -0,0 +1,20 @@
Trucs et astuces pour Pelican
#############################
Personnaliser l'url d'un article pour Pelican
=============================================
Par défaut, quand vous créez un article ayant pour titre *Mon article pour Pelican*,
l'url par défaut devient *mon-article-pour-pelican.html*. Cependant, il est possible
de modifier cela en utilisant la technique utilisée pour les traductions d'article,
c'est à dire le paramètre *:slug:* ::
Mon article pour Pelican
########################
:date: 2011-01-31 11:05
:slug: super-article-pour-pelican
bla, bla, bla …
En prenant cet exemple ci dessus, votre url deviendra *super-article-pour-pelican.html*

58
docs/fr/bases.rst Normal file
View file

@ -0,0 +1,58 @@
Les bases de Pelican
####################
Créer son premier article
=========================
Pour créer notre premier article, nous allons éditer un fichier, par exemple premier_article.rst ::
Premier article pour Pelican
############################
:author: Guillaume
:date: 2011-01-08 10:20
:category: GNU-Linux
:tags: tutoriel, git
Ceci est un tutoriel pour configurer git.
Bla, bla, bla ....
Maintenant que ce fichier est créé, on va lancer la création du blog ::
pelican .
Vous aller obtenir une sortie comme celle ci — $PATH représente le dossier où vous
avez créé votre article ::
[ok] writing $PATH/output/feeds/all.atom.xml
[ok] writing $PATH/output/feeds/GNU/Linux.atom.xml
[ok] writing $PATH/output/feeds/all-en.atom.xml
[ok] writing $PATH/output/premier-article-pour-pelican.html
[ok] writing $PATH/output/index.html
[ok] writing $PATH/output/tags.html
[ok] writing $PATH/output/categories.html
[ok] writing $PATH/output/archives.html
[ok] writing $PATH/output/tag/tutoriel.html
[ok] writing $PATH/output/tag/git.html
[ok] writing $PATH/output/category/GNU-Linux.html
Première analyse
================
Nous allons décortiquer un peu tout ça ensemble.
* Un dossier output/ a été créé pour y mettre le fichiers xml et html du blog.
* Dans le dossier feeds/, nous retrouvons les différents flux de syndication.
* Le fichier de larticle et la page principale du blog a été généré.
* Le répertoire tag/ propose une page par tag.
* La page correspondant à la catégorie est générée dans le répertoire category/
Si vous ouvrez le fichier index.html — ou un autre — avec votre navigateur, vous
remarquerez que :
* Le thème utilisé par défaut est notmyidea
* Le nom du blog est A Pelican Blog.
Bien évidemment, il y a des paramètres de base que lon peut modifier pour mettre
un peu tout ça à sa sauce. Cest ce que nous allons voir au travers du fichier de configuration.

165
docs/fr/configuration.rst Normal file
View file

@ -0,0 +1,165 @@
Fichier de configuration
************************
On va créer un fichier de configuration que lon va appeler **settings.py**. On peut
utiliser Pelican sans faire ce fichier, mais il faudrait à chaque fois passer les paramètres
en ligne de commande. Et comme il va nous servir à faire dautres choses bien utile,
autant lappréhender de suite. Cependant, nous nallons voir que la base pour linstant.
Paramètres de base
==================
AUTHOR :
Désigne lauteur par défaut ;
DEFAULT_CATEGORY :
La catégorie par défaut des articles. Si ce paramètre nest
pas documenté, il prendra la valeur misc — pour miscellaneous (divers en français) ;
SITENAME :
Le nom de votre site ;
OUTPUT_PATH :
Le répertoire de sortie du blog.
Quand je dis quon va faire simple, on fait simple !
Passons donc à ce quoi doit ressembler le fichier de configuration ::
# -*- coding: utf-8 -*-
AUTHOR = "Guillaume"
DEFAULT_CATEGORY = "GNU-Linux"
SITENAME = "Free Culture"
Si vous avez un serveur comme Apache de configuré pour votre machine, vous
pouvez paramétrer le répertoire de sortie vers **/var/www/blog** par exemple ::
OUTPUT_PATH = "/var/www/blog"
Une remarque importante. Si vous avez besoin de passer un caractère accentué, il
faut le préciser que la chaine est en unicode en faisant par exemple
*AUTHOR = u"Guillaume LAMÉ"*
Pour bien vérifier que les paramètres sont bien pris en compte, nous allons enlever les lignes *:author: Guillaume* et *:category: GNU-Linux* de notre fichier
**premier_article.rst** et regénérer le blog.
Rafraichissez votre page, ce devrait être bon.
Nous allons maintenant passer en revue les différents paramètres de Pelican. Je les
ai regroupé par thème. Cependant, cest surtout un listing avant de rentrer dans les
détails au prochain chapitre.
Flux de syndication
===================
CATEGORY_FEED_ATOM :
Chemin décriture des flux Atom liés aux catégories ;
CATEGORY_FEED_RSS :
Idem pour les flux rss (Optionnel);
FEED_ATOM :
Chemin du flux Atom global;
FEED_RSS :
Chemin du flux Rss global (Optionnel);
FEED_ALL_ATOM :
Chemin du flux Atom global qui inclut la totalité des posts, indépendamment de la langue;
FEED_ALL_RSS :
Chemin du flux Rss global qui inclut la totalité des posts, indépendamment de la langue (Optionnel);
TAG_FEED_ATOM :
Chemin des flux Atom pour les tags (Optionnel);
TAG_FEED_RSS :
Chemin des flux Rss pour les tags (Optionnel).
Traductions
===========
DEFAULT_LANG :
Le langage par défaut à utiliser. «*en*» par défaut ;
TRANSLATION_FEED_ATOM :
Chemin du flux Atom pour les traductions.
TRANSLATION_FEED_RSS :
Chemin du flux RSS pour les traductions.
Thèmes
======
CSS_FILE :
Fichier css à utiliser si celui-ci est différent du fichier par défaut (*main.css*) ;
DISPLAY_PAGES_ON_MENU :
Affiche ou non les pages statiques sur le menu du thème ;
DISQUS_SITENAME :
Indiquer le nom du site spécifié sur Disqus ;
GITHUB_URL :
Indiquez votre url Github ;
GOOGLE_ANALYTICS :
'UA-XXXX-YYYY' pour activer Google analytics ;
GOSQUARED_SITENAME :
'XXX-YYYYYY-X' pour activer GoSquared ;
JINJA_EXTENSIONS :
Liste d'extension Jinja2 que vous souhaitez utiliser ;
LINKS :
Une liste de tuples (Titre, url) pour afficher la liste de lien ;
PDF_PROCESSOR :
Génère ou non les articles et pages au format pdf ;
NEWEST_FIRST_ARCHIVES :
Met les articles plus récent en tête de l'archive ;
SOCIAL :
Une liste de tuples (Titre, url) pour afficher la liste de lien dans la section "Social" ;
STATIC_THEME_PATHS :
Répertoire du thème que vous souhaitez importer dans l'arborescence finale ;
THEME :
Thème à utiliser:
TWITTER_USERNAME :
Permet d'afficher un bouton permettant le tweet des articles.
Pelican est fournit avec :doc:`pelican-themes`, un script permettant de gérer les thèmes
Paramètres divers
=================
DEFAULT_DATE:
Date par défaut à utiliser si l'information de date n'est pas spécifiée
dans les metadonnées de l'article.
Si 'fs', Pelican se basera sur le *mtime* du fichier.
Si c'est un tuple, il sera passé au constructeur datetime.datetime pour
générer l'objet datetime utilisé par défaut.
KEEP_OUTPUT DIRECTORY :
Ne génère que les fichiers modifiés et n'efface pas le repertoire de sortie ;
MARKUP :
Langage de balisage à utiliser ;
PATH :
Répertoire à suivre pour les fichiers inclus ;
SITEURL :
URL de base de votre site ;
STATIC_PATHS :
Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ;

18
docs/fr/conventions.rst Normal file
View file

@ -0,0 +1,18 @@
Conventions
###########
Environnement de test
=====================
Les exemples sont basées sur une distribution Debian. Pour les autres distributions,
il y aura des ajustements à faire, notamment pour linstallation de Pelican. Les
noms des paquets peuvent changer.
Conventions typographiques
==========================
Un petit rappel concernant les codes sources.
* $ correspond à une ligne à exécuter en tant quutilisateur courant du systême ;
* # correspond à une ligne à exécuter en tant que root ;
* **settings.py** : Les noms des répertoires et fichiers sont en gras.

40
docs/fr/faq.rst Normal file
View file

@ -0,0 +1,40 @@
Foire aux questions (FAQ)
#########################
Voici un résumé des questions fréquemment posées pour pelican.
Est-il obligatoire d'avoir un fichier de configuration ?
========================================================
Non. Les fichiers de configuration sont juste un moyen facile de configurer
pelican. Pour les opérations de base, il est possible de spécifier des
options
en invoquant pelican avec la ligne de commande (voir pelican --help pour
plus
d'informations à ce sujet)
Je crée mon propre thème, comment utiliser pygments?
====================================================
Pygment ajoute quelques classes au contenu généré, de sorte qua colorisation
de votre thème se fait grâce à un fichier css. Vous pouvez jeter un oeil à
celui proposé par`sur le site du projet <http://pygments.org/demo/15101/>`_
Comment puis-je créer mon propre thèm
=====================================
Vueillez vous référer à :ref:`theming-pelican-fr`.
Comment puis-je aider?
======================
Vous avez plusieurs options pour aider. Tout d'abord, vous pouvez utiliser
le
pélican, et signaler toute idée ou problème que vous avez sur le bugtracker
.
Si vous voulez contribuer, jeter un oeil au dépôt git , ajoutez vos
modifications et faites une demande, je les regarderai dès que possible
Vous pouvez aussi contribuer en créant des thèmes, et/ou compléter la
documentation.

57
docs/fr/index.rst Normal file
View file

@ -0,0 +1,57 @@
Pelican
#######
Pelican est un generateur de blog simple codé en python
* Écrivez vos articles directement dans votre éditeur favori (vim !) et
directement en syntaxe reStructuredText ou Markdown ;
* Un outil simple en ligne de conmmande pour (re)générer le blog ;
* Sortie complètement statique, facile pour l'héberger n'importe où ;
Fonctionnalités
===============
Pelican supporte actuellement :
* des articles de blog ;
* des pages statiques ;
* les commentaires via un service externe (`disqus <http://disqus.com>`_)
Notez qu'étant bien un service externe assez pratique, vous ne gérez pas
vous même les commentaires. Ce qui pourrait occasionner une perte de vos données;
* support de template (les templates sont crées avec `jinja2 <http://jinjna.pocoo.org>`_) ;
* génération optionnelle de vos pages et articles en pdf.
Pourquoi le nom "Pelican" ?
============================
Vous n'avez pas remarqué ? "Pelican" est un anagramme pour "Calepin" ;)
Code source
===========
Vous pouvez accéder au code source via git à l'adresse
http://github.com/getpelican/pelican/
Feedback !
==========
Si vous voulez de nouvelles fonctionnalitées pour Pelican, n'hésitez pas à nous le dire,
à cloner le dépôt, etc … C'est open source !!!
Contactez Alexis à "alexis at notmyidea dot org" pour quelques requêtes ou retour d'expérience que ce soi !
Documentation
=============
.. toctree::
:maxdepth: 2
conventions
installation
bases
configuration
themes
parametres_article
astuces
faq
pelican-themes

67
docs/fr/installation.rst Normal file
View file

@ -0,0 +1,67 @@
Installation et mise à jour de Pelican
######################################
Installation
============
Il y a deux façons dinstaller Pelican sur son système. La première est via lutilitaire
pip, lautre façon est de télécharger Pelican via Github. Ici nous allons voir les deux
façons de procéder.
Via pip
-------
Pour installer Pelican via pip, vous aurez besoin du paquet python-pip. puis installez Pelican ::
# apt-get install python-pip
# pip install pelican
Via Github
----------
Pour installer Pelican en reprenant le code via Github, nous aurons besoin du paquet
git-core pour récupérez les sources de Pelican. Puis nous procédons à linstallation ::
# apt-get install git-core
$ git clone https://github.com/getpelican/pelican.git
$ cd pelican
# python setup.py install
Mises à jour
============
Via pip
-------
Rien de bien compliqué pour mettre à jour via pip ::
$ cd votreRepertoireSource
$ pip install --upgrade pelican
Via Github
----------
C'est un peu plus long avec Github par contre ::
$ cd votreRepertoireSource
$ git pull origin master
$ cd pelican
# python setup.py install
Vous aurez un message derreur si le module setuptools de python nest pas installé.
La manipulation est la suivante ::
# apt-get install python-setuptools
Alors, quelle méthode choisir ?
===============================
Vous avez le choix entre deux méthodes, mais aussi entre deux concepts. La méthode
de Github est la version de développement, où les modifications arrivent assez
fréquemment sans être testées à fond. La version de pip est une version arrêtée avec un
numéro de version dans laquelle vous aurez moins de bug. Noubliez cependant pas
que le projet est très jeune et manque donc de maturité. Si vous aimez avoir les toutes
dernières versions utilisez Github, sinon penchez vous sur pip.

View file

@ -0,0 +1,106 @@
Les paramètres des articles dans Pelican
########################################
Les catégories
==============
Nous avons vu que pour affecter un article à une catégorie, nous avions le paramètre *:category:*.
Il y a cependant plus simple, affecter un répertoire à une catégorie.
Dans le répertoire ou vous avez vos articles, créez le repertoire **GNU-Linux** et déplacez y le fichier
**premier_article.rst**. Bien évidemment nous ne verront pas la différence, car jusqu'ici *GNU-Linux*
est notre catégorie par défaut.
Nous allons faire un autre exemple d'article avec la catégorie Pelican. Créez le répertoire **Pelican**
et collez cette exemple d'article ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation
Il y a quand même pas mal de boulot pour faire une documentation !
Et lancez la compilation du blog. Vous voyez que la catégorie est affectée automatiquement.
Les tags
========
Pour les tags, il n'y a rien de compliqué. il suffit de mettre le(s) tags séparés si besoin d'une virgule. ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation, pelican
Par contre, par soucis de clarté au niveau des url je vous conseille de mettre les expression de plusieurs
mots séparées par des tirets ::
:tags: mise-a-jour
et non ::
:tags: mise a jour
Les auteurs
===========
Par défaut, vous pouvez indiqué votre nom en tant qu'auteur dans le fichier de configuration.
S'il y a plusieurs auteurs pour le site, vous pouvez le définir manuellement dans
l'article avec la méta-donnée ::
:author: Guillaume
La date
=======
La date se met au format anglophone : **YYYY-MM-DD hh:mm** ::
:date: 2011-01-31 14:12
Les traductions
===============
Pelican permet de générer un blog multilingue assez facilement. Pour cela nous devons :
* Définir la langue de base du blog ;
* Donner une référence à l'article initial ;
* Définir la langue du fichier traduit et y reporter la référence.
Pour définir la langue de base nous allons modifier le fichier **settings.py** et y rajouter la ligne suivante ::
DEFAULT_LANG = "fr"
Puis ajouter la référence dans notre article d'origine qui deviendra ::
Préparation de la documentation
###############################
:date: 2011-01-27 15:28
:tags: documentation
:slug: preparation-de-la-documentation
Il y a quand même pas mal de boulot pour faire une documentation !
Nous n'avons plus qu'à créer l'article en anglais ::
Start of documentation
######################
:slug: preparation-de-la-documention
:lang: en
There are still a lot of work to documentation !
**Il est important de comprendre que la valeur de :slug: deviendra votre url. Ne mettez donc pas un diminutif pour
identifier l'article**
Rien de plus à savoir pour traduire efficacement des articles.
Maintenant que vous avez toutes les clés en main pour créer un article, nous allons passer à la personnalisation
du fichier de configuration.

172
docs/fr/pelican-themes.rst Normal file
View file

@ -0,0 +1,172 @@
pelican-themes
##############
Description
===========
``pelican-themes`` est un outil en lignes de commandes pour gérer les thèmes de Pelican.
Utilisation:
""""""""""""
| pelican-themes [-h] [-l] [-i *chemin d'un thème* [*chemin d'un thème* ...]]
| [-r *nom d'un thème* [*nom d'un thème* ...]]
| [-s *chemin d'un thème* [*chemin d'un thème* ...]] [-v] [--version]
Arguments:
""""""""""
-h, --help Afficher l'aide et quitter
-l, --list Montrer les thèmes installés
-i chemin, --install chemin Chemin(s) d'accès d'un ou plusieurs thème à installer
-r nom, --remove nom Noms d'un ou plusieurs thèmes à installer
-s chemin, --symlink chemin Fonctionne de la même façon que l'option ``--install``, mais crée un lien symbolique au lieu d'effectuer une copie du thème vers le répertoire des thèmes.
Utile pour le développement de thèmes.
-v, --verbose Sortie détaillée
--version Affiche la version du script et quitte
Exemples
========
Lister les thèmes installés
"""""""""""""""""""""""""""
``pelican-themes`` peut afficher les thèmes disponibles.
Pour cela, vous pouvez utiliser l'option ``-l`` ou ``--list``, comme ceci:
.. code-block:: console
$ pelican-themes -l
notmyidea
two-column@
simple
$ pelican-themes --list
notmyidea
two-column@
simple
Dans cet exemple, nous voyons qu'il y a trois thèmes d'installés: ``notmyidea``, ``simple`` and ``two-column``.
``two-column`` est suivi d'un ``@`` par ce que c'est un lien symbolique (voir `Créer des liens symboliques`_).
Notez que vous pouvez combiner l'option ``--list`` avec l'option ``--verbose``, pour afficher plus de détails:
.. 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
Installer des thèmes
""""""""""""""""""""
Vous pouvez installer un ou plusieurs thèmes en utilisant l'option ``-i`` ou ``--install``.
Cette option prends en argument le(s) chemin(s) d'accès du ou des thème(s) que vous voulez installer, et peut se combiner avec l'option ``--verbose``:
.. 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
Supprimer des thèmes
""""""""""""""""""""
``pelican-themes`` peut aussi supprimer des thèmes précédemment installés grâce à l'option ``-r`` ou ``--remove``.
Cette option prends en argument le ou les nom(s) des thèmes que vous voulez installer, et peux se combiner avec l'option ``--verbose``:
.. code-block:: console
# pelican-themes --remove two-column
.. code-block:: console
# pelican-themes -r martyachin notmyidea-cmd -v
Créer des liens symboliques
"""""""""""""""""""""""""""
L'option ``-s`` ou ``--symlink`` de ``pelican-themes`` permet de lier symboliquement un thème.
Cette option s'utilise exactement comme l'option ``--install``:
.. code-block:: console
# pelican-themes --symlink ~/Dev/Python/pelican-themes/two-column
Dans l'exemple ci dessus, un lien symbolique pointant vers le thème ``two-column`` a été installé dans le répertoire des thèmes de Pelican, toute modification sur le thème ``two-column`` prendra donc effet immédiatement.
Cela peut être pratique pour le développement de thèmes
.. 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
Notez que cette fonctionnalité nécessite d'avoir un système d'exploitation et un système de fichiers supportant les liens symboliques, elle n'est donc pas disponible sous Micro$oft®©™ Fenêtre®©™.
Faire plusieurs choses à la fois
""""""""""""""""""""""""""""""""
Les options ``--install``, ``--remove`` et ``--symlink`` peuvent être employées en même temps, ce qui permets de réaliser plusieurs opérations en même temps:
.. 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
Dans cette exemple, le thème ``notmyidea-cms`` sera remplacé par le thème ``notmyidea-cms-fr`` et le thème ``two-column`` sera lié symboliquement...
À voir également
================
- http://docs.notmyidea.org/alexis/pelican/
- ``/usr/share/doc/pelican/`` si vous avez installé Pelican par le `dépôt APT <http://skami18.github.com/pelican-packages/>`_

171
docs/fr/themes.rst Normal file
View file

@ -0,0 +1,171 @@
.. _theming-pelican:
Cette page est une traduction de la documentation originale, en anglais et
disponible `ici <../themes.html>`_.
Comment créer des thèmes pour Pelican
#####################################
Pelican utlise le très bon moteur de template `jinja2 <http://jinja.pocoo.org>`_
pour produire de l'HTML. La syntaxe de jinja2 est vraiment très simple. Si vous
voulez créer votre propre thème, soyez libre de prendre inspiration sur le theme
"simple" qui est disponible `ici
<https://github.com/getpelican/pelican/tree/master/pelican/themes/simple/templates>`_
Structure
=========
Pour réaliser votre propre thème vous devez respecter la structure suivante ::
├── static
│   ├── css
│   └── images
└── templates
├── archives.html // pour afficher les archives
├── article.html // généré pour chaque article
├── categories.html // doit lister toutes les catégories
├── category.html // généré pour chaque catégorie
├── index.html // la page d'index, affiche tous les articles
├── page.html // généré pour chaque page
├── tag.html // généré pour chaque tag
└── tags.html // doit lister tous les tags. Peut être un nuage de tag.
* `static` contient tout le contenu statique. Il sera copié dans le dossier
`theme/static`. J'ai mis un dossier css et un image, mais ce sont juste des
exemples. Mettez ce dont vous avez besoin ici.
* `templates` contient tous les templates qui vont être utiliser pour générer les
pages. J'ai juste mis les templates obligatoires ici, vous pouvez définir les
vôtres si cela vous aide à vous organiser pendant que vous réaliser le thème.
Vous pouvez par exemple utiliser les directives {% include %} et {% extends %}
de jinja2.
Templates et variables
======================
Cela utilise une syntaxe simple, que vous pouvez insérer dans vos pages HTML.
Ce document décrit les templates qui doivent exister dans un thème, et quelles
variables seront passées à chaque template, au moment de le générer.
Tous les templates recevront les variables définies dans votre fichier de
configuration, si elles sont en capitales. Vous pouvez y accéder directement.
Variables communes
------------------
Toutes ces variables seront passées à chaque template.
============= ===================================================
Variable Description
============= ===================================================
articles C'est la liste des articles, ordonnée décroissante
par date. Tous les éléments de la liste sont des
objets `Article`, vous pouvez donc accéder à leurs
propriétés (exemple : title, summary, author, etc).
dates La même liste d'articles, ordonnée croissante par
date.
tags Un dictionnaire contenant tous les tags (clés), et
la liste des articles correspondants à chacun
d'entre eux (valeur).
categories Un dictionnaire contenant toutes les catégories
(clés), et la liste des articles correspondants à
chacune d'entre elles (valeur).
pages La liste des pages.
============= ===================================================
index.html
----------
La page d'accueil de votre blog, sera générée dans output/index.html.
Si la pagination est activée, les pages suivantes seront à l'adresse
output/index`n`.html.
=================== ===================================================
Variable Description
=================== ===================================================
articles_paginator Un objet paginator de la liste d'articles.
articles_page La page actuelle d'articles.
dates_paginator Un objet paginator de la liste d'articles, ordonné
par date croissante.
dates_pages La page actuelle d'articles, ordonnée par date
croissante.
page_name 'index'.
=================== ===================================================
category.html
-------------
Ce template sera généré pour chaque catégorie existante, et se retrouvera
finalement à output/category/`nom de la catégorie`.html.
Si la pagination est activée, les pages suivantes seront disponibles à
l'adresse output/category/`nom de la catégorie``n`.html.
=================== ===================================================
Variable Description
=================== ===================================================
category La catégorie qui est en train d'être générée.
articles Les articles dans cette catégorie.
dates Les articles dans cette catégorie, ordonnés par
date croissante.
articles_paginator Un objet paginator de la liste d'articles.
articles_page La page actuelle d'articles.
dates_paginator Un objet paginator de la liste d'articles, ordonné
par date croissante.
dates_pages La page actuelle d'articles, ordonnée par date
croissante.
page_name 'category/`nom de la catégorie`'.
=================== ===================================================
article.html
-------------
Ce template sera généré pour chaque article. Les fichiers .html seront
disponibles à output/`nom de l'article`.html.
============= ===================================================
Variable Description
============= ===================================================
article L'objet article à afficher.
category Le nom de la catégorie de l'article actuel.
============= ===================================================
page.html
---------
Pour chaque page ce template sera généré à l'adresse
output/`nom de la page`.html
============= ===================================================
Variable Description
============= ===================================================
page L'objet page à afficher. Vous pouvez accéder à son
titre (title), slug, et son contenu (content).
============= ===================================================
tag.html
--------
Ce template sera généré pour chaque tag. Cela créera des fichiers .html à
l'adresse output/tag/`nom du tag`.html.
Si la pagination est activée, les pages suivantes seront disponibles à
l'adresse output/tag/`nom du tag``n`.html
=================== ===================================================
Variable Description
=================== ===================================================
tag Nom du tag à afficher.
articles Une liste des articles contenant ce tag.
dates Une liste des articles contenant ce tag, ordonnée
par date croissante.
articles_paginator Un objet paginator de la liste d'articles.
articles_page La page actuelle d'articles.
dates_paginator Un objet paginator de la liste d'articles, ordonné
par date croissante.
dates_pages La page actuelle d'articles, ordonnée par date
croissante.
page_name 'tag/`nom du tag`'.
=================== ===================================================

486
docs/getting_started.rst Normal file
View file

@ -0,0 +1,486 @@
Getting started
###############
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 <http://www.pip-installer.org/>`_::
$ pip install pelican
If you don't have ``pip`` installed, an alternative method is
``easy_install``::
$ easy_install pelican
(Keep in mind that operating systems will often require you to prefix the above
commands 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
$ . 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://github.com/getpelican/pelican#egg=pelican
If you plan on using Markdown as a markup format, you'll need to install the
Markdown library as well::
$ pip install Markdown
If you want to use AsciiDoc you need to install it from `source
<http://www.methods.co.nz/asciidoc/INSTALL.html>`_ or use your operating
system's package manager.
Basic usage
-----------
Once Pelican is installed, you can use it to convert your Markdown or reST
content into HTML via the ``pelican`` command, specifying the path to your
content and (optionally) the path to your 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
Continue reading below for more detail, and check out the Pelican wiki's
`Tutorials <https://github.com/getpelican/pelican/wiki/Tutorials>`_ page for
links to community-published tutorials.
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 either use your browser to open the
files on your disk::
firefox output/index.html
Or run a simple web server using Python::
cd output && python -m SimpleHTTPServer
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.
Dependencies
------------
When Pelican is installed, the following dependent Python packages should be
automatically installed without any action on your part:
* `feedgenerator <http://pypi.python.org/pypi/feedgenerator>`_, to generate the
Atom feeds
* `jinja2 <http://pypi.python.org/pypi/Jinja2>`_, for templating support
* `pygments <http://pypi.python.org/pypi/Pygments>`_, for syntax highlighting
* `docutils <http://pypi.python.org/pypi/docutils>`_, for supporting
reStructuredText as an input format
* `pytz <http://pypi.python.org/pypi/pytz>`_, for timezone definitions
* `blinker <http://pypi.python.org/pypi/blinker>`_, an object-to-object and
broadcast signaling system
* `unidecode <http://pypi.python.org/pypi/Unidecode>`_, for ASCII
transliterations of Unicode text
* `six <http://pypi.python.org/pypi/six>`_, for Python 2 and 3 compatibility
utilities
* `MarkupSafe <http://pypi.python.org/pypi/MarkupSafe>`_, for a markup safe
string implementation
If you want the following optional packages, you will need to install them
manually via ``pip``:
* `markdown <http://pypi.python.org/pypi/Markdown>`_, for supporting Markdown as
an input format
* `typogrify <http://pypi.python.org/pypi/typogrify>`_, for typographical
enhancements
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", which you can optionally add yourself
if you plan to create non-chronological content)::
yourproject/
├── content
│   └── (pages)
├── output
├── develop_server.sh
├── 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. (See *Writing articles using Pelican* section below for
more information about how to format your content.)
Once you have written some content to generate, you can use the ``pelican``
command to generate your site, which will be placed in the output folder.
Alternatively, you can use automation tools that "wrap" the ``pelican`` command
to simplify the process of generating, previewing, and uploading your site. One
such tool is the ``Makefile`` that's automatically created for you when you use
``pelican-quickstart`` to create a skeleton project. 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.
Writing content using Pelican
=============================
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
:tags: thats, awesome
:category: yeah
:slug: my-super-post
:author: Alexis Metaireau
: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
Category: Python
Tags: pelican, publishing
Slug: my-super-post
Author: Alexis Metaireau
Summary: Short version for index and feeds
This is the content of my super blog post.
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::
<html>
<head>
<title>My super title</title>
<meta name="tags" contents="thats, awesome" />
<meta name="date" contents="2012-07-09 22:28" />
<meta name="category" contents="yeah" />
<meta name="author" contents="Alexis Métaireau" />
<meta name="summary" contents="Short version for index and feeds" />
</head>
<body>
This is the content of my super blog post.
</body>
</html>
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`_
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<date>\d{4}-\d{2}-\d{2})_(?P<slug>.*)'``
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.
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.md>`_
`a link relative to current file <|filename|cat/article2.md>`_
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.rst)
[a link relative to current file](|filename|../article1.rst)
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::
![Alt Text](|filename|/images/han.jpg)
Any content can be linked in this way. What happens is that the ``images``
directory gets copied to ``output/static/`` upon publishing. This is
because ``images`` is in the ``settings["STATIC_PATHS"]`` list by default. If
you want to have another directory, say ``pdfs`` you would need to add the
following to ``pelicanconf.py``::
STATIC_PATHS = ['images', 'pdfs']
And then the ``pdfs`` directory would also be copied to ``output/static/``.
Importing an existing blog
--------------------------
It is possible to import your blog from Dotclear, WordPress, and RSS feeds using
a simple script. See :ref:`import`.
Translations
------------
It is possible to translate articles. To do so, you need to add a ``lang`` meta
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!
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
<indented code block goes here>
For Markdown, include the language identifier just above the code block,
indenting both the identifier and code::
A block of text.
:::identifier
<code goes here>
The specified identifier (e.g. ``python``, ``ruby``) should be one that
appears on the `list of available lexers <http://pygments.org/docs/lexers/>`_.
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 page.
.. _virtualenv: http://www.virtualenv.org/
.. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime

107
docs/importer.rst Normal file
View file

@ -0,0 +1,107 @@
.. _import:
=================================
Import from other blog software
=================================
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
- 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] [--feed] [-o OUTPUT]
[-m MARKUP] [--dir-cat] [--dir-page] [--strip-raw] [--disable-slugs]
[-e EMAIL] [-p PASSWORD]
input|api_token
Positional arguments
--------------------
input The input file to read
api_token [Posterous only] api_token can be obtained from http://posterous.com/api/
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)
--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)
--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
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=<email_address> --password=<password> <api_token>
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

81
docs/index.rst Normal file
View file

@ -0,0 +1,81 @@
Pelican
=======
Pelican is a static site generator, written in Python_.
* Write your content directly with your editor of choice (vim!)
in reStructuredText_, Markdown_, or AsciiDoc_ 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
Features
--------
Pelican currently supports:
* Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact")
* 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)
* Publication of articles in multiple languages
* Atom/RSS feeds
* Code syntax highlighting
* PDF generation of the articles/pages (optional)
* Import from WordPress, Dotclear, or RSS feeds
* Integration with external tools: Twitter, Google Analytics, etc. (optional)
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
Feedback / Contact us
---------------------
If you want to see new features in Pelican, don't hesitate to offer suggestions,
clone the repository, etc. There are many ways to :doc:`contribute<contribute>`.
That's open source, dude!
Send a message to "authors at getpelican dot com" with any requests/feedback! You
can also join the team at `#pelican on Freenode`_ (or if you don't have an IRC
client handy, use the webchat_ for quick feedback.
Documentation
-------------
A French version of the documentation is available at :doc:`fr/index`.
.. toctree::
:maxdepth: 2
getting_started
settings
themes
plugins
internals
pelican-themes
importer
faq
tips
contribute
report
changelog
.. Links
.. _Python: http://www.python.org/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _Markdown: http://daringfireball.net/projects/markdown/
.. _AsciiDoc: http://www.methods.co.nz/asciidoc/index.html
.. _Jinja2: http://jinja.pocoo.org/
.. _`Pelican documentation`: http://docs.getpelican.com/latest/
.. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html
.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican
.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4

90
docs/internals.rst Normal file
View file

@ -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, Markdown and AsciiDoc
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 (AsciiDoc, 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 <http://jinja.pocoo.org/>`_ 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(Reader):
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.

164
docs/pelican-themes.rst Normal file
View file

@ -0,0 +1,164 @@
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``
See also
========
- http://docs.notmyidea.org/alexis/pelican/
- ``/usr/share/doc/pelican/`` if you have installed Pelican using the `APT repository <http://skami18.github.com/pelican-packages/>`_

125
docs/plugins.rst Normal file
View file

@ -0,0 +1,125 @@
.. _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 ``PLUGIN_PATH``
in the settings. ``PLUGIN_PATH`` can be an absolute path or a path relative to
the settings file::
PLUGIN_PATH = "plugins"
PLUGINS = ["list", "of", "plugins"]
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
usefull 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__
article_generate_context article_generator, metadata
article_generate_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_finalized article_generator invoked at the end of ArticlesGenerator.generate_context
get_generators generators invoked in Pelican.get_generator_classes,
can return a Generator, or several
generator in a tuple or in a list.
page_generate_context page_generator, metadata
page_generator_init page_generator invoked in the PagesGenerator.__init__
page_generator_finalized page_generator invoked at the end of PagesGenerator.generate_context
content_object_init content_object invoked at the end of Content.__init__ (see note below)
============================= ============================ ===========================================================================
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
========================== ===========================

121
docs/report.rst Normal file
View file

@ -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.
Ive 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.

616
docs/settings.rst Normal file
View file

@ -0,0 +1,616 @@
Settings
########
Pelican is configurable thanks to a configuration file you can pass to
the command line::
$ pelican -s path/to/your/settingsfile.py path
Settings are configured in the form of a Python module (a file). You can see an
example by looking at `/samples/pelican.conf.py
<https://github.com/getpelican/pelican/raw/master/samples/pelican.conf.py>`_
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 (default value) 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 locales" 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.
`FILENAME_METADATA` (``'(?P<date>\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<date>\d{4}-\d{2}-\d{2})_(?P<slug>.*)'``.
See :ref:`path_metadata`.
`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.
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.
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
to use. For the moment, the only available values
are `rst`, `md`, `markdown`, `mkd`, `mdown`, `html`, and `htm`.
`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 <http://pythonhosted.org/Markdown/extensions/>`_
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` (``None``) Path to content directory to be processed by Pelican.
`PAGE_DIR` (``'pages'``) Directory to look at for pages, relative to `PATH`.
`PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages.
`ARTICLE_DIR` (``''``) Directory to look at for articles, relative to `PATH`.
`ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles.
`PDF_GENERATOR` (``False``) Set to ``True`` if you want PDF versions of your documents to be.
generated. You will need to install ``rst2pdf``.
`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`.
`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']``) The static paths you want to have accessible
on the output path "static". By default,
Pelican will copy the "images" folder to the
output folder.
`TIMEZONE` The timezone used in the date information, to
generate Atom and RSS feeds. See the *Timezone*
section below for more info.
`TYPOGRIFY` (``False``) If set to True, several typographical improvements will be
incorporated into the generated HTML via the `Typogrify
<http://static.mintchaos.com/projects/typogrify/>`_
library, which can be installed via: ``pip install typogrify``
`DIRECT_TEMPLATES` (``('index', 'tags', 'categories', '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 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.
`ASCIIDOC_OPTIONS` (``[]``) A list of options to pass to AsciiDoc. See the `manpage
<http://www.methods.co.nz/asciidoc/manpage.html>`_
===================================================================== =====================================================================
.. [#] 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*. Document-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 at the top of
the :doc:`Getting Started <getting_started>` page, 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 URLs 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 at http://bit.ly/cNcJUC for more
information.
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 (default value) What does it do?
==================================================== =====================================================
`ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an ARTICLE.
`ARTICLE_SAVE_AS` (``'{slug}.html'``) The place where we will save an article.
`ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an ARTICLE which doesn't use the
default language.
`ARTICLE_LANG_SAVE_AS` (``'{slug}-{lang}.html'``) The place where we will save an article which
doesn't use the default language.
`PAGE_URL` (``'pages/{slug}.html'``) The URL we will use to link to a page.
`PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page. This value has to be
the same as PAGE_URL or you need to use a rewrite in
your server config.
`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.
`TAGS_URL` (``'tag/{slug}.html'``) The URL to use for the tag list.
`TAGS_SAVE_AS` (``'tags.html'``) The location to save the tag list.
`AUTHOR_URL` (``'author/{slug}.html'``) The URL to use for an author.
`AUTHOR_SAVE_AS` (``'author/{slug}.html'``) The location to save an author.
`AUTHORS_URL` (``'authors.html'``) The URL to use for the author list.
`AUTHORS_SAVE_AS` (``'authors.html'``) The location to save the author list.
`<DIRECT_TEMPLATE_NAME>_SAVE_AS` The location to save content generated from direct
templates. Where <DIRECT_TEMPLATE_NAME> is the
upper case template name.
`ARCHIVES_SAVE_AS` (``'archives.html'``) The location to save the article archives page.
`YEAR_ARCHIVE_SAVE_AS` (False) The location to save per-year archives of your
posts.
`MONTH_ARCHIVE_SAVE_AS` (False) The location to save per-month archives of your
posts.
`DAY_ARCHIVE_SAVE_AS` (False) The location to save per-day archives of your
posts.
==================================================== =====================================================
.. 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 ``False`` to prevent the
relevant page from being generated.
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 this dict using the language name (``lang`` metadata in your post content)
as the key. Regarding available format codes, see `strftime document of python`_ :
.. 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.
.. _strftime document of python: 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<name>…)``. 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 (default value) 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 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.
`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``, ie no RSS tag feed) Relative URL to output the tag RSS feed
`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feed item
quantity is unrestricted by default.
================================================ =====================================================
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
<http://feedburner.google.com>`_ 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 it works pretty well
for small-to-medium blogs, for sites with large quantity of articles it would
be convenient to have a way to paginate the list.
You can use the following settings to configure the pagination.
================================================ =====================================================
Setting name (default value) What does it do?
================================================ =====================================================
`DEFAULT_ORPHANS` (``0``) The minimum number of articles allowed on the
last page. Use this when you don't want to
have a last page with very few articles.
`DEFAULT_PAGINATION` (``False``) The maximum number of articles to include on a
page, not including orphans. False to disable
pagination.
================================================ =====================================================
Tag cloud
=========
If you want to generate a tag cloud with all your tags, you can do so using the
following settings.
================================================ =====================================================
Setting name (default value) What does it do?
================================================ =====================================================
`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 support tag clouds, but it is pretty easy to add::
<ul>
{% for tag in tag_cloud %}
<li class="tag-{{ tag.1 }}"><a href="/tag/{{ tag.0|string|replace(" ", "-" ) }}.html">{{ tag.0 }}</a></li>
{% endfor %}
</ul>
You should then also define a CSS style with the appropriate classes (tag-0 to tag-N, where
N matches `TAG_CLOUD_STEPS` -1).
Translations
============
Pelican offers a way to translate articles. See the :doc:`Getting Started <getting_started>` section for
more information.
===================================================== =====================================================
Setting name (default value) 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 (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 (default value) 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_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.
`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 http://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 <themes>`.
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 = "~/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` 'UA-XXXX-YYYY' to activate Google Analytics.
`GOSQUARED_SITENAME` '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"
Example settings
================
.. literalinclude:: ../samples/pelican.conf.py
:language: python
.. _Jinja custom filters documentation: http://jinja.pocoo.org/docs/api/#custom-filters

347
docs/themes.rst Normal file
View file

@ -0,0 +1,347 @@
.. _theming-pelican:
How to create themes for Pelican
################################
Pelican uses the great `Jinja2 <http://jinja.pocoo.org/>`_ templating engine to
generate its HTML output. Jinja2 syntax is really simple. If you want to
create your own theme, feel free to take inspiration from the `"simple" theme
<https://github.com/getpelican/pelican/tree/master/pelican/themes/simple/templates>`_.
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. I've put the CSS and image folders here, but they are
just examples. Put what you need here.
* `templates` contains all the templates that will be used to generate the content.
I've just put the mandatory templates here; you can define your own if it helps
you 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, if 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 the list of respective 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 with according to your settings and locale
(``DATE_FORMATS``/``DEFAULT_DATE_FORMAT``) and provides a
``locale_date`` attribute. On the other hand, ``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
dates_paginator A paginator object for the article list, ordered by
date, ascending.
dates_page The current page of articles, ordered by date,
ascending.
page_name 'index' -- useful for pagination links
=================== ===================================================
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
dates_paginator A paginator object for the article list, ordered by
date, ascending.
dates_page The current page of articles, ordered by date,
ascending.
page_name AUTHOR_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
dates_paginator A paginator object for the list of articles,
ordered by date, ascending
dates_page The current page of articles, ordered by date,
ascending
page_name CATEGORY_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
============= ===================================================
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
dates_paginator A paginator object for the list of articles,
ordered by date, ascending
dates_page The current page of articles, ordered by date,
ascending
page_name TAG_URL where everything after `{slug}` is removed
-- useful for pagination links
=================== ===================================================
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 the ``index.html`` template from the ``simple`` theme -->
{% extends "index.html" %} <!-- "regular" extending -->
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() }}
<link rel="stylesheet" type="text/css" href="{{ SITEURL }}/theme/css/style.css" />
{% 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>`.

85
docs/tips.rst Normal file
View file

@ -0,0 +1,85 @@
Tips
####
Here are some tips about Pelican that you might find useful.
Publishing to GitHub
====================
`GitHub Pages <https://help.github.com/categories/20/articles>`_ offer an easy
and convenient way to publish Pelican sites. There are `two types of GitHub
Pages <https://help.github.com/articles/user-organization-and-project-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 Project Pages 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 <https://github.com/davisp/ghp-import>`_, which can
be installed with ``easy_install`` or ``pip``, makes this process really easy.
For example, if the sources of your Pelican site are contained in a GitHub
repository, and if you want to publish your Pelican site as Project Pages of
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 as User Pages you need to *push* the content of the
``output`` dir generated by Pelican to the ``master`` branch of your
``<username>.github.com`` 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.com.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.com`` repository's
``master`` branch on GitHub.
.. note::
To publish your Pelican site as User Pages feel free to adjust the 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 pelican content -o output -s pelicanconf.py && ghp-import output && git push origin gh-pages
Tip #2:
To use a `custom domain
<https://help.github.com/articles/setting-up-a-custom-domain-with-pages>`_ with
GitHub Pages you need to have a ``CNAME`` file at the root of your pages. For
that you will add ``CNAME`` file to your ``content``, dir and use the
``FILES_TO_COPY`` setting variable to tell Pelican to copy that file
to the ``output`` dir. For example::
FILES_TO_COPY = (('extra/CNAME', 'CNAME'),)

1556
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +0,0 @@
{
"devDependencies": {
"@tailwindcss/typography": "^0.5.15",
"tailwindcss": "^3.4.17"
}
}

400
pelican/__init__.py Normal file
View file

@ -0,0 +1,400 @@
# -*- 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
from pelican import signals
from pelican.generators import (ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator,
SourceFileGenerator, TemplatePagesGenerator)
from pelican.log import init
from pelican.settings import read_settings
from pelican.utils import clean_output_dir, folder_watcher, file_watcher
from pelican.writers import Writer
__major__ = 3
__minor__ = 2
__micro__ = 0
__version__ = "{0}.{1}.{2}".format(__major__, __minor__, __micro__)
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.markup = settings['MARKUP']
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_PATH to system path')
_sys_path = sys.path[:]
sys.path.insert(0, self.settings['PLUGIN_PATH'])
for plugin in self.settings['PLUGINS']:
# if it's a string, then import it
if isinstance(plugin, six.string_types):
logger.debug("Loading plugin `{0}`".format(plugin))
try:
plugin = __import__(plugin, globals(), locals(), str('module'))
except ImportError as e:
logger.error("Can't find plugin `{0}`: {1}".format(plugin, e))
continue
logger.debug("Registering plugin `{0}`".format(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', 'ARTICLE_SAVE_AS',
'ARTICLE_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]))
if self.settings.get('FEED', False):
logger.warning('Found deprecated `FEED` in settings. Modify FEED'
' to FEED_ATOM in your settings and theme for the same behavior.'
' Temporarily setting FEED_ATOM for backwards compatibility.')
self.settings['FEED_ATOM'] = self.settings['FEED']
if self.settings.get('TAG_FEED', False):
logger.warning('Found deprecated `TAG_FEED` in settings. Modify '
' TAG_FEED to TAG_FEED_ATOM in your settings and theme for the '
'same behavior. Temporarily setting TAG_FEED_ATOM for backwards '
'compatibility.')
self.settings['TAG_FEED_ATOM'] = self.settings['TAG_FEED']
if self.settings.get('CATEGORY_FEED', False):
logger.warning('Found deprecated `CATEGORY_FEED` in settings. '
'Modify CATEGORY_FEED to CATEGORY_FEED_ATOM in your settings and '
'theme for the same behavior. Temporarily setting '
'CATEGORY_FEED_ATOM for backwards compatibility.')
self.settings['CATEGORY_FEED_ATOM'] =\
self.settings['CATEGORY_FEED']
if self.settings.get('TRANSLATION_FEED', False):
logger.warning('Found deprecated `TRANSLATION_FEED` in settings. '
'Modify TRANSLATION_FEED to TRANSLATION_FEED_ATOM in your '
'settings and theme for the same behavior. Temporarily setting '
'TRANSLATION_FEED_ATOM for backwards compatibility.')
self.settings['TRANSLATION_FEED_ATOM'] =\
self.settings['TRANSLATION_FEED']
def run(self):
"""Run the generators and return"""
start_time = time.time()
context = self.settings.copy()
context['filenames'] = {} # share the dict between all the generators
context['localsiteurl'] = self.settings['SITEURL'] # share
generators = [
cls(
context=context,
settings=self.settings,
path=self.path,
theme=self.theme,
output_path=self.output_path,
markup=self.markup,
) for cls in self.get_generator_classes()
]
for p in generators:
if hasattr(p, 'generate_context'):
p.generate_context()
# 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)
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 {} articles and {} pages in {:.2f} seconds.'.format(
len(articles_generator.articles) + len(articles_generator.translations),
len(pages_generator.pages) + len(pages_generator.translations),
time.time() - start_time))
def get_generator_classes(self):
generators = [StaticGenerator, ArticlesGenerator, PagesGenerator]
if self.settings['TEMPLATE_PAGES']:
generators.append(TemplatePagesGenerator)
if self.settings['PDF_GENERATOR']:
generators.append(PdfGenerator)
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: {0}'.format(v))
generators.append(v)
return generators
def get_writer(self):
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('-m', '--markup', dest='markup',
help='The list of markup language to use (rst or md). Please indicate '
'them separated by commas.')
parser.add_argument('-s', '--settings', dest='settings',
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 message, including debug messages.')
parser.add_argument('--version', action='version', version=__version__,
help='Print the pelican version and exit.')
parser.add_argument('-r', '--autoreload', dest='autoreload',
action='store_true',
help="Relaunch pelican each time a modification occurs"
" on the content files.")
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.markup:
config['MARKUP'] = [a.strip().lower() for a in args.markup.split(',')]
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
# 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)
if key == "MARKUP":
config[key] = [a.decode(enc) for a in config[key]]
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)
def main():
args = parse_arguments()
init(args.verbosity)
pelican = get_instance(args)
watchers = {'content': folder_watcher(pelican.path,
pelican.markup,
pelican.ignore_files),
'theme': folder_watcher(pelican.theme,
[''],
pelican.ignore_files),
'settings': file_watcher(args.settings)}
try:
if args.autoreload:
print(' --- AutoReload Mode: Monitoring `content`, `theme` and `settings`'
' for changes. ---')
while True:
try:
# Check source dir for changed files ending with the given
# extension in the settings. In the theme dir is no such
# restriction; all files are recursively checked if they
# have changed, no matter what extension the filenames
# have.
modified = {k: next(v) for k, v in watchers.items()}
if modified['settings']:
pelican = get_instance(args)
if any(modified.values()):
print('\n-> Modified: {}. re-generating...'.format(
', '.join(k for k, v in modified.items() if v)))
if modified['content'] is None:
logger.warning('No valid files found in content.')
if modified['theme'] is None:
logger.warning('Empty theme folder. Using `basic` theme.')
pelican.run()
except KeyboardInterrupt:
logger.warning("Keyboard interrupt, quitting.")
break
except Exception as e:
if (args.verbosity == logging.DEBUG):
logger.critical(e.args)
raise
logger.warning(
'Caught exception "{0}". Reloading.'.format(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:
# localized systems have errors in native language if locale is set
# so convert the message to unicode with the correct encoding
msg = str(e)
if not six.PY3:
msg = msg.decode(locale.getpreferredencoding(False))
logger.critical(msg)
if (args.verbosity == logging.DEBUG):
raise
else:
sys.exit(getattr(e, 'exitcode', 1))

327
pelican/contents.py Normal file
View file

@ -0,0 +1,327 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import six
import copy
import locale
import logging
import functools
import os
import re
import sys
from datetime import datetime
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)
# 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') and 'AUTHOR' in settings:
self.author = Author(settings['AUTHOR'], settings)
if not hasattr(self, 'authors') and hasattr(self, 'author'):
setattr(self, 'authors', [self.author])
if hasattr(self, 'authors') and not hasattr(self, 'author'):
setattr(self, 'author', self.authors[0])
# 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, from the title
if not hasattr(self, 'slug') and hasattr(self, 'title'):
self.slug = slugify(self.title)
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]
if hasattr(self, 'date'):
self.locale_date = strftime(self.date, 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 > datetime.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())
metadata.update({
'path': path_to_url(path),
'slug': getattr(self, 'slug', ''),
'lang': getattr(self, 'lang', 'en'),
'date': getattr(self, 'date', datetime.now()),
'author': getattr(self, 'author', ''),
'category': getattr(self, 'category',
self.settings['DEFAULT_CATEGORY']),
})
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
hrefs = re.compile(r"""
(?P<markup><\s*[^\>]* # match tag with src and href attr
(?:href|src)\s*=)
(?P<quote>["\']) # require value to be quoted
(?P<path>\|(?P<what>.*?)\|(?P<value>.*?)) # the url value
\2""", re.X)
def replacer(m):
what = m.group('what')
value = m.group('value')
origin = m.group('path')
# we support only filename for now. the plan is to support
# categories, tags, etc. in the future, but let's keep things
# simple for now.
# XXX Put this in a different location.
if what == 'filename':
if value.startswith('/'):
value = value[1:]
else:
# relative to the source path of this content
value = self.get_relative_source_path(
os.path.join(self.relative_dir, value)
)
if value in self._context['filenames']:
origin = '/'.join((siteurl,
self._context['filenames'][value].url))
origin = origin.replace('\\', '/') # Fow windows paths.
else:
logger.warning("Unable to find {fn}, skipping url"
" replacement".format(fn=value))
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 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

638
pelican/generators.py Normal file
View file

@ -0,0 +1,638 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import os
import math
import random
import logging
import shutil
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, Page, Static, is_valid_content
from pelican.readers import read_file
from pelican.utils import copy, process_translations, mkdir_p, DateFormatter
from pelican import signals
logger = logging.getLogger(__name__)
class Generator(object):
"""Baseclass generator"""
def __init__(self, context, settings, path, theme, output_path, markup,
**kwargs):
self.context = context
self.settings = settings
self.path = path
self.theme = theme
self.output_path = output_path
self.markup = markup
for arg, value in kwargs.items():
setattr(self, arg, value)
# 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,
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: {0}'.format(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.markup)
basename = os.path.basename(path)
if extensions is False or basename.endswith(extensions):
return True
return False
def get_files(self, path, exclude=[], extensions=None):
"""Return a list of files to use, based on rules
:param path: the path 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)
"""
files = []
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):
location = content.get_relative_source_path()
self.context['filenames'][location] = content
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
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)
finally:
del self.env.loader.loaders[0]
class ArticlesGenerator(Generator):
"""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 = []
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')
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):
write(article.save_as, self.get_template(article.template),
self.context, article=article, category=article.category)
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')
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)
write(save_as, template, self.context,
dates=archive, blog=True)
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')
}
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:
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:
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 article in self.drafts:
write(os.path.join('drafts', '%s.html' % article.slug),
self.get_template(article.template), self.context,
article=article, category=article.category,
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 = []
for f in self.get_files(
self.settings['ARTICLE_DIR'],
exclude=self.settings['ARTICLE_EXCLUDES']):
try:
article = read_file(
base_path=self.path, path=f, content_class=Article,
settings=self.settings, 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.warning('Could not process {}\n{}'.format(f, e))
continue
if not is_valid_content(article, f):
continue
self.add_source_path(article)
if article.status == "published":
all_articles.append(article)
elif article.status == "draft":
self.drafts.append(article)
else:
logger.warning("Unknown status %s for file %s, skipping it." %
(repr(article.status),
repr(f)))
self.articles, self.translations = process_translations(all_articles)
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
if hasattr(article, 'authors'):
for author in 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'))
signals.article_generator_finalized.send(self)
def generate_output(self, writer):
self.generate_feeds(writer)
self.generate_pages(writer)
class PagesGenerator(Generator):
"""Generate pages"""
def __init__(self, *args, **kwargs):
self.pages = []
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_DIR'],
exclude=self.settings['PAGE_EXCLUDES']):
try:
page = read_file(
base_path=self.path, path=f, content_class=Page,
settings=self.settings, 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.warning('Could not process {}\n{}'.format(f, e))
continue
if not is_valid_content(page, f):
continue
self.add_source_path(page)
if page.status == "published":
all_pages.append(page)
elif page.status == "hidden":
hidden_pages.append(page)
else:
logger.warning("Unknown status %s for file %s, skipping it." %
(repr(page.status),
repr(f)))
self.pages, self.translations = process_translations(all_pages)
self.hidden_pages, self.hidden_translations = (
process_translations(hidden_pages))
self._update_context(('pages', ))
self.context['PAGES'] = self.pages
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'])
class StaticGenerator(Generator):
"""copy static paths (what you want to copy, like images, medias etc.
to output"""
def _copy_paths(self, paths, source, destination, output_path,
final_path=None):
"""Copy all the paths from source to destination"""
for path in paths:
copy(path, source, os.path.join(output_path, destination),
final_path, overwrite=True)
def generate_context(self):
self.staticfiles = []
# walk static paths
for static_path in self.settings['STATIC_PATHS']:
for f in self.get_files(
static_path, extensions=False):
static = read_file(
base_path=self.path, path=f, content_class=Static,
fmt='static',
settings=self.settings, 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)
def generate_output(self, writer):
self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
'theme', self.output_path, os.curdir)
# copy all Static files
for sc in self.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.copy(source_path, save_as)
logger.info('copying {} to {}'.format(sc.source_path, sc.save_as))
class PdfGenerator(Generator):
"""Generate PDFs on the output dir, for all articles and pages coming from
rst"""
def __init__(self, *args, **kwargs):
super(PdfGenerator, self).__init__(*args, **kwargs)
try:
from rst2pdf.createpdf import RstToPdf
pdf_style_path = os.path.join(self.settings['PDF_STYLE_PATH'])
pdf_style = self.settings['PDF_STYLE']
self.pdfcreator = RstToPdf(breakside=0,
stylesheets=[pdf_style],
style_path=[pdf_style_path])
except ImportError:
raise Exception("unable to find rst2pdf")
def _create_pdf(self, obj, output_path):
if obj.source_path.endswith('.rst'):
filename = obj.slug + ".pdf"
output_pdf = os.path.join(output_path, filename)
# print('Generating pdf for', obj.source_path, 'in', output_pdf)
with open(obj.source_path) as f:
self.pdfcreator.createPdf(text=f.read(), output=output_pdf)
logger.info(' [ok] writing %s' % output_pdf)
def generate_context(self):
pass
def generate_output(self, writer=None):
# we don't use the writer passed as argument here
# since we write our own files
logger.info(' Generating PDF files...')
pdf_path = os.path.join(self.output_path, 'pdf')
if not os.path.exists(pdf_path):
try:
os.mkdir(pdf_path)
except OSError:
logger.error("Couldn't create the pdf output folder in " +
pdf_path)
for article in self.context['articles']:
self._create_pdf(article, pdf_path)
for page in self.context['pages']:
self._create_pdf(page, pdf_path)
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)

89
pelican/log.py Normal file
View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
__all__ = [
'init'
]
import os
import sys
import logging
from logging import Formatter, getLogger, StreamHandler, DEBUG
RESET_TERM = '\033[0;m'
COLOR_CODES = {
'red': 31,
'yellow': 33,
'cyan': 36,
'white': 37,
'bgred': 41,
'bggrey': 100,
}
def ansi(color, text):
"""Wrap text in an ansi escape sequence"""
code = COLOR_CODES[color]
return '\033[1;{0}m{1}{2}'.format(code, text, RESET_TERM)
class ANSIFormatter(Formatter):
"""Convert a `logging.LogRecord' object into colored text, using ANSI
escape sequences.
"""
def format(self, record):
msg = record.getMessage()
if record.levelname == 'INFO':
return ansi('cyan', '-> ') + msg
elif record.levelname == 'WARNING':
return ansi('yellow', record.levelname) + ': ' + msg
elif record.levelname == 'ERROR':
return ansi('red', record.levelname) + ': ' + msg
elif record.levelname == 'CRITICAL':
return ansi('bgred', record.levelname) + ': ' + msg
elif record.levelname == 'DEBUG':
return ansi('bggrey', record.levelname) + ': ' + msg
else:
return ansi('white', record.levelname) + ': ' + msg
class TextFormatter(Formatter):
"""
Convert a `logging.LogRecord' object into text.
"""
def format(self, record):
if not record.levelname or record.levelname == 'INFO':
return record.getMessage()
else:
return record.levelname + ': ' + record.getMessage()
def init(level=None, logger=getLogger(), handler=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=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')

89
pelican/paginator.py Normal file
View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
# From django.core.paginator
from math import ceil
class Paginator(object):
def __init__(self, object_list, per_page, orphans=0):
self.object_list = object_list
self.per_page = per_page
self.orphans = orphans
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.object_list[bottom:top], number, self)
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, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = paginator
def __repr__(self):
return '<Page %s of %s>' % (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

470
pelican/readers.py Normal file
View file

@ -0,0 +1,470 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import datetime
import logging
import os
import re
try:
import docutils
import docutils.core
import docutils.io
from docutils.writers.html4css1 import HTMLTranslator
# import the directives to have pygments support
from pelican import rstdirectives # NOQA
except ImportError:
core = False
try:
from markdown import Markdown
except ImportError:
Markdown = False # NOQA
try:
from asciidocapi import AsciiDocAPI
asciidoc = True
except ImportError:
asciidoc = False
try:
from html import escape
except ImportError:
from cgi import escape # NOQA
try:
from html.parser import HTMLParser
except ImportError:
from HTMLParser import HTMLParser
from pelican.contents import Page, Category, Tag, Author
from pelican.utils import get_date, pelican_open
logger = logging.getLogger(__name__)
METADATA_PROCESSORS = {
'tags': lambda x, y: [Tag(tag, y) for tag in x.split(',')],
'date': lambda x, y: get_date(x),
'status': lambda x, y: x.strip(),
'category': Category,
'author': Author,
'authors': lambda x, y: [Author(name, y) for name in x.split(',')],
}
class Reader(object):
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('</abbr>')
class RstReader(Reader):
enabled = bool(docutils)
file_extensions = ['rst']
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()
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'}
user_params = self.settings.get('DOCUTILS_SETTINGS')
if user_params:
extra_params.update(user_params)
pub = docutils.core.Publisher(
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()
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(Reader):
enabled = bool(Markdown)
file_extensions = ['md', 'markdown', 'mkd', 'mdown']
def __init__(self, *args, **kwargs):
super(MarkdownReader, self).__init__(*args, **kwargs)
self.extensions = self.settings['MD_EXTENSIONS']
self.extensions.append('meta')
self._md = Markdown(extensions=self.extensions)
def _parse_metadata(self, meta):
"""Return the dict containing document metadata"""
output = {}
for name, value in meta.items():
name = name.lower()
if name == "summary":
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)
else:
output[name] = self.process_metadata(name, value[0])
return output
def read(self, source_path):
"""Parse content and metadata of markdown files"""
with pelican_open(source_path) as text:
content = self._md.convert(text)
metadata = self._parse_metadata(self._md.Meta)
return content, metadata
class HTMLReader(Reader):
"""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):
HTMLParser.__init__(self)
self.body = ''
self.metadata = {}
self.settings = settings
self._data_buffer = ''
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').lower()
contents = self._attr_value(attrs, 'contents', '')
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)
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 AsciiDocReader(Reader):
enabled = bool(asciidoc)
file_extensions = ['asc']
default_options = ["--no-header-footer", "-a newline=\\n"]
def read(self, source_path):
"""Parse content and metadata of asciidoc files"""
from cStringIO import StringIO
with pelican_open(source_path) as source:
text = StringIO(source)
content = StringIO()
ad = AsciiDocAPI()
options = self.settings['ASCIIDOC_OPTIONS']
if isinstance(options, (str, unicode)):
options = [m.strip() for m in options.split(',')]
options = self.default_options + options
for o in options:
ad.options(*o.split())
ad.execute(text, content, backend="html4")
content = content.getvalue()
metadata = {}
for name, value in ad.asciidoc.document.attributes.items():
name = name.lower()
metadata[name] = self.process_metadata(name, value)
if 'doctitle' in metadata:
metadata['title'] = metadata['doctitle']
return content, metadata
EXTENSIONS = {}
for cls in [Reader] + Reader.__subclasses__():
for ext in cls.file_extensions:
EXTENSIONS[ext] = cls
def read_file(base_path, path, content_class=Page, fmt=None,
settings=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)
base, ext = os.path.splitext(os.path.basename(path))
logger.debug('read file {} -> {}'.format(
source_path, content_class.__name__))
if not fmt:
fmt = ext[1:]
if fmt not in EXTENSIONS:
raise TypeError('Pelican does not know how to parse {}'.format(path))
if preread_signal:
logger.debug('signal {}.send({})'.format(
preread_signal, preread_sender))
preread_signal.send(preread_sender)
if settings is None:
settings = {}
reader_class = EXTENSIONS[fmt]
if not reader_class.enabled:
raise ValueError('Missing dependencies for {}'.format(fmt))
reader = reader_class(settings)
settings_key = '%s_EXTENSIONS' % fmt.upper()
if settings and settings_key in settings:
reader.extensions = settings[settings_key]
metadata = default_metadata(
settings=settings, process=reader.process_metadata)
metadata.update(path_metadata(
full_path=path, source_path=source_path, settings=settings))
metadata.update(parse_path_metadata(
source_path=source_path, settings=settings,
process=reader.process_metadata))
content, reader_metadata = reader.read(path)
metadata.update(reader_metadata)
# eventually filter the content with typogrify if asked so
if content and settings and settings['TYPOGRIFY']:
from typogrify.filters import typogrify
content = typogrify(content)
metadata['title'] = typogrify(metadata['title'])
if context_signal:
logger.debug('signal {}.send({}, <metadata>)'.format(
context_signal, context_sender))
context_signal.send(context_sender, metadata=metadata)
return content_class(
content=content,
metadata=metadata,
settings=settings,
source_path=path,
context=context)
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 'DEFAULT_DATE' in settings and settings['DEFAULT_DATE'] != 'fs':
metadata['date'] = datetime.datetime(*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'] = datetime.datetime.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<slug>[^.]*).*',
... 'PATH_METADATA':
... '(?P<category>[^/]*)/(?P<date>\d{4}-\d{2}-\d{2})/.*',
... }
>>> reader = Reader(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': <pelican.urlwrappers.Category object at ...>,
'date': datetime.datetime(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<category>.*)', 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

117
pelican/rstdirectives.py Normal file
View file

@ -0,0 +1,117 @@
# -*- 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
INLINESTYLES = False
DEFAULT = HtmlFormatter(noclasses=INLINESTYLES)
VARIANTS = {
'linenos': HtmlFormatter(noclasses=INLINESTYLES, linenos=True),
}
class Pygments(Directive):
""" Source code syntax hightlighting.
"""
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = dict([(key, directives.flag) for key in VARIANTS])
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()
# take an arbitrary option if more than one is given
formatter = self.options and VARIANTS[self.options.keys()[0]] \
or DEFAULT
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)
class YouTube(Directive):
""" Embed YouTube video in posts.
Courtesy of Brian Hsu: https://gist.github.com/1422773
VIDEO_ID is required, with / height are optional integer,
and align could be left / center / right.
Usage:
.. youtube:: VIDEO_ID
:width: 640
:height: 480
:align: center
"""
def align(argument):
"""Conversion function for the "align" option."""
return directives.choice(argument, ('left', 'center', 'right'))
required_arguments = 1
optional_arguments = 2
option_spec = {
'width': directives.positive_int,
'height': directives.positive_int,
'align': align
}
final_argument_whitespace = False
has_content = False
def run(self):
videoID = self.arguments[0].strip()
width = 420
height = 315
align = 'left'
if 'width' in self.options:
width = self.options['width']
if 'height' in self.options:
height = self.options['height']
if 'align' in self.options:
align = self.options['align']
url = 'http://www.youtube.com/embed/%s' % videoID
div_block = '<div class="youtube" align="%s">' % align
embed_block = '<iframe width="%s" height="%s" src="%s" '\
'frameborder="0"></iframe>' % (width, height, url)
return [
nodes.raw('', div_block, format='html'),
nodes.raw('', embed_block, format='html'),
nodes.raw('', '</div>', format='html')]
directives.register_directive('youtube', YouTube)
_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)

29
pelican/server.py Normal file
View file

@ -0,0 +1,29 @@
from __future__ import print_function
import sys
try:
import SimpleHTTPServer as srvmod
except ImportError:
import http.server as srvmod # NOQA
try:
import SocketServer as socketserver
except ImportError:
import socketserver # NOQA
PORT = 8000
Handler = srvmod.SimpleHTTPRequestHandler
try:
httpd = socketserver.TCPServer(("", PORT), Handler)
except OSError as e:
print("Could not listen on port", PORT)
sys.exit(getattr(e, 'exitcode', 1))
print("serving at port", PORT)
try:
httpd.serve_forever()
except KeyboardInterrupt as e:
print("shutting down server")
httpd.socket.close()

271
pelican/settings.py Normal file
View file

@ -0,0 +1,271 @@
# -*- 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
logger = logging.getLogger(__name__)
DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'themes', 'notmyidea')
DEFAULT_CONFIG = {
'PATH': os.curdir,
'ARTICLE_DIR': '',
'ARTICLE_EXCLUDES': ('pages',),
'PAGE_DIR': 'pages',
'PAGE_EXCLUDES': (),
'THEME': DEFAULT_THEME,
'OUTPUT_PATH': 'output',
'MARKUP': ('rst', 'md'),
'STATIC_PATHS': ['images', ],
'THEME_STATIC_PATHS': ['static', ],
'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'),
'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.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,
'PDF_GENERATOR': False,
'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_LANG_URL': '{slug}-{lang}.html',
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
'PAGE_URL': 'pages/{slug}.html',
'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'),
'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_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'),
'YEAR_ARCHIVE_SAVE_AS': False,
'MONTH_ARCHIVE_SAVE_AS': False,
'DAY_ARCHIVE_SAVE_AS': False,
'RELATIVE_URLS': False,
'DEFAULT_LANG': 'en',
'TAG_CLOUD_STEPS': 4,
'TAG_CLOUD_MAX_ITEMS': 100,
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
'EXTRA_TEMPLATES_PATHS': [],
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
'PELICAN_CLASS': 'pelican.Pelican',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
'DATE_FORMATS': {},
'ASCIIDOC_OPTIONS': [],
'MD_EXTENSIONS': ['codehilite(css_class=highlight)', 'extra'],
'JINJA_EXTENSIONS': [],
'JINJA_FILTERS': {},
'LOCALE': [], # defaults to user locale
'DEFAULT_PAGINATION': False,
'DEFAULT_ORPHANS': 0,
'DEFAULT_METADATA': (),
'FILENAME_METADATA': '(?P<date>\d{4}-\d{2}-\d{2}).*',
'PATH_METADATA': '',
'EXTRA_PATH_METADATA': {},
'DEFAULT_STATUS': 'published',
'ARTICLE_PERMALINK_STRUCTURE': '',
'TYPOGRIFY': False,
'SUMMARY_MAX_LENGTH': 50,
'PLUGIN_PATH': '',
'PLUGINS': [],
'TEMPLATE_PAGES': {},
'IGNORE_FILES': ['.#*'],
}
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', 'PLUGIN_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', 'PLUGIN_PATH') or os.path.exists(absp):
local_settings[p] = absp
else:
local_settings = copy.deepcopy(DEFAULT_CONFIG)
if override:
local_settings.update(override)
return configure_settings(local_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.
"""
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)')
# 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'])
# 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 {} ({}), '
'falling back to the default ({})'.format(
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']
# 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',
'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')
# 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',
'MARKUP',
'PAGINATED_DIRECT_TEMPLATES',
'PLUGINS',
'STATIC_PATHS',
'THEME_STATIC_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]
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

35
pelican/signals.py Normal file
View file

@ -0,0 +1,35 @@
# -*- 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')
finalized = signal('pelican_finalized')
# Generator-level signals
generator_init = signal('generator_init')
article_generator_init = signal('article_generator_init')
article_generator_finalized = signal('article_generator_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')

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,4 @@
This is a test page
###################
The quick brown fox jumped over the lazy dog's back.

View file

@ -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.

View file

@ -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

View file

@ -0,0 +1,2 @@
import logging
logging.getLogger().addHandler(logging.NullHandler())

View file

@ -0,0 +1,6 @@
Rst with filename metadata
##########################
:category: yeah
:author: Alexis Métaireau

View file

@ -0,0 +1,6 @@
category: yeah
author: Alexis Métaireau
Markdown with filename metadata
===============================

View file

@ -0,0 +1,7 @@
This is an article with category !
##################################
:category: yeah
:date: 1970-01-01
This article should be in 'yeah' category.

View file

@ -0,0 +1,4 @@
This is an article without category !
#####################################
This article should be in 'TestCategory' category.

View file

@ -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)`.

View file

@ -0,0 +1,12 @@
Test AsciiDoc File Header
=========================
:Author: Author O. Article
:Email: <author@nowhere.com>
:Date: 2011-09-15 09:05
:Category: Blog
:Tags: Linux, Python, Pelican
Used for pelican test
---------------------
The quick brown fox jumped over the lazy dog's back.

View file

@ -0,0 +1,9 @@
Test AsciiDoc File Header
=========================
Used for pelican test
---------------------
version {revision}
The quick brown fox jumped over the lazy dog's back.

View file

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
Body content
<!-- This comment is included (including extra whitespace) -->
</body>
</html>

View file

@ -0,0 +1,6 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="keywords" contents="foo, bar, foobar" />
</head>
</html>

View file

@ -0,0 +1,8 @@
Title: Article with markdown containing footnotes
Date: 2012-10-31
Summary: Summary with **inline** markup *should* be supported.
This is some content[^1] with some footnotes[^footnote]
[^1]: Numbered footnote
[^footnote]: Named footnote

View file

@ -0,0 +1,18 @@
Title: マックOS X 10.8でパイソンとVirtualenvをインストールと設定
Slug: python-virtualenv-on-mac-osx-mountain-lion-10.8
Date: 2012-12-20
Tags: パイソン, マック
Category: 指導書
Summary: パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
Writing unicode is certainly fun.
パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
And let's mix languages.
первый пост
Now another.
İlk yazı çok özel değil.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -0,0 +1,8 @@
Title: Test Markdown extensions
[TOC]
## Level1
### Level2

View file

@ -0,0 +1,13 @@
Title: Test md File
Category: test
Tags: foo, bar, foobar
Date: 2010-12-02 10:14
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.

View file

@ -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.

View file

@ -0,0 +1,15 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="tags" contents="foo, bar, foobar" />
<meta name="date" contents="2010-12-02 10:14" />
<meta name="category" contents="yeah" />
<meta name="author" contents="Alexis Métaireau" />
<meta name="summary" contents="Summary and stuff" />
<meta name="custom_field" contents="http://notmyidea.org" />
</head>
<body>
Multi-line metadata should be supported
as well as <strong>inline markup</strong>.
</body>
</html>

View file

@ -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

View file

@ -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.

View file

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
Ensure that empty attributes are copied properly.
<input name="test" disabled style="" />
</body>
</html>

View file

@ -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".

View file

@ -0,0 +1,6 @@
<html>
<head>
<title>This is a super article !</title>
<meta name="Category" contents="Yeah" />
</head>
</html>

View file

@ -0,0 +1,6 @@
This is a super article !
#########################
:Category: Yeah

View file

@ -0,0 +1,6 @@
This is an article without category !
#####################################
This article should be in the DEFAULT_CATEGORY.

View file

@ -0,0 +1,48 @@
<p>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.</p>
<p><object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/XSrW-wAWZe4"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/XSrW-wAWZe4" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<blockquote><p>
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.</p>
<p>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.</p></blockquote>
<ul>
<li>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.</li>
<li>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.</li>
</ul>
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre>
<p>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.</p>

View file

@ -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.
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/XSrW-wAWZe4"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/XSrW-wAWZe4" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>
<blockquote>
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.
</blockquote>
<ul>
<li>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.</li>
<li>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.</li>
</ul>
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre>
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.

View file

@ -0,0 +1,686 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
<!-- You may use this file to transfer that content from one site to another. -->
<!-- This file is not intended to serve as a complete backup of your site. -->
<!-- To import this information into a WordPress site follow these steps: -->
<!-- 1. Log in to that site as an administrator. -->
<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
<!-- 3. Install the "WordPress" importer from the list. -->
<!-- 4. Activate & Run Importer. -->
<!-- 5. Upload this file using the form provided on that page. -->
<!-- 6. You will first be asked to map the authors in this export file to users -->
<!-- on the site. For each author, you may choose to map to an -->
<!-- existing user on the site or to create a new user. -->
<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
<!-- contained in this file into your site. -->
<!-- generator="WordPress/3.3.1" created="2012-05-13 01:13" -->
<rss version="2.0"
xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:wp="http://wordpress.org/export/1.1/"
>
<channel>
<title>Pelican test channel</title>
<link>http://thisisa.test</link>
<description>Not a real feed, just for test</description>
<pubDate>Sun, 13 May 2012 01:13:52 +0000</pubDate>
<language>en</language>
<wp:wxr_version>1.1</wp:wxr_version>
<wp:base_site_url>http://thisisa.test</wp:base_site_url>
<wp:base_blog_url>http://thisisa.test</wp:base_blog_url>
<wp:author><wp:author_id>2</wp:author_id><wp:author_login>Bob</wp:author_login><wp:author_email>bob@thisisa.test</wp:author_email><wp:author_display_name><![CDATA[Bob]]></wp:author_display_name><wp:author_first_name><![CDATA[Bob]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
<wp:author><wp:author_id>3</wp:author_id><wp:author_login>Jonh</wp:author_login><wp:author_email>jonh@thisisa.test</wp:author_email><wp:author_display_name><![CDATA[Jonh]]></wp:author_display_name><wp:author_first_name><![CDATA[Jonh]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
<wp:category><wp:term_id>7</wp:term_id><wp:category_nicename>categ-1</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Category 1]]></wp:cat_name></wp:category>
<wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>categ-2</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Category 2]]></wp:cat_name></wp:category>
<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
<wp:category><wp:term_id>15</wp:term_id><wp:category_nicename>categ-3</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Category 3]]></wp:cat_name></wp:category>
<wp:tag><wp:term_id>25</wp:term_id><wp:tag_slug>tag-1</wp:tag_slug><wp:tag_name><![CDATA[tag 1]]></wp:tag_name></wp:tag>
<wp:tag><wp:term_id>122</wp:term_id><wp:tag_slug>tag2</wp:tag_slug><wp:tag_name><![CDATA[Tag2]]></wp:tag_name></wp:tag>
<wp:tag><wp:term_id>68</wp:term_id><wp:tag_slug>tag-3</wp:tag_slug><wp:tag_name><![CDATA[Tag 3]]></wp:tag_name></wp:tag>
<generator>http://wordpress.org/?v=3.3.1</generator>
<item>
<title>Empty post</title>
<link>http://thisisa.test/?attachment_id=24</link>
<pubDate>Sat, 04 Feb 2012 03:17:33 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Pelican_lakes_entrance02.jpg/240px-Pelican_lakes_entrance02.jpg</guid>
<description></description>
<content:encoded><![CDATA[]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>24</wp:post_id>
<wp:post_date>2012-02-04 03:17:33</wp:post_date>
<wp:post_date_gmt>2012-02-04 03:17:33</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>empty-post</wp:post_name>
<wp:status>inherit</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>attachment</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<wp:attachment_url>https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Pelican_lakes_entrance02.jpg/240px-Pelican_lakes_entrance02.jpg</wp:attachment_url>
<wp:postmeta>
<wp:meta_key>_wp_attachment_metadata</wp:meta_key>
<wp:meta_value><![CDATA[a:5:{s:5:"width";s:3:"150";s:6:"height";s:3:"186";s:14:"hwstring_small";s:22:"height='96' width='77'";s:4:"file";s:20:"2012/02/pelican.png";s:10:"image_meta";a:10:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:0:"";}}]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_wp_attached_file</wp:meta_key>
<wp:meta_value><![CDATA[2012/02/stuff.png]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_wp_attachment_image_alt</wp:meta_key>
<wp:meta_value><![CDATA[Stuff]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title></title>
<link>http://thisisa.test/?p=168</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=168</guid>
<description></description>
<content:encoded><![CDATA[This is a draft with no title]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>168</wp:post_id>
<wp:post_date>2012-02-15 21:23:57</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name></wp:post_name>
<wp:status>draft</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="categ-1"><![CDATA[Category 1]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>A normal post</title>
<link>http://thisisa.test/?p=174</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=174</guid>
<description></description>
<content:encoded><![CDATA[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.
<ul>
<li>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.</li>
<li>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.</li>
</ul>
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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>174</wp:post_id>
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name></wp:post_name>
<wp:status>draft</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-2"><![CDATA[Category 2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Complete draft</title>
<link>http://thisisa.test/?p=176</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=176</guid>
<description></description>
<content:encoded><![CDATA[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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>176</wp:post_id>
<wp:post_date>2012-02-17 15:11:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name></wp:post_name>
<wp:status>draft</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-3"><![CDATA[Category 3]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Page</title>
<link>http://thisisa.test/contact/</link>
<pubDate>Wed, 11 Apr 2012 11:38:08 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?page_id=334</guid>
<description></description>
<content:encoded><![CDATA[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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>334</wp:post_id>
<wp:post_date>2012-04-11 06:38:08</wp:post_date>
<wp:post_date_gmt>2012-04-11 11:38:08</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>contact</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>page</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<wp:postmeta>
<wp:meta_key>sharing_disabled</wp:meta_key>
<wp:meta_value><![CDATA[1]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_wp_page_template</wp:meta_key>
<wp:meta_value><![CDATA[default]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Empty Page</title>
<link>http://thisisa.test/empty/</link>
<pubDate>Wed, 11 Apr 2012 11:38:08 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?page_id=334</guid>
<description></description>
<content:encoded><![CDATA[]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>334</wp:post_id>
<wp:post_date>2012-04-11 06:38:08</wp:post_date>
<wp:post_date_gmt>2012-04-11 11:38:08</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>empty</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>page</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<wp:postmeta>
<wp:meta_key>sharing_disabled</wp:meta_key>
<wp:meta_value><![CDATA[1]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_wp_page_template</wp:meta_key>
<wp:meta_value><![CDATA[default]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Special chars: l&#039;é</title>
<link>http://thisisa.test/?p=471</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=471</guid>
<description></description>
<content:encoded><![CDATA[l&#039;é]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>471</wp:post_id>
<wp:post_date>2012-04-29 09:44:27</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name></wp:post_name>
<wp:status>draft</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-1"><![CDATA[Category 1]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>With excerpt</title>
<link>http://thisisa.test/with-excerpt/</link>
<pubDate>Sat, 04 Feb 2012 02:03:06 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=8</guid>
<description></description>
<content:encoded><![CDATA[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.]]></content:encoded>
<excerpt:encoded><![CDATA[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.]]></excerpt:encoded>
<wp:post_id>8</wp:post_id>
<wp:post_date>2012-02-04 02:03:06</wp:post_date>
<wp:post_date_gmt>2012-02-04 02:03:06</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>with-excerpt</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="Category 1"><![CDATA[Category 1]]></category>
<category domain="post_tag" nicename="tag-1"><![CDATA[tag 1]]></category>
<category domain="post_tag" nicename="tag2"><![CDATA[Tag2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>et_bigpost</wp:meta_key>
<wp:meta_value><![CDATA[0]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_thumbnail_id</wp:meta_key>
<wp:meta_value><![CDATA[32]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>With tags</title>
<link>http://thisisa.test/tags/</link>
<pubDate>Sat, 04 Feb 2012 21:05:25 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=25</guid>
<description></description>
<content:encoded><![CDATA[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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>25</wp:post_id>
<wp:post_date>2012-02-04 21:05:25</wp:post_date>
<wp:post_date_gmt>2012-02-04 21:05:25</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>with-tags</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-3"><![CDATA[Category 3]]></category>
<category domain="post_tag" nicename="tag-1"><![CDATA[tag 1]]></category>
<category domain="post_tag" nicename="tag-2"><![CDATA[Tag2]]></category>
<category domain="post_tag" nicename="tag-3"><![CDATA[Tag 3]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>et_bigpost</wp:meta_key>
<wp:meta_value><![CDATA[0]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_thumbnail_id</wp:meta_key>
<wp:meta_value><![CDATA[29]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>With comments</title>
<link>http://thisisa.test/with-comments/</link>
<pubDate>Wed, 18 Apr 2012 08:36:26 +0000</pubDate>
<dc:creator>john</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=422</guid>
<description></description>
<content:encoded><![CDATA[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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>422</wp:post_id>
<wp:post_date>2012-04-18 03:36:26</wp:post_date>
<wp:post_date_gmt>2012-04-18 08:36:26</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>with-comments</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-1"><![CDATA[Category 1]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[2]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key>_thumbnail_id</wp:meta_key>
<wp:meta_value><![CDATA[423]]></wp:meta_value>
</wp:postmeta>
<wp:comment>
<wp:comment_id>116</wp:comment_id>
<wp:comment_author><![CDATA[User2]]></wp:comment_author>
<wp:comment_author_email>User2@mail.test</wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP>127.0.0.1</wp:comment_author_IP>
<wp:comment_date>2012-05-06 15:46:06</wp:comment_date>
<wp:comment_date_gmt>2012-05-06 20:46:06</wp:comment_date_gmt>
<wp:comment_content><![CDATA[Comment content.]]></wp:comment_content>
<wp:comment_approved>1</wp:comment_approved>
<wp:comment_type></wp:comment_type>
<wp:comment_parent>0</wp:comment_parent>
<wp:comment_user_id>0</wp:comment_user_id>
<wp:commentmeta>
<wp:meta_key>akismet_result</wp:meta_key>
<wp:meta_value><![CDATA[false]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_history</wp:meta_key>
<wp:meta_value><![CDATA[a:4:{s:4:"time";s:15:"1336337189.7974";s:7:"message";s:28:"Akismet cleared this comment";s:5:"event";s:9:"check-ham";s:4:"user";s:0:"";}]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_as_submitted</wp:meta_key>
<wp:meta_value><![CDATA[a:64:{s:15:"comment_post_ID";s:3:"422";s:14:"comment_author";s:8:"Baronsed";s:20:"comment_author_email";s:15:"User2@mail.test";s:18:"comment_author_url";s:0:"";s:15:"comment_content";s:118:"Dans les listes d'articles où celui-ci apparaît, l'image est trop grande et ralentit le chargement de toute la page.";s:12:"comment_type";s:0:"";s:14:"comment_parent";s:1:"0";s:7:"user_ID";s:1:"0";s:7:"user_ip";s:14:"127.0.0.1";s:10:"user_agent";s:74:"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0";s:8:"referrer";s:0:"";s:4:"blog";s:19:"http://thisisa.test";s:9:"blog_lang";s:5:"en_US";s:12:"blog_charset";s:5:"UTF-8";s:9:"permalink";s:62:"http://thisisa.test/htop-un-visualiseur-interactif-de-process/";s:9:"user_role";s:0:"";s:21:"akismet_comment_nonce";s:6:"passed";s:11:"POST_author";s:8:"Baronsed";s:10:"POST_email";s:15:"User2@mail.test";s:8:"POST_url";s:0:"";s:12:"POST_comment";s:118:"Dans les listes d'articles où celui-ci apparaît, l'image est trop grande et ralentit le chargement de toute la page.";s:11:"POST_submit";s:14:"Submit Comment";s:20:"POST_comment_post_ID";s:3:"422";s:19:"POST_comment_parent";s:1:"0";s:26:"POST_akismet_comment_nonce";s:10:"96b66769dc";s:15:"SERVER_SOFTWARE";s:12:"nginx/0.8.55";s:11:"REQUEST_URI";s:21:"/wp-comments-post.php";s:4:"TERM";s:5:"xterm";s:17:"PHP_FCGI_CHILDREN";s:1:"4";s:4:"PATH";s:29:"/sbin:/usr/sbin:/bin:/usr/bin";s:1:"_";s:25:"/usr/local/bin/spawn-fcgi";s:3:"PWD";s:1:"/";s:4:"LANG";s:11:"en_US.UTF-8";s:5:"SHLVL";s:1:"2";s:21:"PHP_FCGI_MAX_REQUESTS";s:4:"1000";s:9:"FCGI_ROLE";s:9:"RESPONDER";s:12:"QUERY_STRING";s:0:"";s:14:"REQUEST_METHOD";s:4:"POST";s:12:"CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:14:"CONTENT_LENGTH";s:3:"277";s:11:"SCRIPT_NAME";s:21:"/wp-comments-post.php";s:12:"DOCUMENT_URI";s:21:"/wp-comments-post.php";s:13:"DOCUMENT_ROOT";s:14:"/home/blog";s:15:"SERVER_PROTOCOL";s:8:"HTTP/1.1";s:17:"GATEWAY_INTERFACE";s:7:"CGI/1.1";s:11:"REMOTE_ADDR";s:14:"127.0.0.1";s:11:"REMOTE_PORT";s:5:"52826";s:11:"SERVER_ADDR";s:13:"127.0.0.1";s:11:"SERVER_PORT";s:2:"80";s:11:"SERVER_NAME";s:12:"thisisa.test";s:15:"REDIRECT_STATUS";s:3:"200";s:15:"SCRIPT_FILENAME";s:35:"/home/blog/wp-comments-post.php";s:9:"HTTP_HOST";s:12:"thisisa.test";s:15:"HTTP_USER_AGENT";s:74:"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0";s:11:"HTTP_ACCEPT";s:63:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";s:20:"HTTP_ACCEPT_LANGUAGE";s:35:"fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3";s:20:"HTTP_ACCEPT_ENCODING";s:13:"gzip, deflate";s:8:"HTTP_DNT";s:1:"1";s:15:"HTTP_CONNECTION";s:10:"keep-alive";s:11:"HTTP_COOKIE";s:0:"";s:17:"HTTP_CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:19:"HTTP_CONTENT_LENGTH";s:3:"277";s:8:"PHP_SELF";s:21:"/wp-comments-post.php";s:12:"REQUEST_TIME";s:10:"1336337164";}]]></wp:meta_value>
</wp:commentmeta>
</wp:comment>
<wp:comment>
<wp:comment_id>117</wp:comment_id>
<wp:comment_author><![CDATA[Bob]]></wp:comment_author>
<wp:comment_author_email>bob@thisisa.test</wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP>127.0.0.1</wp:comment_author_IP>
<wp:comment_date>2012-05-06 17:44:06</wp:comment_date>
<wp:comment_date_gmt>2012-05-06 22:44:06</wp:comment_date_gmt>
<wp:comment_content><![CDATA[Comment content.]]></wp:comment_content>
<wp:comment_approved>1</wp:comment_approved>
<wp:comment_type></wp:comment_type>
<wp:comment_parent>116</wp:comment_parent>
<wp:comment_user_id>3</wp:comment_user_id>
<wp:commentmeta>
<wp:meta_key>akismet_result</wp:meta_key>
<wp:meta_value><![CDATA[false]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_history</wp:meta_key>
<wp:meta_value><![CDATA[a:4:{s:4:"time";s:15:"1336344263.8658";s:7:"message";s:28:"Akismet cleared this comment";s:5:"event";s:9:"check-ham";s:4:"user";s:3:"bob";}]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_as_submitted</wp:meta_key>
<wp:meta_value><![CDATA[a:74:{s:15:"comment_post_ID";s:3:"422";s:14:"comment_author";s:3:"bob";s:20:"comment_author_email";s:25:"bob@thisisa.test";s:18:"comment_author_url";s:0:"";s:15:"comment_content";s:26:"Merci de l'avoir signalé.";s:14:"comment_parent";s:3:"116";s:7:"user_ID";s:1:"3";s:7:"user_ip";s:14:"127.0.0.1";s:10:"user_agent";s:139:"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/127.0.0.1 Chrome/127.0.0.1 Safari/535.19";s:8:"referrer";s:29:"http://thisisa.test/wp-admin/";s:4:"blog";s:19:"http://thisisa.test";s:9:"blog_lang";s:5:"en_US";s:12:"blog_charset";s:5:"UTF-8";s:9:"permalink";s:62:"http://thisisa.test/htop-un-visualiseur-interactif-de-process/";s:9:"user_role";s:13:"administrator";s:21:"akismet_comment_nonce";s:6:"passed";s:22:"POST_newcomment_author";s:0:"";s:28:"POST_newcomment_author_email";s:0:"";s:26:"POST_newcomment_author_url";s:0:"";s:12:"POST_user_ID";s:1:"3";s:11:"POST_action";s:15:"replyto-comment";s:15:"POST_comment_ID";s:3:"116";s:20:"POST_comment_post_ID";s:3:"422";s:11:"POST_status";s:0:"";s:13:"POST_position";s:2:"-1";s:13:"POST_checkbox";s:1:"0";s:9:"POST_mode";s:9:"dashboard";s:32:"POST__ajax_nonce-replyto-comment";s:10:"d1ad3bd917";s:32:"POST__wp_unfiltered_html_comment";s:10:"fc11aee860";s:12:"POST_content";s:26:"Merci de l'avoir signalé.";s:7:"POST_id";s:3:"422";s:21:"POST_comments_listing";s:0:"";s:15:"SERVER_SOFTWARE";s:12:"nginx/0.8.55";s:11:"REQUEST_URI";s:24:"/wp-admin/admin-ajax.php";s:4:"TERM";s:5:"xterm";s:17:"PHP_FCGI_CHILDREN";s:1:"4";s:4:"PATH";s:29:"/sbin:/usr/sbin:/bin:/usr/bin";s:1:"_";s:25:"/usr/local/bin/spawn-fcgi";s:3:"PWD";s:1:"/";s:4:"LANG";s:11:"en_US.UTF-8";s:5:"SHLVL";s:1:"2";s:21:"PHP_FCGI_MAX_REQUESTS";s:4:"1000";s:9:"FCGI_ROLE";s:9:"RESPONDER";s:12:"QUERY_STRING";s:0:"";s:14:"REQUEST_METHOD";s:4:"POST";s:12:"CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:14:"CONTENT_LENGTH";s:3:"322";s:11:"SCRIPT_NAME";s:24:"/wp-admin/admin-ajax.php";s:12:"DOCUMENT_URI";s:24:"/wp-admin/admin-ajax.php";s:13:"DOCUMENT_ROOT";s:14:"/home/blog";s:15:"SERVER_PROTOCOL";s:8:"HTTP/1.1";s:17:"GATEWAY_INTERFACE";s:7:"CGI/1.1";s:11:"REMOTE_ADDR";s:14:"127.0.0.1";s:11:"REMOTE_PORT";s:5:"10252";s:11:"SERVER_ADDR";s:13:"127.0.0.1";s:11:"SERVER_PORT";s:2:"80";s:11:"SERVER_NAME";s:12:"thisisa.test";s:15:"REDIRECT_STATUS";s:3:"200";s:15:"SCRIPT_FILENAME";s:38:"/home/blog/wp-admin/admin-ajax.php";s:9:"HTTP_HOST";s:12:"thisisa.test";s:15:"HTTP_CONNECTION";s:10:"keep-alive";s:19:"HTTP_CONTENT_LENGTH";s:3:"322";s:11:"HTTP_ORIGIN";s:19:"http://thisisa.test";s:21:"HTTP_X_REQUESTED_WITH";s:14:"XMLHttpRequest";s:15:"HTTP_USER_AGENT";s:139:"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/127.0.0.1 Chrome/127.0.0.1 Safari/535.19";s:17:"HTTP_CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:11:"HTTP_ACCEPT";s:3:"*/*";s:12:"HTTP_REFERER";s:29:"http://thisisa.test/wp-admin/";s:20:"HTTP_ACCEPT_ENCODING";s:17:"gzip,deflate,sdch";s:20:"HTTP_ACCEPT_LANGUAGE";s:35:"fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4";s:19:"HTTP_ACCEPT_CHARSET";s:30:"ISO-8859-1,utf-8;q=0.7,*;q=0.3";s:11:"HTTP_COOKIE";s:0:"";s:8:"PHP_SELF";s:24:"/wp-admin/admin-ajax.php";s:12:"REQUEST_TIME";s:10:"1336344246";}]]></wp:meta_value>
</wp:commentmeta>
</wp:comment>
<wp:comment>
<wp:comment_id>156</wp:comment_id>
<wp:comment_author><![CDATA[Ping back comment author]]></wp:comment_author>
<wp:comment_author_email></wp:comment_author_email>
<wp:comment_author_url>http://thisisa.test/to-article-you-ping-back/</wp:comment_author_url>
<wp:comment_author_IP>127.0.0.1</wp:comment_author_IP>
<wp:comment_date>2012-05-09 19:30:19</wp:comment_date>
<wp:comment_date_gmt>2012-05-10 00:30:19</wp:comment_date_gmt>
<wp:comment_content><![CDATA[[...]this is a ping pack [...] ]]></wp:comment_content>
<wp:comment_approved>trash</wp:comment_approved>
<wp:comment_type>pingback</wp:comment_type>
<wp:comment_parent>0</wp:comment_parent>
<wp:comment_user_id>0</wp:comment_user_id>
<wp:commentmeta>
<wp:meta_key>akismet_history</wp:meta_key>
<wp:meta_value><![CDATA[a:4:{s:4:"time";s:15:"1336610793.7343";s:7:"message";s:39:"bob changed the comment status to trash";s:5:"event";s:12:"status-trash";s:4:"user";s:3:"bob";}]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>_wp_trash_meta_status</wp:meta_key>
<wp:meta_value><![CDATA[0]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>_wp_trash_meta_time</wp:meta_key>
<wp:meta_value><![CDATA[1336610793]]></wp:meta_value>
</wp:commentmeta>
</wp:comment>
<wp:comment>
<wp:comment_id>122</wp:comment_id>
<wp:comment_author><![CDATA[bob]]></wp:comment_author>
<wp:comment_author_email>bob@thisisa.test</wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP>127.0.0.1</wp:comment_author_IP>
<wp:comment_date>2012-05-07 14:11:34</wp:comment_date>
<wp:comment_date_gmt>2012-05-07 19:11:34</wp:comment_date_gmt>
<wp:comment_content><![CDATA[Comment content.]]></wp:comment_content>
<wp:comment_approved>1</wp:comment_approved>
<wp:comment_type></wp:comment_type>
<wp:comment_parent>121</wp:comment_parent>
<wp:comment_user_id>3</wp:comment_user_id>
<wp:commentmeta>
<wp:meta_key>akismet_result</wp:meta_key>
<wp:meta_value><![CDATA[false]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_history</wp:meta_key>
<wp:meta_value><![CDATA[a:4:{s:4:"time";s:15:"1336417895.1821";s:7:"message";s:28:"Akismet cleared this comment";s:5:"event";s:9:"check-ham";s:4:"user";s:3:"bob";}]]></wp:meta_value>
</wp:commentmeta>
<wp:commentmeta>
<wp:meta_key>akismet_as_submitted</wp:meta_key>
<wp:meta_value><![CDATA[a:65:{s:15:"comment_post_ID";s:3:"422";s:14:"comment_author";s:3:"bob";s:20:"comment_author_email";s:25:"bob@thisisa.test";s:18:"comment_author_url";s:0:"";s:15:"comment_content";s:832:"Comment content";s:12:"comment_type";s:0:"";s:14:"comment_parent";s:3:"121";s:7:"user_ID";s:1:"3";s:7:"user_ip";s:14:"127.0.0.1";s:10:"user_agent";s:139:"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/127.0.0.1 Chrome/127.0.0.1 Safari/535.19";s:8:"referrer";s:62:"http://thisisa.test/htop-un-visualiseur-interactif-de-process/";s:4:"blog";s:19:"http://thisisa.test";s:9:"blog_lang";s:5:"en_US";s:12:"blog_charset";s:5:"UTF-8";s:9:"permalink";s:62:"http://thisisa.test/htop-un-visualiseur-interactif-de-process/";s:9:"user_role";s:13:"administrator";s:21:"akismet_comment_nonce";s:6:"passed";s:12:"POST_comment";s:832:"Comment content.";s:11:"POST_submit";s:14:"Submit Comment";s:20:"POST_comment_post_ID";s:3:"422";s:19:"POST_comment_parent";s:3:"121";s:32:"POST__wp_unfiltered_html_comment";s:10:"9dacc22ee8";s:26:"POST_akismet_comment_nonce";s:10:"b9cbdae553";s:15:"SERVER_SOFTWARE";s:12:"nginx/0.8.55";s:11:"REQUEST_URI";s:21:"/wp-comments-post.php";s:4:"TERM";s:5:"xterm";s:17:"PHP_FCGI_CHILDREN";s:1:"4";s:4:"PATH";s:29:"/sbin:/usr/sbin:/bin:/usr/bin";s:1:"_";s:25:"/usr/local/bin/spawn-fcgi";s:3:"PWD";s:1:"/";s:4:"LANG";s:11:"en_US.UTF-8";s:5:"SHLVL";s:1:"2";s:21:"PHP_FCGI_MAX_REQUESTS";s:4:"1000";s:9:"FCGI_ROLE";s:9:"RESPONDER";s:12:"QUERY_STRING";s:0:"";s:14:"REQUEST_METHOD";s:4:"POST";s:12:"CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:14:"CONTENT_LENGTH";s:4:"1143";s:11:"SCRIPT_NAME";s:21:"/wp-comments-post.php";s:12:"DOCUMENT_URI";s:21:"/wp-comments-post.php";s:13:"DOCUMENT_ROOT";s:14:"/home/blog";s:15:"SERVER_PROTOCOL";s:8:"HTTP/1.1";s:17:"GATEWAY_INTERFACE";s:7:"CGI/1.1";s:11:"REMOTE_ADDR";s:14:"127.0.0.1";s:11:"REMOTE_PORT";s:5:"10834";s:11:"SERVER_ADDR";s:13:"127.0.0.1";s:11:"SERVER_PORT";s:2:"80";s:11:"SERVER_NAME";s:12:"thisisa.test";s:15:"REDIRECT_STATUS";s:3:"200";s:15:"SCRIPT_FILENAME";s:35:"/home/blog/wp-comments-post.php";s:9:"HTTP_HOST";s:12:"thisisa.test";s:15:"HTTP_CONNECTION";s:10:"keep-alive";s:19:"HTTP_CONTENT_LENGTH";s:4:"1143";s:18:"HTTP_CACHE_CONTROL";s:9:"john-age=0";s:11:"HTTP_ORIGIN";s:19:"http://thisisa.test";s:15:"HTTP_USER_AGENT";s:139:"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/127.0.0.1 Chrome/127.0.0.1 Safari/535.19";s:17:"HTTP_CONTENT_TYPE";s:33:"application/x-www-form-urlencoded";s:11:"HTTP_ACCEPT";s:63:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";s:12:"HTTP_REFERER";s:62:"http://thisisa.test/htop-un-visualiseur-interactif-de-process/";s:20:"HTTP_ACCEPT_ENCODING";s:17:"gzip,deflate,sdch";s:20:"HTTP_ACCEPT_LANGUAGE";s:35:"fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4";s:19:"HTTP_ACCEPT_CHARSET";s:30:"ISO-8859-1,utf-8;q=0.7,*;q=0.3";s:11:"HTTP_COOKIE";s:0:"";s:8:"PHP_SELF";s:21:"/wp-comments-post.php";s:12:"REQUEST_TIME";s:10:"1336417893";}]]></wp:meta_value>
</wp:commentmeta>
</wp:comment>
</item>
<item>
<title>Post with raw data</title>
<link>http://thisisa.test/?p=173</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=173</guid>
<description></description>
<content:encoded><![CDATA[<h1>Pelicans are scary</h1>
Pelicans are supposed to eat fish, damn it!
<iframe width="420" height="315" src="http://www.youtube.com/embed/QNNl_uWmQXE" frameborder="0" allowfullscreen></iframe>
Bottom line: don't mess up with birds]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>173</wp:post_id>
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>post-with-raw-data</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-2"><![CDATA[Category 2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>A normal post with some &lt;html&gt; entities in the title. You can&#039;t miss them.</title>
<link>http://thisisa.test/?p=175</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=175</guid>
<description></description>
<content:encoded><![CDATA[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.
<ul>
<li>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.</li>
<li>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.</li>
</ul>
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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>175</wp:post_id>
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>html-entity-test</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-2"><![CDATA[Category 2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
<item>
<title>Code in List</title>
<link>http://thisisa.test/?p=175</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
<dc:creator>bob</dc:creator>
<guid isPermaLink="false">http://thisisa.test/?p=175</guid>
<description></description>
<content:encoded><![CDATA[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.
<ul>
<li>List Item One!</li>
<li>List Item Two!</li>
<li>This is a code sample
<pre>
<code>
a = [1, 2, 3]
b = [4, 5, 6]
for i in zip(a, b):
print i
</code>
</pre></li>
<li>List Item Four!</li>
</ul>
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.]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>175</wp:post_id>
<wp:post_date>2012-02-16 15:52:55</wp:post_date>
<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
<wp:comment_status>open</wp:comment_status>
<wp:ping_status>open</wp:ping_status>
<wp:post_name>code-in-list-test</wp:post_name>
<wp:status>publish</wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type>post</wp:post_type>
<wp:post_password></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="category" nicename="category-2"><![CDATA[Category 2]]></category>
<wp:postmeta>
<wp:meta_key>_edit_last</wp:meta_key>
<wp:meta_value><![CDATA[3]]></wp:meta_value>
</wp:postmeta>
</item>
</channel>
</rss>

View file

@ -0,0 +1,45 @@
# -*- 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
LOCALE = ""
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"

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A markdown powered article</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="/a-markdown-powered-article.html" rel="bookmark"
title="Permalink to A markdown powered article">A markdown powered article</a></h1>
</header>
<div class="entry-content">
<footer class="post-info">
<abbr class="published" title="2011-04-20T00:00:00">
Wed 20 April 2011
</abbr>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <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>
</div><!-- /.entry-content -->
</article>
</section>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Pelican Blog</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li ><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<h1>Archives for A Pelican Blog</h1>
<dl>
<dt>Fri 30 November 2012</dt>
<dd><a href="/filename_metadata-example.html">FILENAME_METADATA example</a></dd>
<dt>Wed 29 February 2012</dt>
<dd><a href="/second-article.html">Second article</a></dd>
<dt>Wed 20 April 2011</dt>
<dd><a href="/a-markdown-powered-article.html">A markdown powered article</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-1.html">Article 1</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-2.html">Article 2</a></dd>
<dt>Thu 17 February 2011</dt>
<dd><a href="/article-3.html">Article 3</a></dd>
<dt>Thu 02 December 2010</dt>
<dd><a href="/this-is-a-super-article.html">This is a super article !</a></dd>
<dt>Wed 20 October 2010</dt>
<dd><a href="/oh-yeah.html">Oh yeah !</a></dd>
<dt>Fri 15 October 2010</dt>
<dd><a href="/unbelievable.html">Unbelievable !</a></dd>
</dl>
</section>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

View file

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Article 1</title>
<link rel="stylesheet" href="/theme/css/main.css">
<link href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="A Pelican Blog Atom Feed" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">A Pelican Blog </a></h1>
<nav><ul>
<li><a href="/override/">Override url/save_as</a></li>
<li><a href="/pages/this-is-a-test-page.html">This is a test page</a></li>
<li ><a href="/category/bar.html">bar</a></li>
<li class="active"><a href="/category/cat1.html">cat1</a></li>
<li ><a href="/category/misc.html">misc</a></li>
<li ><a href="/category/yeah.html">yeah</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="/article-1.html" rel="bookmark"
title="Permalink to Article 1">Article 1</a></h1>
</header>
<div class="entry-content">
<footer class="post-info">
<abbr class="published" title="2011-02-17T00:00:00">
Thu 17 February 2011
</abbr>
<p>In <a href="/category/cat1.html">cat1</a>. </p>
</footer><!-- /.post-info --> <p>Article 1</p>
</div><!-- /.entry-content -->
</article>
</section>
<section id="extras" class="body">
<div class="social">
<h2>social</h2>
<ul>
<li><a href="/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more