Forráskód Böngészése

Merge remote-tracking branch 'upstream/master' into add_code_include

Colin Dunklau 11 éve
szülő
commit
84c8867f29
100 módosított fájl, 3314 hozzáadás és 177 törlés
  1. 2 0
      .gitignore
  2. 13 0
      .travis.yml
  3. 31 0
      Contributing.rst
  4. 665 0
      LICENSE
  5. 0 11
      README.rst
  6. 41 0
      Readme.rst
  7. 80 0
      assets/Readme.rst
  8. 1 0
      assets/__init__.py
  9. 60 0
      assets/assets.py
  10. 141 0
      assets/test_assets.py
  11. 1 0
      assets/test_data/static/css/style.min.css
  12. 19 0
      assets/test_data/static/css/style.scss
  13. 7 0
      assets/test_data/templates/base.html
  14. 66 0
      extract_toc/README.md
  15. 1 0
      extract_toc/__init__.py
  16. 32 0
      extract_toc/extract_toc.py
  17. 30 0
      github_activity/Readme.rst
  18. 1 0
      github_activity/__init__.py
  19. 71 0
      github_activity/github_activity.py
  20. 6 0
      global_license/Readme.rst
  21. 1 0
      global_license/__init__.py
  22. 18 0
      global_license/global_license.py
  23. 80 0
      goodreads_activity/Readme.md
  24. 1 0
      goodreads_activity/__init__.py
  25. 63 0
      goodreads_activity/goodreads_activity.py
  26. 15 0
      gravatar/Readme.rst
  27. 1 0
      gravatar/__init__.py
  28. 31 0
      gravatar/gravatar.py
  29. 10 0
      gzip_cache/Readme.rst
  30. 1 0
      gzip_cache/__init__.py
  31. 84 0
      gzip_cache/gzip_cache.py
  32. 54 0
      gzip_cache/test_gzip_cache.py
  33. 45 0
      html_rst_directive/Readme.rst
  34. 1 0
      html_rst_directive/__init__.py
  35. 30 0
      html_rst_directive/html_rst_directive.py
  36. 0 0
      latex/Readme.md
  37. 1 0
      latex/__init__.py
  38. 47 0
      latex/latex.py
  39. 32 0
      multi_part/Readme.md
  40. 1 0
      multi_part/__init__.py
  41. 34 0
      multi_part/multi_part.py
  42. 1 18
      pelicanext/neighbors/README.rst
  43. 1 0
      neighbors/__init__.py
  44. 0 0
      neighbors/neighbors.py
  45. 0 112
      pelicanext/latex/latex.py
  46. 0 36
      pelicanext/random_article/Readme.md
  47. 19 0
      random_article/Readme.md
  48. 1 0
      random_article/__init__.py
  49. 9 0
      pelicanext/random_article/random_article.py
  50. 19 0
      related_posts/Readme.rst
  51. 1 0
      related_posts/__init__.py
  52. 35 0
      related_posts/related_posts.py
  53. 65 0
      sitemap/Readme.rst
  54. 1 0
      sitemap/__init__.py
  55. 202 0
      sitemap/sitemap.py
  56. 27 0
      summary/Readme.rst
  57. 1 0
      summary/__init__.py
  58. 61 0
      summary/summary.py
  59. 75 0
      summary/test_summary.py
  60. 13 0
      test_data/Readme.rst
  61. 4 0
      test_data/content/2012-11-30_filename-metadata.rst
  62. 7 0
      test_data/content/another_super_article-fr.rst
  63. 20 0
      test_data/content/another_super_article.rst
  64. 9 0
      test_data/content/article2-fr.rst
  65. 9 0
      test_data/content/article2.rst
  66. 7 0
      test_data/content/cat1/article1.rst
  67. 6 0
      test_data/content/cat1/article2.rst
  68. 6 0
      test_data/content/cat1/article3.rst
  69. 7 0
      test_data/content/cat1/markdown-article.md
  70. 7 0
      test_data/content/draft_article.rst
  71. 2 0
      test_data/content/extra/robots.txt
  72. 9 0
      test_data/content/pages/hidden_page.rst
  73. 6 0
      test_data/content/pages/jinja2_template.html
  74. 9 0
      test_data/content/pages/override_url_saveas.rst
  75. 12 0
      test_data/content/pages/test_page.rst
  76. BIN
      test_data/content/pictures/Fat_Cat.jpg
  77. BIN
      test_data/content/pictures/Sushi.jpg
  78. BIN
      test_data/content/pictures/Sushi_Macro.jpg
  79. 36 0
      test_data/content/super_article.rst
  80. 9 0
      test_data/content/unbelievable.rst
  81. 1 0
      test_data/content/unwanted_file
  82. 45 0
      test_data/pelican.conf.py
  83. 446 0
      test_data/themes/notmyidea/static/css/main.css
  84. 205 0
      test_data/themes/notmyidea/static/css/pygment.css
  85. 52 0
      test_data/themes/notmyidea/static/css/reset.css
  86. 3 0
      test_data/themes/notmyidea/static/css/typogrify.css
  87. 48 0
      test_data/themes/notmyidea/static/css/wide.css
  88. BIN
      test_data/themes/notmyidea/static/images/icons/aboutme.png
  89. BIN
      test_data/themes/notmyidea/static/images/icons/bitbucket.png
  90. BIN
      test_data/themes/notmyidea/static/images/icons/delicious.png
  91. BIN
      test_data/themes/notmyidea/static/images/icons/facebook.png
  92. BIN
      test_data/themes/notmyidea/static/images/icons/github.png
  93. BIN
      test_data/themes/notmyidea/static/images/icons/gitorious.png
  94. BIN
      test_data/themes/notmyidea/static/images/icons/gittip.png
  95. BIN
      test_data/themes/notmyidea/static/images/icons/google-groups.png
  96. BIN
      test_data/themes/notmyidea/static/images/icons/google-plus.png
  97. BIN
      test_data/themes/notmyidea/static/images/icons/hackernews.png
  98. BIN
      test_data/themes/notmyidea/static/images/icons/lastfm.png
  99. BIN
      test_data/themes/notmyidea/static/images/icons/linkedin.png
  100. 0 0
      test_data/themes/notmyidea/static/images/icons/reddit.png

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*.pyc
+*.log

+ 13 - 0
.travis.yml

@@ -0,0 +1,13 @@
+language: python
+python:
+    - "2.7"
+    - "3.2"
+before_install:
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq --no-install-recommends ruby-sass
+install:
+    - pip install nose
+    - pip install -e git://github.com/getpelican/pelican.git#egg=pelican
+    - pip install --use-mirrors Markdown
+    - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors webassets cssmin; fi
+script: nosetests

+ 31 - 0
Contributing.rst

@@ -0,0 +1,31 @@
+Contributing a plugin
+=====================
+
+Details of how to write a plugin is explained in the official Pelican `docs`_.
+
+If you want to contribute, please fork this repository and issue your pull
+request. Make sure that your plugin follows the structure below::
+
+    my_plugin
+       ├──  __init__.py
+       ├──  my_plugin.py
+       ├──  test_my_plugin.py
+       └──  Readme.rst / Readme.md
+
+
+``my_plugin.py`` is the actual plugin implementation. Include a brief
+explanation of what the plugin does as a module docstring. Leave any further
+explanations and usage details to ``Readme`` file.
+
+``__init__.py`` should contain a single line with ``from .my_plugin import *``.
+
+Place tests for your plugin in the same folder with name ``test_my_plugin.py``.
+You can use ``test_data`` main folder, if you need content or templates in your tests.
+
+**Note:** Each plugin can contain a LICENSE file stating the license it's
+released under. If there is an absence of LICENSE then it defaults to the
+*GNU AFFERO GENERAL PUBLIC LICENSE Version 3*.
+
+Please refer to the ``LICENSE`` file for the full text of the license.
+
+.. _docs: http://docs.getpelican.com/en/latest/plugins.html#how-to-create-plugins

+ 665 - 0
LICENSE

@@ -0,0 +1,665 @@
+Unless the folder itself contains a LICENSE stating otherwise, all the files
+distributed here are released under the GNU AFFERO GENERAL PUBLIC LICENSE.
+
+
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ 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.
+
+                            Preamble
+
+  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 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/>.

+ 0 - 11
README.rst

@@ -1,11 +0,0 @@
-Pelican Plugins
-###############
-
-This repository contains plugins for Pelican. At the moment, this is only a
-subset of the available plugins, with others currently residing in the primary
-Pelican repository. Those latter plugins will eventually be moved here, so all
-plugins will be in one place.
-
-That being the case, if you have a plugin to contribute, please fork this
-Pelican Plugins repository and issue your pull request from there (as opposed
-to the primary Pelican repository).

+ 41 - 0
Readme.rst

@@ -0,0 +1,41 @@
+Pelican 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. Starting
+with 3.2, all plugins (including the ones previously in the core) are 
+moved here, so this is the central place for all plugins. 
+
+How to use plugins
+==================
+
+Easiest way to install and use these plugins is cloning this repo::
+
+    git clone https://github.com/getpelican/pelican-plugins
+
+and activating the ones you want in your settings file::
+
+    PLUGIN_PATH = 'path/to/pelican-plugins'
+    PLUGINS = ['assets', 'sitemap', 'gravatar']
+
+``PLUGIN_PATH`` can be a path relative to your settings file or an absolute path.
+
+Alternatively, if plugins are in an importable path, you can omit ``PLUGIN_PATH``
+and list them::
+
+    PLUGINS = ['assets', 'sitemap', 'gravatar']
+
+or you can ``import`` the plugin directly and give that::
+
+    import my_plugin
+    PLUGINS = [my_plugin, 'assets']
+
+Please refer to the ``Readme`` file in a plugin's folder for detailed information about 
+that plugin.
+
+Contributing a plugin
+=====================
+
+Please refer to the `Contributing`_ file.
+
+.. _Contributing: Contributing.rst

+ 80 - 0
assets/Readme.rst

@@ -0,0 +1,80 @@
+Asset management
+----------------
+
+This plugin allows you to use the `Webassets`_ module to manage assets such as
+CSS and JS files. The module must first be installed::
+
+    pip install webassets
+
+The Webassets module allows you to perform a number of useful asset management
+functions, including:
+
+* CSS minifier (``cssmin``, ``yui_css``, ...)
+* CSS compiler (``less``, ``sass``, ...)
+* JS minifier (``uglifyjs``, ``yui_js``, ``closure``, ...)
+
+Others filters include gzip compression, integration of images in CSS via data
+URIs, and more. Webassets can also append a version identifier to your asset
+URL to convince browsers to download new versions of your assets when you use
+far-future expires headers. Please refer to the `Webassets documentation`_ for
+more information.
+
+When used with Pelican, Webassets is configured to process assets in the
+``OUTPUT_PATH/theme`` directory. You can use Webassets in your templates by
+including one or more template tags. The Jinja variable ``{{ ASSET_URL }}`` can
+be used in templates and is relative to the ``theme/`` url. The
+``{{ ASSET_URL }}`` variable should be used in conjunction with the
+``{{ SITEURL }}`` variable in order to generate URLs properly. For example:
+
+.. code-block:: jinja
+
+    {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %}
+        <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+    {% endassets %}
+
+... will produce a minified css file with a version identifier that looks like:
+
+.. code-block:: html
+
+    <link href="http://{SITEURL}/theme/css/style.min.css?b3a7c807" rel="stylesheet">
+
+These filters can be combined. Here is an example that uses the SASS compiler
+and minifies the output:
+
+.. code-block:: jinja
+
+    {% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %}
+        <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+    {% endassets %}
+
+Another example for Javascript:
+
+.. code-block:: jinja
+
+    {% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %}
+        <script src="{{ SITEURL }}/{{ ASSET_URL }}"></script>
+    {% endassets %}
+
+The above will produce a minified and gzipped JS file:
+
+.. code-block:: html
+
+    <script src="http://{SITEURL}/theme/js/packed.js?00703b9d"></script>
+
+Pelican's debug mode is propagated to Webassets to disable asset packaging
+and instead work with the uncompressed assets.
+
+Many of Webasset's available compilers have additional configuration options
+(i.e. 'Less', 'Sass', 'Stylus', 'Closure_js').  You can pass these options to
+Webassets using the ``ASSET_CONFIG`` in your settings file.
+
+The following will handle Google Closure's compilation level and locate
+LessCSS's binary:
+
+.. code-block:: python
+
+    ASSET_CONFIG = (('closure_compressor_optimization', 'WHITESPACE_ONLY'),
+                    ('less_bin', 'lessc.cmd'), )
+
+.. _Webassets: https://github.com/miracle2k/webassets
+.. _Webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html

+ 1 - 0
assets/__init__.py

@@ -0,0 +1 @@
+from .assets import *

+ 60 - 0
assets/assets.py

@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+Asset management plugin for Pelican
+===================================
+
+This plugin allows you to use the `webassets`_ module to manage assets such as
+CSS and JS files.
+
+The ASSET_URL is set to a relative url to honor Pelican's RELATIVE_URLS
+setting. This requires the use of SITEURL in the templates::
+
+    <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+
+.. _webassets: https://webassets.readthedocs.org/
+
+"""
+from __future__ import unicode_literals
+
+import os
+import logging
+
+from pelican import signals
+logger = logging.getLogger(__name__)
+
+try:
+    import webassets
+    from webassets import Environment
+    from webassets.ext.jinja2 import AssetsExtension
+except ImportError:
+    webassets = None
+
+def add_jinja2_ext(pelican):
+    """Add Webassets to Jinja2 extensions in Pelican settings."""
+
+    pelican.settings['JINJA_EXTENSIONS'].append(AssetsExtension)
+
+
+def create_assets_env(generator):
+    """Define the assets environment and pass it to the generator."""
+
+    assets_url = 'theme/'
+    assets_src = os.path.join(generator.output_path, 'theme')
+    generator.env.assets_environment = Environment(assets_src, assets_url)
+
+    if 'ASSET_CONFIG' in generator.settings:
+        for item in generator.settings['ASSET_CONFIG']:
+            generator.env.assets_environment.config[item[0]] = item[1]
+
+    if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG":
+        generator.env.assets_environment.debug = True
+
+
+def register():
+    """Plugin registration."""
+    if webassets:
+        signals.initialized.connect(add_jinja2_ext)
+        signals.generator_init.connect(create_assets_env)
+    else:
+        logger.warning('`assets` failed to load dependency `webassets`.'
+                       '`assets` plugin not loaded.')

+ 141 - 0
assets/test_assets.py

@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+# from __future__ import unicode_literals
+
+import hashlib
+import locale
+import os
+from codecs import open
+from tempfile import mkdtemp
+from shutil import rmtree
+import unittest
+import subprocess
+
+from pelican import Pelican
+from pelican.settings import read_settings
+
+CUR_DIR = os.path.dirname(__file__)
+THEME_DIR = os.path.join(CUR_DIR, 'test_data')
+CSS_REF = open(os.path.join(THEME_DIR, 'static', 'css',
+                            'style.min.css')).read()
+CSS_HASH = hashlib.md5(CSS_REF).hexdigest()[0:8]
+
+
+def skipIfNoExecutable(executable):
+    """Skip test if `executable` is not found
+
+    Tries to run `executable` with subprocess to make sure it's in the path,
+    and skips the tests if not found (if subprocess raises a `OSError`).
+    """
+
+    with open(os.devnull, 'w') as fnull:
+        try:
+            res = subprocess.call(executable, stdout=fnull, stderr=fnull)
+        except OSError:
+            res = None
+
+    if res is None:
+        return unittest.skip('{0} executable not found'.format(executable))
+
+    return lambda func: func
+
+
+def module_exists(module_name):
+    """Test if a module is importable."""
+
+    try:
+        __import__(module_name)
+    except ImportError:
+        return False
+    else:
+        return True
+
+
+
+@unittest.skipUnless(module_exists('webassets'), "webassets isn't installed")
+@skipIfNoExecutable(['scss', '-v'])
+@skipIfNoExecutable(['cssmin', '--version'])
+class TestWebAssets(unittest.TestCase):
+    """Base class for testing webassets."""
+
+    def setUp(self, override=None):
+        import assets
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        settings = {
+            'PATH': os.path.join(os.path.dirname(CUR_DIR), 'test_data', 'content'),
+            'OUTPUT_PATH': self.temp_path,
+            'PLUGINS': [assets],
+            'THEME': THEME_DIR,
+            'LOCALE': locale.normalize('en_US'),
+        }
+        if override:
+            settings.update(override)
+
+        self.settings = read_settings(override=settings)
+        pelican = Pelican(settings=self.settings)
+        pelican.run()
+
+    def tearDown(self):
+        rmtree(self.temp_path)
+
+    def check_link_tag(self, css_file, html_file):
+        """Check the presence of `css_file` in `html_file`."""
+
+        link_tag = ('<link rel="stylesheet" href="{css_file}">'
+                    .format(css_file=css_file))
+        html = open(html_file).read()
+        self.assertRegexpMatches(html, link_tag)
+
+
+class TestWebAssetsRelativeURLS(TestWebAssets):
+    """Test pelican with relative urls."""
+
+
+    def setUp(self):
+        TestWebAssets.setUp(self, override={'RELATIVE_URLS': True})
+
+    def test_jinja2_ext(self):
+        # Test that the Jinja2 extension was correctly added.
+
+        from webassets.ext.jinja2 import AssetsExtension
+        self.assertIn(AssetsExtension, self.settings['JINJA_EXTENSIONS'])
+
+    def test_compilation(self):
+        # Compare the compiled css with the reference.
+
+        gen_file = os.path.join(self.temp_path, 'theme', 'gen',
+                                'style.{0}.min.css'.format(CSS_HASH))
+        self.assertTrue(os.path.isfile(gen_file))
+
+        css_new = open(gen_file).read()
+        self.assertEqual(css_new, CSS_REF)
+
+    def test_template(self):
+        # Look in the output files for the link tag.
+
+        css_file = './theme/gen/style.{0}.min.css'.format(CSS_HASH)
+        html_files = ['index.html', 'archives.html',
+                      'this-is-a-super-article.html']
+        for f in html_files:
+            self.check_link_tag(css_file, os.path.join(self.temp_path, f))
+
+        self.check_link_tag(
+            '../theme/gen/style.{0}.min.css'.format(CSS_HASH),
+            os.path.join(self.temp_path, 'category/yeah.html'))
+
+
+class TestWebAssetsAbsoluteURLS(TestWebAssets):
+    """Test pelican with absolute urls."""
+
+    def setUp(self):
+        TestWebAssets.setUp(self, override={'RELATIVE_URLS': False,
+                                            'SITEURL': 'http://localhost'})
+
+    def test_absolute_url(self):
+        # Look in the output files for the link tag with absolute url.
+
+        css_file = ('http://localhost/theme/gen/style.{0}.min.css'
+                    .format(CSS_HASH))
+        html_files = ['index.html', 'archives.html',
+                      'this-is-a-super-article.html']
+        for f in html_files:
+            self.check_link_tag(css_file, os.path.join(self.temp_path, f))

+ 1 - 0
assets/test_data/static/css/style.min.css

@@ -0,0 +1 @@
+body{font:14px/1.5 "Droid Sans",sans-serif;background-color:#e4e4e4;color:#242424}a{color:red}a:hover{color:orange}

+ 19 - 0
assets/test_data/static/css/style.scss

@@ -0,0 +1,19 @@
+/* -*- scss-compile-at-save: nil -*- */
+
+$baseFontFamily : "Droid Sans", sans-serif;
+$textColor      : #242424;
+$bodyBackground : #e4e4e4;
+
+body {
+  font: 14px/1.5 $baseFontFamily;
+  background-color: $bodyBackground;
+  color: $textColor;
+}
+
+a {
+  color: red;
+
+  &:hover {
+    color: orange;
+  }
+}

+ 7 - 0
assets/test_data/templates/base.html

@@ -0,0 +1,7 @@
+{% extends "!simple/base.html" %}
+
+{% block head %}
+  {% assets filters="scss,cssmin", output="gen/style.%(version)s.min.css", "css/style.scss" %}
+    <link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
+  {% endassets %}
+{% endblock %}

+ 66 - 0
extract_toc/README.md

@@ -0,0 +1,66 @@
+Extract Table of Content
+========================
+
+A Pelican plugin to extract table of contents (ToC) from `article.content` and
+place it in its own `article.toc` variable.
+
+Copyright (c) Talha Mansoor
+
+Author          | Talha Mansoor
+----------------|-----
+Author Email    | talha131@gmail.com
+Author Homepage | http://onCrashReboot.com
+Github Account  | https://github.com/talha131
+
+Acknowledgement
+---------------
+
+Thanks to [Avaris](https://github.com/avaris) for going out of the way to help
+me fix Unicode issues and doing a thorough code review.
+
+Why do you need it?
+===================
+
+Pelican can generate ToC of reST and Markdown files, using markup's respective
+directive and extension. ToC is generated and placed at the beginning of
+`article.content`. You cannot place the ToC in `<nav>` HTML5 tag, nor can you
+place the ToC at the end of your article's content because ToC is part of
+`article.content`.
+
+This plugin extracts ToC from `article.content` and places it in `article.toc`.
+
+Requirements
+============
+
+`extract_toc` requires BeautifulSoup.
+
+```bash
+pip install beautifulsoup4
+```
+
+How to Use
+==========
+
+**Important!** This plugin only works with reST and Markdown files. reST files
+should have `.rst` extension. Markdown files can have `.md`, `.mkd` or
+`markdown`.
+
+If ToC appears in your article at more than one places, `extract_toc` will
+remove only the first occurrence. You shouldn't probably need to have multiple
+ToC in your article. In case you need to display it multiple times, you can
+print it via your template.
+
+ToC generated by Markdown is enclosed in `<div class="toc">`. On the other hand
+ToC generated by reST is enclosed in `<div class="contents topic">`.
+`extract_toc` relies on this behavior to work.
+
+Template Example
+================
+
+```python
+{% if article.toc %}
+    <nav class="affix">
+    {{ article.toc }}
+    </nav>
+{% endif %}
+```

+ 1 - 0
extract_toc/__init__.py

@@ -0,0 +1 @@
+from .extract_toc import *

+ 32 - 0
extract_toc/extract_toc.py

@@ -0,0 +1,32 @@
+"""
+Extract Table of Content
+========================
+
+This plugin allows you to extract table of contents (ToC) from article.content
+and place it in its own article.toc variable.
+"""
+
+from os import path
+from bs4 import BeautifulSoup
+from pelican import signals, readers
+
+
+def extract_toc(content):
+    soup = BeautifulSoup(content._content)
+    filename = content.source_path
+    extension = path.splitext(filename)[1][1:]
+    toc = ''
+    # if it is a Markdown file
+    if extension in readers.MarkdownReader.file_extensions:
+        toc = soup.find('div', class_='toc')
+    # else if it is a reST file
+    elif extension in readers.RstReader.file_extensions:
+        toc = soup.find('div', class_='contents topic')
+    if toc:
+        toc.extract()
+        content._content = soup.decode()
+        content.toc = toc.decode()
+
+
+def register():
+    signals.content_object_init.connect(extract_toc)

+ 30 - 0
github_activity/Readme.rst

@@ -0,0 +1,30 @@
+GitHub activity
+---------------
+
+This plugin makes use of the `feedparser`_ library that you'll need to
+install.
+
+Set the ``GITHUB_ACTIVITY_FEED`` parameter to your GitHub activity feed.
+For example, to track Pelican project activity, the setting would be::
+
+     GITHUB_ACTIVITY_FEED = 'https://github.com/getpelican.atom'
+
+On the template side, you just have to iterate over the ``github_activity``
+variable, as in this example::
+
+     {% if GITHUB_ACTIVITY_FEED %}
+        <div class="social">
+                <h2>Github Activity</h2>
+                <ul>
+
+                {% for entry in github_activity %}
+                    <li><b>{{ entry[0] }}</b><br /> {{ entry[1] }}</li>
+                {% endfor %}
+                </ul>
+        </div><!-- /.github_activity -->
+     {% endif %}
+
+``github_activity`` is a list of lists. The first element is the title,
+and the second element is the raw HTML from GitHub.
+
+.. _feedparser: https://crate.io/packages/feedparser/

+ 1 - 0
github_activity/__init__.py

@@ -0,0 +1 @@
+from .github_activity import *

+ 71 - 0
github_activity/github_activity.py

@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# NEEDS WORK
+"""
+Copyright (c) Marco Milanesi <kpanic@gnufunk.org>
+
+Github Activity
+---------------
+A plugin to list your Github Activity
+"""
+
+from __future__ import unicode_literals, print_function
+
+import logging
+logger = logging.getLogger(__name__)
+
+from pelican import signals
+
+
+class GitHubActivity():
+    """
+        A class created to fetch github activity with feedparser
+    """
+    def __init__(self, generator):
+        import feedparser
+        self.activities = feedparser.parse(
+            generator.settings['GITHUB_ACTIVITY_FEED'])
+
+    def fetch(self):
+        """
+            returns a list of html snippets fetched from github actitivy feed
+        """
+
+        entries = []
+        for activity in self.activities['entries']:
+            entries.append(
+                    [element for element in [activity['title'],
+                        activity['content'][0]['value']]])
+
+        return entries
+
+
+def fetch_github_activity(gen, metadata):
+    """
+        registered handler for the github activity plugin
+        it puts in generator.context the html needed to be displayed on a
+        template
+    """
+
+    if 'GITHUB_ACTIVITY_FEED' in gen.settings.keys():
+        gen.context['github_activity'] = gen.plugin_instance.fetch()
+
+
+def feed_parser_initialization(generator):
+    """
+        Initialization of feed parser
+    """
+
+    generator.plugin_instance = GitHubActivity(generator)
+
+
+def register():
+    """
+        Plugin registration
+    """
+    try:
+        signals.article_generator_init.connect(feed_parser_initialization)
+        signals.article_generate_context.connect(fetch_github_activity)
+    except ImportError:
+        logger.warning('`github_activity` failed to load dependency `feedparser`.'
+                       '`github_activity` plugin not loaded.')

+ 6 - 0
global_license/Readme.rst

@@ -0,0 +1,6 @@
+Global license
+--------------
+
+This plugin allows you to define a ``LICENSE`` setting and adds the contents of that
+license variable to the article's context, making that variable available to use
+from within your theme's templates.

+ 1 - 0
global_license/__init__.py

@@ -0,0 +1 @@
+from .global_license import *

+ 18 - 0
global_license/global_license.py

@@ -0,0 +1,18 @@
+"""
+License plugin for Pelican
+==========================
+
+This plugin allows you to define a LICENSE setting and adds the contents of that
+license variable to the article's context, making that variable available to use
+from within your theme's templates.
+"""
+
+from pelican import signals
+
+def add_license(generator, metadata):
+    if 'license' not in metadata.keys()\
+        and 'LICENSE' in generator.settings.keys():
+            metadata['license'] = generator.settings['LICENSE']
+
+def register():
+    signals.article_generate_context.connect(add_license)

+ 80 - 0
goodreads_activity/Readme.md

@@ -0,0 +1,80 @@
+Goodreads Activity
+==================
+
+A Pelican plugin to lists books from your Goodreads shelves.
+
+Copyright (c) Talha Mansoor
+
+Author          | Talha Mansoor
+----------------|-----
+Author Email    | talha131@gmail.com 
+Author Homepage | http://onCrashReboot.com 
+Github Account  | https://github.com/talha131 
+
+### Credits
+
+This plugin is inspired by Marco Milanesi <kpanic@gnufunk.org> Github activity plugin.
+
+Requirements
+============
+
+`goodreads_activity` requires feedparser.
+
+```bash
+pip install feedparser
+```
+
+How to Use
+==========
+
+**Important** Unlike Marco's Github activity plugin, this plugin returns
+a dictionary composed of the books in your Goodreads shelf and their
+details.
+
+To enable it, set `GOODREADS_ACTIVITY_FEED` in your pelican config file. It should point to the activity feed of your bookshelf.
+
+To find your self's activity feed,
+
+1.  Open Goodreads homepage and login
+2.  Click on My Books in the top navigational bar
+3.  Select the bookshelf you are interested in from the left hand column
+4.  Look for RSS link in the footer. Copy it's link.
+
+Here is an example feed of currently-reading shelf,
+
+```python
+GOODREADS_ACTIVITY_FEED='http://www.goodreads.com/review/list_rss/8028663?key=b025l3000336epw1pix047e853agggannc9932ed&shelf=currently-reading'
+```
+
+You can access the `goodreads_activity` in your Jinja2 template. `goodreads_activity` is a dictionary. Its valid keys are
+
+1.  `shelf_title` it has the title of your shelf
+2.  `books` it is an array of book dictionary
+
+Valid keys for `book` dictionary are
+
+1.  `title`
+2.  `author`
+3.  `link` link to your book review
+4.  `l_cover` large cover
+5.  `m_cover` medium cover
+6.  `s_cover` small cover
+7.  `description`
+8.  `rating`
+9.  `review`
+10. `tags`
+
+Template Example
+================
+
+```python
+{% if GOODREADS_ACTIVITY_FEED %}
+    <h2>{{ goodreads_activity.shelf_title }}</h2>
+    {% for book in goodreads_activity.books %}
+        <img src="{{book.s_cover}}"/>
+        <header>{{book.title}}<small> by {{book.author}}</small></header>
+        <article>{{book.description|truncate(end='')}}
+        <a href={{book.link}} target="_blank">...more</a></article>
+    {% endfor %}
+{% endif %}
+```

+ 1 - 0
goodreads_activity/__init__.py

@@ -0,0 +1 @@
+from .goodreads_activity import *

+ 63 - 0
goodreads_activity/goodreads_activity.py

@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+"""
+Goodreads Activity
+==================
+
+A Pelican plugin to lists books from your Goodreads shelves.
+
+Copyright (c) Talha Mansoor
+"""
+
+from __future__ import unicode_literals
+
+import logging
+logger = logging.getLogger(__name__)
+
+from pelican import signals
+
+
+class GoodreadsActivity():
+    def __init__(self, generator):
+        import feedparser
+        self.activities = feedparser.parse(
+            generator.settings['GOODREADS_ACTIVITY_FEED'])
+
+    def fetch(self):
+        goodreads_activity = {
+            'shelf_title': self.activities.feed.title,
+            'books': []
+        }
+        for entry in self.activities['entries']:
+            book = {
+                'title': entry.title,
+                'author': entry.author_name,
+                'link': entry.link,
+                'l_cover': entry.book_large_image_url,
+                'm_cover': entry.book_medium_image_url,
+                's_cover': entry.book_small_image_url,
+                'description': entry.book_description,
+                'rating': entry.user_rating,
+                'review': entry.user_review,
+                'tags': entry.user_shelves
+            }
+            goodreads_activity['books'].append(book)
+
+        return goodreads_activity
+
+
+def fetch_goodreads_activity(gen, metadata):
+    if 'GOODREADS_ACTIVITY_FEED' in gen.settings:
+        gen.context['goodreads_activity'] = gen.goodreads.fetch()
+
+
+def initialize_feedparser(generator):
+    generator.goodreads = GoodreadsActivity(generator)
+
+
+def register():
+    try:
+        signals.article_generator_init.connect(initialize_feedparser)
+        signals.article_generate_context.connect(fetch_goodreads_activity)
+    except ImportError:
+        logger.warning('`goodreads_activity` failed to load dependency `feedparser`.'
+                       '`goodreads_activity` plugin not loaded.')

+ 15 - 0
gravatar/Readme.rst

@@ -0,0 +1,15 @@
+Gravatar
+--------
+
+This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
+makes the variable available within the article's context. You can add
+``AUTHOR_EMAIL`` to your settings file to define the default author's email
+address. Obviously, that email address must be associated with a Gravatar
+account.
+
+Alternatively, you can provide an email address from within article metadata::
+
+    :email:  john.doe@example.com
+
+If the email address is defined via at least one of the two methods above,
+the ``author_gravatar`` variable is added to the article's context.

+ 1 - 0
gravatar/__init__.py

@@ -0,0 +1 @@
+from .gravatar import *

+ 31 - 0
gravatar/gravatar.py

@@ -0,0 +1,31 @@
+"""
+Gravatar plugin for Pelican
+===========================
+
+This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
+makes the variable available within the article's context.
+"""
+
+import hashlib
+import six
+
+from pelican import signals
+
+
+def add_gravatar(generator, metadata):
+
+    #first check email
+    if 'email' not in metadata.keys()\
+        and 'AUTHOR_EMAIL' in generator.settings.keys():
+            metadata['email'] = generator.settings['AUTHOR_EMAIL']
+
+    #then add gravatar url
+    if 'email' in metadata.keys():
+        email_bytes = six.b(metadata['email']).lower()
+        gravatar_url = "http://www.gravatar.com/avatar/" + \
+                        hashlib.md5(email_bytes).hexdigest()
+        metadata["author_gravatar"] = gravatar_url
+
+
+def register():
+    signals.article_generate_context.connect(add_gravatar)

+ 10 - 0
gzip_cache/Readme.rst

@@ -0,0 +1,10 @@
+Gzip cache
+----------
+
+Certain web servers (e.g., Nginx) can use a static cache of gzip-compressed
+files to prevent the server from compressing files during an HTTP call. Since
+compression occurs at another time, these compressed files can be compressed
+at a higher compression level for increased optimization.
+
+The ``gzip_cache`` plugin compresses all common text type files into a ``.gz``
+file within the same directory as the original file.

+ 1 - 0
gzip_cache/__init__.py

@@ -0,0 +1 @@
+from .gzip_cache import *

+ 84 - 0
gzip_cache/gzip_cache.py

@@ -0,0 +1,84 @@
+'''
+Copyright (c) 2012 Matt Layman
+
+Gzip cache
+----------
+
+A plugin to create .gz cache files for optimization.
+'''
+
+import gzip
+import logging
+import os
+
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+# A list of file types to exclude from possible compression
+EXCLUDE_TYPES = [
+    # Compressed types
+    '.bz2',
+    '.gz',
+
+    # Audio types
+    '.aac',
+    '.flac',
+    '.mp3',
+    '.wma',
+
+    # Image types
+    '.gif',
+    '.jpg',
+    '.jpeg',
+    '.png',
+
+    # Video types
+    '.avi',
+    '.mov',
+    '.mp4',
+]
+
+def create_gzip_cache(pelican):
+    '''Create a gzip cache file for every file that a webserver would
+    reasonably want to cache (e.g., text type files).
+
+    :param pelican: The Pelican instance
+    '''
+    for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']):
+        for name in filenames:
+            if should_compress(name):
+                filepath = os.path.join(dirpath, name)
+                create_gzip_file(filepath)
+
+def should_compress(filename):
+    '''Check if the filename is a type of file that should be compressed.
+
+    :param filename: A file name to check against
+    '''
+    for extension in EXCLUDE_TYPES:
+        if filename.endswith(extension):
+            return False
+
+    return True
+
+def create_gzip_file(filepath):
+    '''Create a gzipped file in the same directory with a filepath.gz name.
+
+    :param filepath: A file to compress
+    '''
+    compressed_path = filepath + '.gz'
+
+    with open(filepath, 'rb') as uncompressed:
+        try:
+            logger.debug('Compressing: %s' % filepath)
+            compressed = gzip.open(compressed_path, 'wb')
+            compressed.writelines(uncompressed)
+        except Exception as ex:
+            logger.critical('Gzip compression failed: %s' % ex)
+        finally:
+            compressed.close()
+
+def register():
+    signals.finalized.connect(create_gzip_cache)
+

+ 54 - 0
gzip_cache/test_gzip_cache.py

@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+'''Core plugins unit tests'''
+
+import os
+import tempfile
+import unittest
+
+from contextlib import contextmanager
+from tempfile import mkdtemp
+from shutil import rmtree
+
+import gzip_cache
+
+@contextmanager
+def temporary_folder():
+    """creates a temporary folder, return it and delete it afterwards.
+
+    This allows to do something like this in tests:
+
+        >>> with temporary_folder() as d:
+            # do whatever you want
+    """
+    tempdir = mkdtemp()
+    try:
+        yield tempdir
+    finally:
+        rmtree(tempdir)
+
+
+class TestGzipCache(unittest.TestCase):
+
+    def test_should_compress(self):
+        # Some filetypes should compress and others shouldn't.
+        self.assertTrue(gzip_cache.should_compress('foo.html'))
+        self.assertTrue(gzip_cache.should_compress('bar.css'))
+        self.assertTrue(gzip_cache.should_compress('baz.js'))
+        self.assertTrue(gzip_cache.should_compress('foo.txt'))
+
+        self.assertFalse(gzip_cache.should_compress('foo.gz'))
+        self.assertFalse(gzip_cache.should_compress('bar.png'))
+        self.assertFalse(gzip_cache.should_compress('baz.mp3'))
+        self.assertFalse(gzip_cache.should_compress('foo.mov'))
+
+    def test_creates_gzip_file(self):
+        # A file matching the input filename with a .gz extension is created.
+
+        # The plugin walks over the output content after the finalized signal
+        # so it is safe to assume that the file exists (otherwise walk would
+        # not report it). Therefore, create a dummy file to use.
+        with temporary_folder() as tempdir:
+            _, a_html_filename = tempfile.mkstemp(suffix='.html', dir=tempdir)
+            gzip_cache.create_gzip_file(a_html_filename)
+            self.assertTrue(os.path.exists(a_html_filename + '.gz'))
+

+ 45 - 0
html_rst_directive/Readme.rst

@@ -0,0 +1,45 @@
+HTML tags for reStructuredText
+------------------------------
+
+This plugin allows you to use HTML tags from within reST documents. 
+
+
+Directives
+----------
+
+
+::
+
+    .. html::
+
+        (HTML code)
+
+
+Example
+-------
+
+A search engine::
+
+    .. html::
+
+       <form action="http://seeks.fr/search" method="GET">
+         <input type="text" value="Pelican v2" title="Search" maxlength="2048" name="q" autocomplete="on" />
+         <input type="hidden" name="lang" value="en" />
+         <input type="submit" value="Seeks !" id="search_button" />
+       </form>
+
+
+A contact form::
+
+    .. html::
+
+        <form method="GET" action="mailto:some email">
+          <p>
+            <input type="text" placeholder="Subject" name="subject">
+            <br />
+            <textarea name="body" placeholder="Message">
+            </textarea>
+            <br />
+            <input type="reset"><input type="submit">
+          </p>
+        </form>

+ 1 - 0
html_rst_directive/__init__.py

@@ -0,0 +1 @@
+from .html_rst_directive import *

+ 30 - 0
html_rst_directive/html_rst_directive.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""
+HTML tags for reStructuredText
+==============================
+
+This plugin allows you to use HTML tags from within reST documents. 
+
+"""
+
+from __future__ import unicode_literals
+from docutils import nodes
+from docutils.parsers.rst import directives, Directive
+
+
+class RawHtml(Directive):
+    required_arguments = 0
+    optional_arguments = 0
+    final_argument_whitespace = True
+    has_content = True
+
+    def run(self):
+        html = ' '.join(self.content)
+        node = nodes.raw('', html, format='html')
+        return [node]
+
+
+
+def register():
+    directives.register_directive('html', RawHtml)
+

pelicanext/latex/Readme.md → latex/Readme.md


+ 1 - 0
latex/__init__.py

@@ -0,0 +1 @@
+from .latex import *

+ 47 - 0
latex/latex.py

@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+"""
+Latex Plugin For Pelican
+========================
+
+This plugin allows you to write mathematical equations in your articles using Latex.
+It uses the MathJax Latex JavaScript library to render latex that is embedded in
+between `$..$` for inline math and `$$..$$` for displayed math. It also allows for 
+writing equations in by using `\begin{equation}`...`\end{equation}`.
+"""
+
+from pelican import signals
+
+latexScript = """
+    <script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type= "text/javascript">
+       MathJax.Hub.Config({
+           config: ["MMLorHTML.js"],
+           jax: ["input/TeX","input/MathML","output/HTML-CSS","output/NativeMML"],
+           TeX: { extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"], equationNumbers: { autoNumber: "AMS" } },
+           extensions: ["tex2jax.js","mml2jax.js","MathMenu.js","MathZoom.js"],
+           tex2jax: { 
+               inlineMath: [ [\'$\',\'$\'] ],
+               displayMath: [ [\'$$\',\'$$\'] ],
+               processEscapes: true },
+           "HTML-CSS": {
+               styles: { ".MathJax .mo, .MathJax .mi": {color: "black ! important"}}
+           }
+       });
+    </script>
+"""
+
+def addLatex(gen, metadata):
+    """
+        The registered handler for the latex plugin. It will add 
+        the latex script to the article metadata
+    """
+    if 'LATEX' in gen.settings.keys() and gen.settings['LATEX'] == 'article':
+        if 'latex' in metadata.keys():
+            metadata['latex'] = latexScript
+    else:
+        metadata['latex'] = latexScript
+
+def register():
+    """
+        Plugin registration
+    """
+    signals.article_generate_context.connect(addLatex)

+ 32 - 0
multi_part/Readme.md

@@ -0,0 +1,32 @@
+Multi parts posts
+-----------------
+
+The multi-part posts plugin allow you to write multi-part posts.
+
+In order to mark posts as part of a multi-part post, use the `:parts:` metadata:
+
+    :parts:  MY_AWESOME_MULTI_PART_POST
+
+You can then use the `article.metadata.parts_articles` variable in your templates 
+to display other parts of current post.
+
+For example:
+
+    {% if article.metadata.parts_articles %}
+        <ol class="parts">
+        <li>Post parts</li>
+        {% for part_article in article.metadata.parts_articles %}
+            {% if part_article == article %}
+                <li class="active">
+                    <a href='{{ SITEURL }}/{{ part_article.url }}'>{{ part_article.title }}
+                    </a>
+                </li>
+            {% else %}
+                <li>
+                    <a href='{{ SITEURL }}/{{ part_article.url }}'>{{ part_article.title }}
+                    </a>
+                </li>
+            {% endif %}
+        {% endfor %}
+        </ol>
+    {% endif %}

+ 1 - 0
multi_part/__init__.py

@@ -0,0 +1 @@
+from .multi_part import *

+ 34 - 0
multi_part/multi_part.py

@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (c) FELD Boris <lothiraldan@gmail.com>
+
+Multiple part support
+=====================
+
+Create a navigation menu for multi-part related_posts
+"""
+
+from collections import defaultdict
+
+from pelican import signals
+
+
+def aggregate_multi_part(generator):
+        multi_part = defaultdict(list)
+
+        for article in generator.articles:
+            if 'parts' in article.metadata:
+                multi_part[article.metadata['parts']].append(article)
+
+        for part_id in multi_part:
+            parts = multi_part[part_id]
+
+            # Sort by date
+            parts.sort(key=lambda x: x.metadata['date'])
+
+            for article in parts:
+                article.metadata['parts_articles'] = parts
+
+
+def register():
+    signals.article_generator_finalized.connect(aggregate_multi_part)

+ 1 - 18
pelicanext/neighbors/README.rst

@@ -4,23 +4,6 @@ Neighbor Articles Plugin for Pelican
 This plugin adds ``next_article`` (newer) and ``prev_article`` (older) 
 variables to the article's context
 
-Installation
-------------
-To enable, ensure that ``neighbors.py`` is in somewhere you can ``import``.
-Then use the following in your `settings`::
-
-    PLUGINS = ["neighbors"]
-
-Or you can put the plugin in ``plugins`` folder in pelican installation. You 
-can find the location by typing::
-
-    python -c 'import pelican.plugins as p, os; print os.path.dirname(p.__file__)'
-
-Once you get the folder, copy the ``neighbors.py`` there and use the following
-in your settings::
-
-    PLUGINS = ["pelican.plugins.neighbors"]
-
 Usage
 -----
 
@@ -41,4 +24,4 @@ Usage
             </a>
         </li>
     {% endif %}
-    </ul>
+    </ul>

+ 1 - 0
neighbors/__init__.py

@@ -0,0 +1 @@
+from .neighbors import *

pelicanext/neighbors/neighbors.py → neighbors/neighbors.py


+ 0 - 112
pelicanext/latex/latex.py

@@ -1,112 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Latex Plugin For Pelican
-========================
-
-This plugin allows you to write mathematical equations in your articles using Latex.
-It uses the MathJax Latex JavaScript library to render latex that is embedded in
-between `$..$` for inline math and `$$..$$` for displayed math. It also allows for 
-writing equations in by using `\begin{equation}`...`\end{equation}`.
-
-Installation
-------------
-
-To enable, ensure that `latex.py` is put somewhere that is accessible.
-Then use as follows by adding the following to your settings.py:
-
-    PLUGINS = ["latex"]
-
-Be careful: Not loading the plugin is easy to do, and difficult to detect. To
-make life easier, find where pelican is installed, and then copy the plugin
-there. An easy way to find where pelican is installed is to verbose list the
-available themes by typing `pelican-themes -l -v`. 
-
-Once the pelican folder is found, copy `latex.py` to the `plugins` folder. Then 
-add to settings.py like this:
-
-    PLUGINS = ["pelican.plugins.latex"]
-
-Now all that is left to do is to embed the following to your template file 
-between the `<head>` parameters (for the NotMyIdea template, this file is base.html)
-
-    {% if article and article.latex %}
-        {{ article.latex }}
-    {% endif %}
-
-Usage
------
-Latex will be embedded in every article. If however you want latex only for
-selected articles, then in settings.py, add
-
-    LATEX = 'article'
-
-And in each article, add the metadata key `latex:`. For example, with the above
-settings, creating an article that I want to render latex math, I would just 
-include 'Latex' as part of the metadata without any value:
-
-    Date: 1 sep 2012
-    Status: draft
-    Latex:
-
-Latex Examples
---------------
-###Inline
-Latex between `$`..`$`, for example, `$`x^2`$`, will be rendered inline 
-with respect to the current html block.
-
-###Displayed Math
-Latex between `$$`..`$$`, for example, `$$`x^2`$$`, will be rendered centered in a 
-new paragraph.
-
-###Equations
-Latex between `\begin` and `\end`, for example, `begin{equation}` x^2 `\end{equation}`, 
-will be rendered centered in a new paragraph with a right justified equation number 
-at the top of the paragraph. This equation number can be referenced in the document. 
-To do this, use a `label` inside of the equation format and then refer to that label 
-using `ref`. For example: `begin{equation}` `\label{eq}` X^2 `\end{equation}`. Now 
-refer to that equation number by `$`\ref{eq}`$`.
-   
-Template And Article Examples
------------------------------
-To see an example of this plugin in action, look at 
-[this article](http://doctrina.org/How-RSA-Works-With-Examples.html). To see how 
-this plugin works with a template, look at 
-[this template](https://github.com/barrysteyn/pelican_theme-personal_blog).
-"""
-
-from pelican import signals
-
-latexScript = """
-    <script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type= "text/javascript">
-       MathJax.Hub.Config({
-           config: ["MMLorHTML.js"],
-           jax: ["input/TeX","input/MathML","output/HTML-CSS","output/NativeMML"],
-           TeX: { extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"], equationNumbers: { autoNumber: "AMS" } },
-           extensions: ["tex2jax.js","mml2jax.js","MathMenu.js","MathZoom.js"],
-           tex2jax: { 
-               inlineMath: [ [\'$\',\'$\'] ],
-               displayMath: [ [\'$$\',\'$$\'] ],
-               processEscapes: true },
-           "HTML-CSS": {
-               styles: { ".MathJax .mo, .MathJax .mi": {color: "black ! important"}}
-           }
-       });
-    </script>
-"""
-
-def addLatex(gen, metadata):
-    """
-        The registered handler for the latex plugin. It will add 
-        the latex script to the article metadata
-    """
-    if 'LATEX' in gen.settings.keys() and gen.settings['LATEX'] == 'article':
-        if 'latex' in metadata.keys():
-            metadata['latex'] = latexScript
-    else:
-        metadata['latex'] = latexScript
-
-def register():
-    """
-        Plugin registration
-    """
-    signals.article_generate_context.connect(addLatex)

+ 0 - 36
pelicanext/random_article/Readme.md

@@ -1,36 +0,0 @@
-Random Article Plugin For Pelican
-========================
-
-This plugin generates a html file which redirect to a random article
-using javascript's window.location. The generated html file is 
-saved at SITEURL.
-
-Only published articles are listed to redirect.
-
-
-Installation
-------------
-
-To enable, ensure that `random_article.py` is put somewhere that is accessible.
-Then use as follows by adding the following to your settings.py:
-
-    PLUGINS = ["random_article"]
-
-An easy way to find where pelican is installed is to verbose list the
-available themes by typing `pelican-themes -l -v`.
-
-Once the pelican folder is found, copy `random_article.py` to the `plugins` folder. Then
-add to settings.py like this:
-
-    PLUGINS = ["pelican.plugins.random_article"]
-
-Usage
------
-
-To use it you have to add in your config file the name of the file to use:
-
-    RANDOM = 'random.html'
-
-Then in some template you add:
-
-    <a href="{{ SITEURL }}/{{ RANDOM }}">random article</a>

+ 19 - 0
random_article/Readme.md

@@ -0,0 +1,19 @@
+Random Article Plugin For Pelican
+========================
+
+This plugin generates a html file which redirect to a random article
+using javascript's `window.location`. The generated html file is 
+saved at `SITEURL`.
+
+Only published articles are listed to redirect.
+
+Usage
+-----
+
+To use it you have to add in your config file the name of the file to use:
+
+    RANDOM = 'random.html'
+
+Then in some template you add:
+
+    <a href="{{ SITEURL }}/{{ RANDOM }}">random article</a>

+ 1 - 0
random_article/__init__.py

@@ -0,0 +1 @@
+from .random_article import *

+ 9 - 0
pelicanext/random_article/random_article.py

@@ -1,4 +1,13 @@
 # -*- coding: utf-8 -*-
+"""
+Random Article Plugin For Pelican
+========================
+
+This plugin generates a html file which redirect to a random article
+using javascript's window.location. The generated html file is 
+saved at SITEURL.
+"""
+
 from __future__ import unicode_literals
 
 import os.path

+ 19 - 0
related_posts/Readme.rst

@@ -0,0 +1,19 @@
+Related posts
+-------------
+
+This plugin adds the ``related_posts`` variable to the article's context.
+By default, up to 5 articles are listed. You can customize this value by 
+defining ``RELATED_POSTS_MAX`` in your settings file::
+
+    RELATED_POSTS_MAX = 10
+
+You can then use the ``article.related_posts`` variable in your templates.
+For example::
+
+    {% if article.related_posts %}
+        <ul>
+        {% for related_post in article.related_posts %}
+            <li><a href="{{ SITEURL }}/{{ related_post.url }}">{{ related_post.title }}</a></li>
+        {% endfor %}
+        </ul>
+    {% endif %}

+ 1 - 0
related_posts/__init__.py

@@ -0,0 +1 @@
+from .related_posts import *

+ 35 - 0
related_posts/related_posts.py

@@ -0,0 +1,35 @@
+"""
+Related posts plugin for Pelican
+================================
+
+Adds related_posts variable to article's context
+"""
+
+from pelican import signals
+from collections import Counter
+
+
+def add_related_posts(generator):
+    # get the max number of entries from settings
+    # or fall back to default (5)
+    numentries = generator.settings.get('RELATED_POSTS_MAX', 5)
+
+    for article in generator.articles:
+        # no tag, no relation
+        if not hasattr(article, 'tags'):
+            continue
+
+        # score = number of common tags
+        scores = Counter()
+        for tag in article.tags:
+            scores += Counter(generator.tags[tag])
+
+        # remove itself
+        scores.pop(article)
+
+        article.related_posts = [other for other, count 
+            in scores.most_common(numentries)]
+
+
+def register():
+    signals.article_generator_finalized.connect(add_related_posts)

+ 65 - 0
sitemap/Readme.rst

@@ -0,0 +1,65 @@
+Sitemap
+-------
+
+The sitemap plugin generates plain-text or XML sitemaps. You can use the
+``SITEMAP`` variable in your settings file to configure the behavior of the
+plugin.
+
+The ``SITEMAP`` variable must be a Python dictionary and can contain three keys:
+
+- ``format``, which sets the output format of the plugin (``xml`` or ``txt``)
+
+- ``priorities``, which is a dictionary with three keys:
+
+  - ``articles``, the priority for the URLs of the articles and their
+    translations
+
+  - ``pages``, the priority for the URLs of the static pages
+
+  - ``indexes``, the priority for the URLs of the index pages, such as tags,
+     author pages, categories indexes, archives, etc...
+
+  All the values of this dictionary must be decimal numbers between ``0`` and ``1``.
+
+- ``changefreqs``, which is a dictionary with three items:
+
+  - ``articles``, the update frequency of the articles
+
+  - ``pages``, the update frequency of the pages
+
+  - ``indexes``, the update frequency of the index pages
+
+  Valid frequency values are ``always``, ``hourly``, ``daily``, ``weekly``, ``monthly``,
+  ``yearly`` and ``never``.
+
+If a key is missing or a value is incorrect, it will be replaced with the
+default value.
+
+The sitemap is saved in ``<output_path>/sitemap.<format>``.
+
+.. note::
+   ``priorities`` and ``changefreqs`` are information for search engines.
+   They are only used in the XML sitemaps.
+   For more information: <http://www.sitemaps.org/protocol.html#xmlTagDefinitions>
+
+**Example**
+
+Here is an example configuration (it's also the default settings):
+
+.. code-block:: python
+
+    PLUGINS=['pelican.plugins.sitemap',]
+
+    SITEMAP = {
+        'format': 'xml',
+        'priorities': {
+            'articles': 0.5,
+            'indexes': 0.5,
+            'pages': 0.5
+        },
+        'changefreqs': {
+            'articles': 'monthly',
+            'indexes': 'daily',
+            'pages': 'monthly'
+        }
+    }

+ 1 - 0
sitemap/__init__.py

@@ -0,0 +1 @@
+from .sitemap import *

+ 202 - 0
sitemap/sitemap.py

@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+'''
+Sitemap
+-------
+
+The sitemap plugin generates plain-text or XML sitemaps.
+'''
+
+from __future__ import unicode_literals
+
+import collections
+import os.path
+
+from datetime import datetime
+from logging import warning, info
+from codecs import open
+
+from pelican import signals, contents
+
+TXT_HEADER = """{0}/index.html
+{0}/archives.html
+{0}/tags.html
+{0}/categories.html
+"""
+
+XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>
+<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
+xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+"""
+
+XML_URL = """
+<url>
+<loc>{0}/{1}</loc>
+<lastmod>{2}</lastmod>
+<changefreq>{3}</changefreq>
+<priority>{4}</priority>
+</url>
+"""
+
+XML_FOOTER = """
+</urlset>
+"""
+
+
+def format_date(date):
+    if date.tzinfo:
+        tz = date.strftime('%s')
+        tz = tz[:-2] + ':' + tz[-2:]
+    else:
+        tz = "-00:00"
+    return date.strftime("%Y-%m-%dT%H:%M:%S") + tz
+
+
+class SitemapGenerator(object):
+
+    def __init__(self, context, settings, path, theme, output_path, *null):
+
+        self.output_path = output_path
+        self.context = context
+        self.now = datetime.now()
+        self.siteurl = settings.get('SITEURL')
+
+        self.format = 'xml'
+
+        self.changefreqs = {
+            'articles': 'monthly',
+            'indexes': 'daily',
+            'pages': 'monthly'
+        }
+
+        self.priorities = {
+            'articles': 0.5,
+            'indexes': 0.5,
+            'pages': 0.5
+        }
+
+        config = settings.get('SITEMAP', {})
+
+        if not isinstance(config, dict):
+            warning("sitemap plugin: the SITEMAP setting must be a dict")
+        else:
+            fmt = config.get('format')
+            pris = config.get('priorities')
+            chfreqs = config.get('changefreqs')
+
+            if fmt not in ('xml', 'txt'):
+                warning("sitemap plugin: SITEMAP['format'] must be `txt' or `xml'")
+                warning("sitemap plugin: Setting SITEMAP['format'] on `xml'")
+            elif fmt == 'txt':
+                self.format = fmt
+                return
+
+            valid_keys = ('articles', 'indexes', 'pages')
+            valid_chfreqs = ('always', 'hourly', 'daily', 'weekly', 'monthly',
+                    'yearly', 'never')
+
+            if isinstance(pris, dict):
+                # We use items for Py3k compat. .iteritems() otherwise
+                for k, v in pris.items():
+                    if k in valid_keys and not isinstance(v, (int, float)):
+                        default = self.priorities[k]
+                        warning("sitemap plugin: priorities must be numbers")
+                        warning("sitemap plugin: setting SITEMAP['priorities']"
+                                "['{0}'] on {1}".format(k, default))
+                        pris[k] = default
+                self.priorities.update(pris)
+            elif pris is not None:
+                warning("sitemap plugin: SITEMAP['priorities'] must be a dict")
+                warning("sitemap plugin: using the default values")
+
+            if isinstance(chfreqs, dict):
+                # .items() for py3k compat.
+                for k, v in chfreqs.items():
+                    if k in valid_keys and v not in valid_chfreqs:
+                        default = self.changefreqs[k]
+                        warning("sitemap plugin: invalid changefreq `{0}'".format(v))
+                        warning("sitemap plugin: setting SITEMAP['changefreqs']"
+                                "['{0}'] on '{1}'".format(k, default))
+                        chfreqs[k] = default
+                self.changefreqs.update(chfreqs)
+            elif chfreqs is not None:
+                warning("sitemap plugin: SITEMAP['changefreqs'] must be a dict")
+                warning("sitemap plugin: using the default values")
+
+
+
+    def write_url(self, page, fd):
+
+        if getattr(page, 'status', 'published') != 'published':
+            return
+
+        page_path = os.path.join(self.output_path, page.url)
+        if not os.path.exists(page_path):
+            return
+
+        lastmod = format_date(getattr(page, 'date', self.now))
+
+        if isinstance(page, contents.Article):
+            pri = self.priorities['articles']
+            chfreq = self.changefreqs['articles']
+        elif isinstance(page, contents.Page):
+            pri = self.priorities['pages']
+            chfreq = self.changefreqs['pages']
+        else:
+            pri = self.priorities['indexes']
+            chfreq = self.changefreqs['indexes']
+
+
+        if self.format == 'xml':
+            fd.write(XML_URL.format(self.siteurl, page.url, lastmod, chfreq, pri))
+        else:
+            fd.write(self.siteurl + '/' + loc + '\n')
+
+
+    def generate_output(self, writer):
+        path = os.path.join(self.output_path, 'sitemap.{0}'.format(self.format))
+
+        pages = self.context['pages'] + self.context['articles'] \
+                + [ c for (c, a) in self.context['categories']] \
+                + [ t for (t, a) in self.context['tags']] \
+                + [ a for (a, b) in self.context['authors']]
+
+        for article in self.context['articles']:
+            pages += article.translations
+
+        info('writing {0}'.format(path))
+
+        with open(path, 'w', encoding='utf-8') as fd:
+
+            if self.format == 'xml':
+                fd.write(XML_HEADER)
+            else:
+                fd.write(TXT_HEADER.format(self.siteurl))
+
+            FakePage = collections.namedtuple('FakePage',
+                                              ['status',
+                                               'date',
+                                               'url'])
+
+            for standard_page_url in ['index.html',
+                                      'archives.html',
+                                      'tags.html',
+                                      'categories.html']:
+                fake = FakePage(status='published',
+                                date=self.now,
+                                url=standard_page_url)
+                self.write_url(fake, fd)
+
+            for page in pages:
+                self.write_url(page, fd)
+
+            if self.format == 'xml':
+                fd.write(XML_FOOTER)
+
+
+def get_generators(generators):
+    return SitemapGenerator
+
+
+def register():
+    signals.get_generators.connect(get_generators)

+ 27 - 0
summary/Readme.rst

@@ -0,0 +1,27 @@
+Summary
+-------
+
+This plugin allows easy, variable length summaries directly embedded into the 
+body of your articles. It introduces two new settings: ``SUMMARY_BEGIN_MARKER``
+and ``SUMMARY_END_MARKER``: strings which can be placed directly into an article
+to mark the beginning and end of a summary. When found, the standard 
+``SUMMARY_MAX_LENGTH`` setting will be ignored. The markers themselves will also
+be removed from your articles before they are published. The default values
+are ``<!-- PELICAN_BEGIN_SUMMARY -->`` and ``<!-- PELICAN_END_SUMMARY -->``.
+For example::
+
+    Title: My super title
+    Date: 2010-12-03 10:20
+    Tags: thats, awesome
+    Category: yeah
+    Slug: my-super-post
+    Author: Alexis Metaireau
+    
+    This is the content of my super blog post.
+    <!-- PELICAN_END_SUMMARY -->
+    and this content occurs after the summary.
+
+Here, the summary is taken to be the first line of the post. Because no
+beginning marker was found, it starts at the top of the body. It is possible
+to leave out the end marker instead, in which case the summary will start at the
+beginning marker and continue to the end of the body.

+ 1 - 0
summary/__init__.py

@@ -0,0 +1 @@
+from .summary import *

+ 61 - 0
summary/summary.py

@@ -0,0 +1,61 @@
+"""
+Summary
+-------
+
+This plugin allows easy, variable length summaries directly embedded into the 
+body of your articles.
+"""
+
+import types
+
+from pelican import signals
+
+def initialized(pelican):
+    from pelican.settings import _DEFAULT_CONFIG
+    _DEFAULT_CONFIG.setdefault('SUMMARY_BEGIN_MARKER',
+                               '<!-- PELICAN_BEGIN_SUMMARY -->')
+    _DEFAULT_CONFIG.setdefault('SUMMARY_END_MARKER',
+                               '<!-- PELICAN_END_SUMMARY -->')
+    if pelican:
+        pelican.settings.setdefault('SUMMARY_BEGIN_MARKER',
+                                    '<!-- PELICAN_BEGIN_SUMMARY -->')
+        pelican.settings.setdefault('SUMMARY_END_MARKER',
+                                    '<!-- PELICAN_END_SUMMARY -->')
+
+def content_object_init(instance):
+    # if summary is already specified, use it
+    if 'summary' in instance.metadata:
+        return
+
+    def _get_content(self):
+        content = self._content
+        if self.settings['SUMMARY_BEGIN_MARKER']:
+            content = content.replace(
+                self.settings['SUMMARY_BEGIN_MARKER'], '', 1)
+        if self.settings['SUMMARY_END_MARKER']:
+            content = content.replace(
+                self.settings['SUMMARY_END_MARKER'], '', 1)
+        return content
+    instance._get_content = types.MethodType(_get_content, instance)
+
+    # extract out our summary
+    if not hasattr(instance, '_summary') and instance._content is not None:
+        content = instance._content
+        begin_summary = -1
+        end_summary = -1
+        if instance.settings['SUMMARY_BEGIN_MARKER']:
+            begin_summary = content.find(instance.settings['SUMMARY_BEGIN_MARKER'])
+        if instance.settings['SUMMARY_END_MARKER']:
+            end_summary = content.find(instance.settings['SUMMARY_END_MARKER'])
+        if begin_summary != -1 or end_summary != -1:
+            # the beginning position has to take into account the length
+            # of the marker
+            begin_summary = (begin_summary +
+                            len(instance.settings['SUMMARY_BEGIN_MARKER'])
+                            if begin_summary != -1 else 0)
+            end_summary = end_summary if end_summary != -1 else None
+            instance._summary = content[begin_summary:end_summary]
+
+def register():
+    signals.initialized.connect(initialized)
+    signals.content_object_init.connect(content_object_init)

+ 75 - 0
summary/test_summary.py

@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+from jinja2.utils import generate_lorem_ipsum
+
+# generate one paragraph, enclosed with <p>
+TEST_CONTENT = str(generate_lorem_ipsum(n=1))
+TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False)
+
+
+from pelican.contents import Page
+
+import summary
+
+class TestSummary(unittest.TestCase):
+    def setUp(self):
+        super(TestSummary, self).setUp()
+
+        summary.register()
+        summary.initialized(None)
+        self.page_kwargs = {
+            'content': TEST_CONTENT,
+            'context': {
+                'localsiteurl': '',
+            },
+            'metadata': {
+                'summary': TEST_SUMMARY,
+                'title': 'foo bar',
+                'author': 'Blogger',
+            },
+        }
+
+    def _copy_page_kwargs(self):
+        # make a deep copy of page_kwargs
+        page_kwargs = dict([(key, self.page_kwargs[key]) for key in
+                            self.page_kwargs])
+        for key in page_kwargs:
+            if not isinstance(page_kwargs[key], dict):
+                break
+            page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey])
+                                     for subkey in page_kwargs[key]])
+
+        return page_kwargs
+
+    def test_end_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+            TEST_SUMMARY + '<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_SUMMARY)
+        self.assertEqual(page.content, TEST_SUMMARY + TEST_CONTENT)
+
+    def test_begin_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+            'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_CONTENT)
+        self.assertEqual(page.content, 'FOOBAR' + TEST_CONTENT)
+
+    def test_begin_end_summary(self):
+        page_kwargs = self._copy_page_kwargs()
+        del page_kwargs['metadata']['summary']
+        page_kwargs['content'] = (
+                'FOOBAR<!-- PELICAN_BEGIN_SUMMARY -->' + TEST_SUMMARY +
+                '<!-- PELICAN_END_SUMMARY -->' + TEST_CONTENT)
+        page = Page(**page_kwargs)
+        # test both the summary and the marker removal
+        self.assertEqual(page.summary, TEST_SUMMARY)
+        self.assertEqual(page.content, 'FOOBAR' + TEST_SUMMARY + TEST_CONTENT)

+ 13 - 0
test_data/Readme.rst

@@ -0,0 +1,13 @@
+Test Data
+---------
+
+Place tests for your plugin here. ``test_data`` folder contains following
+common data for your tests, if you need them. 
+
+===============   ===========================
+File/Folder       Description
+===============   ===========================
+content           A sample content folder
+themes            Default themes from Pelican
+pelican.conf.py   A sample settings file
+===============   ===========================

+ 4 - 0
test_data/content/2012-11-30_filename-metadata.rst

@@ -0,0 +1,4 @@
+FILENAME_METADATA example
+#########################
+
+Some cool stuff!

+ 7 - 0
test_data/content/another_super_article-fr.rst

@@ -0,0 +1,7 @@
+Trop bien !
+###########
+
+:lang: fr
+:slug: oh-yeah
+
+Et voila du contenu en français

+ 20 - 0
test_data/content/another_super_article.rst

@@ -0,0 +1,20 @@
+Oh yeah !
+#########
+
+:tags: oh, bar, yeah
+:date: 2010-10-20 10:14
+:category: bar
+:author: Alexis Métaireau
+:slug: oh-yeah
+:license: WTFPL
+
+Why not ?
+=========
+
+After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
+YEAH !
+
+.. image:: |filename|/pictures/Sushi.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text

+ 9 - 0
test_data/content/article2-fr.rst

@@ -0,0 +1,9 @@
+Deuxième article
+################
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: fr
+:slug: second-article
+
+Ceci est un article, en français.

+ 9 - 0
test_data/content/article2.rst

@@ -0,0 +1,9 @@
+Second article
+##############
+
+:tags: foo, bar, baz
+:date: 2012-02-29
+:lang: en
+:slug: second-article
+
+This is some article, in english

+ 7 - 0
test_data/content/cat1/article1.rst

@@ -0,0 +1,7 @@
+Article 1
+#########
+
+:date: 2011-02-17
+:yeah: oh yeah !
+
+Article 1

+ 6 - 0
test_data/content/cat1/article2.rst

@@ -0,0 +1,6 @@
+Article 2
+#########
+
+:date: 2011-02-17
+
+Article 2

+ 6 - 0
test_data/content/cat1/article3.rst

@@ -0,0 +1,6 @@
+Article 3
+#########
+
+:date: 2011-02-17
+
+Article 3

+ 7 - 0
test_data/content/cat1/markdown-article.md

@@ -0,0 +1,7 @@
+Title: A markdown powered article
+Date: 2011-04-20
+
+You're mutually oblivious.
+
+[a root-relative link to unbelievable](|filename|/unbelievable.rst)
+[a file-relative link to unbelievable](|filename|../unbelievable.rst)

+ 7 - 0
test_data/content/draft_article.rst

@@ -0,0 +1,7 @@
+A draft article
+###############
+
+:status: draft
+
+This is a draft article, it should live under the /drafts/ folder and not be
+listed anywhere else.

+ 2 - 0
test_data/content/extra/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /static/pictures

+ 9 - 0
test_data/content/pages/hidden_page.rst

@@ -0,0 +1,9 @@
+This is a test hidden page
+##########################
+
+:category: test
+:status: hidden
+
+This is great for things like error(404) pages
+Anyone can see this page but it's not linked to anywhere!
+

+ 6 - 0
test_data/content/pages/jinja2_template.html

@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% block content %}
+
+Some text
+
+{% endblock %}

+ 9 - 0
test_data/content/pages/override_url_saveas.rst

@@ -0,0 +1,9 @@
+Override url/save_as
+####################
+
+:date: 2012-12-07
+:url: override/
+:save_as: override/index.html
+
+Test page which overrides save_as and url so that this page will be generated
+at a custom location.

+ 12 - 0
test_data/content/pages/test_page.rst

@@ -0,0 +1,12 @@
+This is a test page
+###################
+
+:category: test
+
+Just an image.
+
+.. image:: |filename|/pictures/Fat_Cat.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+

BIN
test_data/content/pictures/Fat_Cat.jpg


BIN
test_data/content/pictures/Sushi.jpg


BIN
test_data/content/pictures/Sushi_Macro.jpg


+ 36 - 0
test_data/content/super_article.rst

@@ -0,0 +1,36 @@
+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**.
+
+Some content here !
+
+This is a simple title
+======================
+
+And here comes the cool stuff_.
+
+.. image:: |filename|/pictures/Sushi.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+
+.. image:: |filename|/pictures/Sushi_Macro.jpg
+   :height: 450 px
+   :width: 600 px
+   :alt: alternate text
+
+::
+
+   >>> from ipdb import set_trace
+   >>> set_trace()
+
+→ And now try with some utf8 hell: ééé
+
+.. _stuff: http://books.couchdb.org/relax/design-documents/views

+ 9 - 0
test_data/content/unbelievable.rst

@@ -0,0 +1,9 @@
+Unbelievable !
+##############
+
+:date: 2010-10-15 20:30
+
+Or completely awesome. Depends the needs.
+
+`a root-relative link to markdown-article <|filename|/cat1/markdown-article.md>`_
+`a file-relative link to markdown-article <|filename|cat1/markdown-article.md>`_

+ 1 - 0
test_data/content/unwanted_file

@@ -0,0 +1 @@
+not to be parsed

+ 45 - 0
test_data/pelican.conf.py

@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+AUTHOR = 'Alexis Métaireau'
+SITENAME = "Alexis' log"
+SITEURL = 'http://blog.notmyidea.org'
+TIMEZONE = "Europe/Paris"
+
+GITHUB_URL = 'http://github.com/ametaireau/'
+DISQUS_SITENAME = "blog-notmyidea"
+PDF_GENERATOR = False
+REVERSE_CATEGORY_ORDER = True
+LOCALE = "C"
+DEFAULT_PAGINATION = 4
+DEFAULT_DATE = (2012, 3, 2, 14, 1, 1)
+
+FEED_ALL_RSS = 'feeds/all.rss.xml'
+CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
+
+LINKS = (('Biologeek', 'http://biologeek.org'),
+         ('Filyb', "http://filyb.info/"),
+         ('Libert-fr', "http://www.libert-fr.com"),
+         ('N1k0', "http://prendreuncafe.com/blog/"),
+         ('Tarek Ziadé', "http://ziade.org/blog"),
+         ('Zubin Mithra', "http://zubin71.wordpress.com/"),)
+
+SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
+          ('lastfm', 'http://lastfm.com/user/akounet'),
+          ('github', 'http://github.com/ametaireau'),)
+
+# global metadata to all the contents
+DEFAULT_METADATA = (('yeah', 'it is'),)
+
+# static paths will be copied under the same name
+STATIC_PATHS = ["pictures", ]
+
+# A list of files to copy from the source to the destination
+FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)
+
+# custom page generated with a jinja2 template
+TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'}
+
+# foobar will not be used, because it's not in caps. All configuration keys
+# have to be in caps
+foobar = "barbaz"

+ 446 - 0
test_data/themes/notmyidea/static/css/main.css

@@ -0,0 +1,446 @@
+/*
+	Name: Smashing HTML5
+	Date: July 2009
+	Description: Sample layout for HTML5 and CSS3 goodness.
+	Version: 1.0
+	Author: Enrique Ramírez
+	Autor URI: http://enrique-ramirez.com
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("pygment.css");
+@import url("typogrify.css");
+@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
+
+/***** Global *****/
+/* Body */
+body {
+    background: #F5F4EF;
+    color: #000305;
+    font-size: 87.5%; /* Base font size: 14px */
+    font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+    line-height: 1.429;
+    margin: 0;
+    padding: 0;
+    text-align: left;
+}
+
+/* Headings */
+h1 {font-size: 2em }
+h2 {font-size: 1.571em}	/* 22px */
+h3 {font-size: 1.429em}	/* 20px */
+h4 {font-size: 1.286em}	/* 18px */
+h5 {font-size: 1.143em}	/* 16px */
+h6 {font-size: 1em}		/* 14px */
+
+h1, h2, h3, h4, h5, h6 {
+	font-weight: 400;
+	line-height: 1.1;
+	margin-bottom: .8em;
+    font-family: 'Yanone Kaffeesatz', arial, serif;
+}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+	
+hr { border: 2px solid #EEEEEE; }
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+	color: #C74350;
+	padding: 0 1px;
+	text-decoration: underline;
+}
+a:hover, a:active {
+	background-color: #C74350;
+	color: #fff;
+	text-decoration: none;
+	text-shadow: 1px 1px 1px #333;
+}
+
+h1 a:hover {
+    background-color: inherit
+}
+	
+/* Paragraphs */
+div.line-block,
+p { margin-top: 1em;
+    margin-bottom: 1em;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+/* Lists */
+ul {
+	list-style: outside disc;
+	margin: 0em 0 0 1.5em;
+}
+
+ol {
+	list-style: outside decimal;
+	margin: 0em 0 0 1.5em;
+}
+
+li { margin-top: 0.5em;}
+
+.post-info {
+    float:right;
+    margin:10px;
+    padding:5px;
+}
+
+.post-info p{
+    margin-top: 1px;
+    margin-bottom: 1px;
+}
+
+.readmore { float: right }
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+pre{background-color:  rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
+
+/* Quotes */
+blockquote {
+    margin: 20px;
+    font-style: italic;
+}
+cite {}
+
+q {}
+
+div.note {
+   float: right;
+   margin: 5px;
+   font-size: 85%;
+   max-width: 300px;
+}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+	
+	/* Thead */
+	thead th {padding: .5em .4em; text-align: left;}
+	thead td {}
+
+	/* Tbody */
+	tbody td {padding: .5em .4em;}
+	tbody th {}
+	
+	tbody .alt td {}
+	tbody .alt th {}
+	
+	/* Tfoot */
+	tfoot th {}
+	tfoot td {}
+	
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+	display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right, figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: left; margin: 0 2em 2em 0;}
+
+/*
+	Header
+*****************/
+#banner {
+	margin: 0 auto;
+	padding: 2.5em 0 0 0;
+}
+
+	/* Banner */
+	#banner h1 {font-size: 3.571em; line-height: 0;}
+	#banner h1 a:link, #banner h1 a:visited {
+		color: #000305;
+		display: block;
+		font-weight: bold;
+		margin: 0 0 .6em .2em;
+		text-decoration: none;
+	}
+	#banner h1 a:hover, #banner h1 a:active {
+		background: none;
+		color: #C74350;
+		text-shadow: none;
+	}
+	
+	#banner h1 strong {font-size: 0.36em; font-weight: normal;}
+	
+	/* Main Nav */
+	#banner nav {
+		background: #000305;
+		font-size: 1.143em;
+		height: 40px;
+		line-height: 30px;
+		margin: 0 auto 2em auto;
+		padding: 0;
+		text-align: center;
+		width: 800px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	
+	#banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+	#banner nav li {float: left; display: inline; margin: 0;}
+	
+	#banner nav a:link, #banner nav a:visited {
+		color: #fff;
+		display: inline-block;
+		height: 30px;
+		padding: 5px 1.5em;
+		text-decoration: none;
+	}
+	#banner nav a:hover, #banner nav a:active,
+	#banner nav .active a:link, #banner nav .active a:visited {
+		background: #C74451;
+		color: #fff;
+		text-shadow: none !important;
+	}
+	
+	#banner nav li:first-child a {
+		border-top-left-radius: 5px;
+		-moz-border-radius-topleft: 5px;
+		-webkit-border-top-left-radius: 5px;
+		
+		border-bottom-left-radius: 5px;
+		-moz-border-radius-bottomleft: 5px;
+		-webkit-border-bottom-left-radius: 5px;
+	}
+
+/*
+	Featured
+*****************/
+#featured {
+	background: #fff;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+#featured figure {
+	border: 2px solid #eee;
+	float: right;
+	margin: 0.786em 2em 0 5em;
+	width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+	Body
+*****************/
+#content {
+	background: #fff;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px 20px;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+/*
+	Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+	color: #C74350;
+	font-size: 1.429em;
+	margin-bottom: .25em;
+	padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+	color: #444;
+	display: block;
+	border-bottom: 1px solid #F4E3E3;
+	text-decoration: none;
+	padding: .3em .25em;
+}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+	/* Blogroll */
+	#extras .blogroll {
+		float: left;
+		width: 615px;
+	}
+	
+	#extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+	
+	/* Social */
+	#extras .social {
+		float: right;
+		width: 175px;
+	}
+	
+	#extras div[class='social'] a {
+		background-repeat: no-repeat;
+		background-position: 3px 6px;
+		padding-left: 25px;
+	}
+	
+		/* Icons */
+		.social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
+		.social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');}
+		.social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+		.social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+		.social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+		.social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');}
+		.social a[href*='github.com'],
+		.social a[href*='git.io'] {background-image: url('../images/icons/github.png');}
+		.social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');}
+		.social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');}
+		.social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');}
+		.social a[href*='news.ycombinator.com'],
+		.social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');}
+		.social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+		.social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');}
+		.social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');}
+		.social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');}
+		.social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');}
+		.social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');}
+		.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+		.social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');}
+		.social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');}
+
+/*
+	About
+*****************/
+#about {
+	background: #fff;
+	font-style: normal;
+	margin-bottom: 2em;
+	overflow: hidden;
+	padding: 20px;
+	text-align: left;
+	width: 760px;
+	
+	border-radius: 10px;
+	-moz-border-radius: 10px;
+	-webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+	Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+	display: block;
+	clear: both;
+	border-bottom: 1px solid #eee;
+	padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+.hentry img{display : none ;}
+.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;}
+.entry-title a:visited {background-color: #fff;}
+
+.hentry .post-info * {font-style: normal;}
+
+	/* Content */
+	.hentry footer {margin-bottom: 2em;}
+	.hentry footer address {display: inline;}
+	#posts-list footer address {display: block;}
+
+	/* Blog Index */
+	#posts-list {list-style: none; margin: 0;}
+	#posts-list .hentry {padding-left: 10px; position: relative;}
+	
+	#posts-list footer {
+		left: 10px;
+		position: relative;
+        float: left;
+		top: 0.5em;
+		width: 190px;
+	}
+	
+	/* About the Author */
+	#about-author {
+		background: #f9f9f9;
+		clear: both;
+		font-style: normal;
+		margin: 2em 0;
+		padding: 10px 20px 15px 20px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	
+	#about-author strong {
+		color: #C64350;
+		clear: both;
+		display: block;
+		font-size: 1.429em;
+	}
+	
+	#about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+	
+	/* Comments */
+	#comments-list {list-style: none; margin: 0 1em;}
+	#comments-list blockquote {
+		background: #f8f8f8;
+		clear: both;
+		font-style: normal;
+		margin: 0;
+		padding: 15px 20px;
+		
+		border-radius: 5px;
+		-moz-border-radius: 5px;
+		-webkit-border-radius: 5px;
+	}
+	#comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+	
+	#comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+	
+	/* Add a Comment */
+	#add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+	#add-comment input[type='text'],
+	#add-comment input[type='email'],
+	#add-comment input[type='url'] {float: left; width: 200px;}
+	
+	#add-comment textarea {float: left; height: 150px; width: 495px;}
+	
+	#add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+	
+	#add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+	#add-comment * {margin-bottom: .5em;}

+ 205 - 0
test_data/themes/notmyidea/static/css/pygment.css

@@ -0,0 +1,205 @@
+.hll {
+background-color:#eee;
+}
+.c {
+color:#408090;
+font-style:italic;
+}
+.err {
+border:1px solid #FF0000;
+}
+.k {
+color:#007020;
+font-weight:bold;
+}
+.o {
+color:#666666;
+}
+.cm {
+color:#408090;
+font-style:italic;
+}
+.cp {
+color:#007020;
+}
+.c1 {
+color:#408090;
+font-style:italic;
+}
+.cs {
+background-color:#FFF0F0;
+color:#408090;
+}
+.gd {
+color:#A00000;
+}
+.ge {
+font-style:italic;
+}
+.gr {
+color:#FF0000;
+}
+.gh {
+color:#000080;
+font-weight:bold;
+}
+.gi {
+color:#00A000;
+}
+.go {
+color:#303030;
+}
+.gp {
+color:#C65D09;
+font-weight:bold;
+}
+.gs {
+font-weight:bold;
+}
+.gu {
+color:#800080;
+font-weight:bold;
+}
+.gt {
+color:#0040D0;
+}
+.kc {
+color:#007020;
+font-weight:bold;
+}
+.kd {
+color:#007020;
+font-weight:bold;
+}
+.kn {
+color:#007020;
+font-weight:bold;
+}
+.kp {
+color:#007020;
+}
+.kr {
+color:#007020;
+font-weight:bold;
+}
+.kt {
+color:#902000;
+}
+.m {
+color:#208050;
+}
+.s {
+color:#4070A0;
+}
+.na {
+color:#4070A0;
+}
+.nb {
+color:#007020;
+}
+.nc {
+color:#0E84B5;
+font-weight:bold;
+}
+.no {
+color:#60ADD5;
+}
+.nd {
+color:#555555;
+font-weight:bold;
+}
+.ni {
+color:#D55537;
+font-weight:bold;
+}
+.ne {
+color:#007020;
+}
+.nf {
+color:#06287E;
+}
+.nl {
+color:#002070;
+font-weight:bold;
+}
+.nn {
+color:#0E84B5;
+font-weight:bold;
+}
+.nt {
+color:#062873;
+font-weight:bold;
+}
+.nv {
+color:#BB60D5;
+}
+.ow {
+color:#007020;
+font-weight:bold;
+}
+.w {
+color:#BBBBBB;
+}
+.mf {
+color:#208050;
+}
+.mh {
+color:#208050;
+}
+.mi {
+color:#208050;
+}
+.mo {
+color:#208050;
+}
+.sb {
+color:#4070A0;
+}
+.sc {
+color:#4070A0;
+}
+.sd {
+color:#4070A0;
+font-style:italic;
+}
+.s2 {
+color:#4070A0;
+}
+.se {
+color:#4070A0;
+font-weight:bold;
+}
+.sh {
+color:#4070A0;
+}
+.si {
+color:#70A0D0;
+font-style:italic;
+}
+.sx {
+color:#C65D09;
+}
+.sr {
+color:#235388;
+}
+.s1 {
+color:#4070A0;
+}
+.ss {
+color:#517918;
+}
+.bp {
+color:#007020;
+}
+.vc {
+color:#BB60D5;
+}
+.vg {
+color:#BB60D5;
+}
+.vi {
+color:#BB60D5;
+}
+.il {
+color:#208050;
+}

+ 52 - 0
test_data/themes/notmyidea/static/css/reset.css

@@ -0,0 +1,52 @@
+/*
+	Name: Reset Stylesheet
+	Description: Resets browser's default CSS
+	Author: Eric Meyer
+	Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+	background: transparent;
+	border: 0;
+	font-size: 100%;
+	margin: 0;
+	outline: 0;
+	padding: 0;
+	vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+	outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}

+ 3 - 0
test_data/themes/notmyidea/static/css/typogrify.css

@@ -0,0 +1,3 @@
+.caps {font-size:.92em;}
+.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;}    
+.dquo {margin-left:-.38em;}

+ 48 - 0
test_data/themes/notmyidea/static/css/wide.css

@@ -0,0 +1,48 @@
+@import url("main.css");
+
+body {
+    font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif;
+}
+
+.post-info{
+    display: none;
+}
+
+#banner nav {
+    display: none;
+    -moz-border-radius: 0px;
+    margin-bottom: 20px;
+    overflow: hidden;
+    font-size: 1em;
+    background: #F5F4EF;
+}
+
+#banner nav ul{
+    padding-right: 50px;
+}
+
+#banner nav li{
+    float: right;
+    color: #000;
+}
+
+#banner nav li a {
+    color: #000;
+}
+
+#banner h1 {
+    margin-bottom: -18px;
+}
+
+#featured, #extras {
+    padding: 50px;
+}
+
+#featured {
+    padding-top: 20px;
+}
+
+#extras {
+    padding-top: 0px;
+    padding-bottom: 0px;
+}

BIN
test_data/themes/notmyidea/static/images/icons/aboutme.png


BIN
test_data/themes/notmyidea/static/images/icons/bitbucket.png


BIN
test_data/themes/notmyidea/static/images/icons/delicious.png


BIN
test_data/themes/notmyidea/static/images/icons/facebook.png


BIN
test_data/themes/notmyidea/static/images/icons/github.png


BIN
test_data/themes/notmyidea/static/images/icons/gitorious.png


BIN
test_data/themes/notmyidea/static/images/icons/gittip.png


BIN
test_data/themes/notmyidea/static/images/icons/google-groups.png


BIN
test_data/themes/notmyidea/static/images/icons/google-plus.png


BIN
test_data/themes/notmyidea/static/images/icons/hackernews.png


BIN
test_data/themes/notmyidea/static/images/icons/lastfm.png


BIN
test_data/themes/notmyidea/static/images/icons/linkedin.png


+ 0 - 0
test_data/themes/notmyidea/static/images/icons/reddit.png


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott