2>&1

"pelican" on https://aligot-death.space, available at https://aligot-death.space/wiki/pelican-en

wiki/webdev CMS static site generator python

pelican

A few tips and notes on the static site generator pelican

Read it aloud: play pause stop

Pelican is a static site generator in python which is not necessarily beautiful out of the box, but offers a lot of control on the generation process.

Content

More practical work environment#

Add to ~/.bashrc the following aliases:

1 alias pc="pelican content"
2 alias pl="pelican --listen"

simply build the website py typing pc from the directory, and pl to launch Pelican’s web server.

The website can be build without shutting down the server, so you can simply launch the pl command in another terminal (or typically, in another tmux pane) and build the website on the fly.

Writing#

This part assumes that you use RestructuredText format for your articles.

Easy styling#

to add a class to a paragraph, use (be careful with the whitespaces to avoid errors):

1 .. class:: warning
2 
3 This part assumes that you use RestructuredText format for your articles.

Example: the warning just above (assuming you've created the corresponding CSS class)!

Multiple ..class can be nested, as long as the empty lines are respected. For instance:

1 .. class:: align-right
2 
3 .. class:: side-note
4 
5 **Le Règlement Général sur la Protection des Données (RGPD) c'est quoi ?**

using html within a .rst file#

Use:

1 .. raw:: html
2 
3    <details>
4    <summary><b>Here is a summary.</b><br>
5    this is a summary that can be clicked to view/hide its details </summary>
6    Even though there is not much to see here.
7    </details>

Result:

Here is a summary.
this is a summary that can be clicked to view/hide its details
Even though there is not much to see here.

Configuration#

clean URLs#

In the pelicanconf.py file, set the following variables:

TAGS_URL = "tags"
CATEGORIES_URL = "categories"
ARCHIVES_URL = "archives"

ARTICLE_URL = "{slug}"
ARTICLE_LANG_URL = '{slug}-{lang}'

PAGE_URL = "{slug}"
PAGE_SAVE_AS = "{slug}.html"
PAGE_LANG_URL = 'pages/{slug}-{lang}'

CATEGORY_SAVE_AS = 'categories/{slug}.html'
CATEGORY_URL = 'categories/{slug}'
TAG_SAVE_AS = 'tags/{slug}.html'
TAG_URL = 'tags/{slug}'

The result will be urls of the form example.com/making-soup or example.com/categories/linux.

To ensure that these links are used, you also need to configure the web server. In the case of nginx:

location = / {
  index index.html;
}

location / {
    root /var/www/example.com/;
    try_files $uri.html $uri =404;
}

Theming#

Using article "summary"#

Use the striptags filter to avoid having <p class = "first"> garbage in your meta description:

1 {% if  article.summary  %}
2 <meta property="og:description" content="{{ article.summary|striptags|safe  }}">
3 {% endif %}

Article ordering#

By default articles are ordered in "reverse-date". But any metadata (with a "reverse-" prefix to... reverse) can be used: for instance, this wiki uses "title", like this in pelicanconf.py:

1 ARTICLE_ORDER_BY = "title"

See Ordering content for more details.

Translation switch for custom pages#

To get translation links for custom page (:save_as: and :template:), edit the translations.html from the theme and the following new macro:

 1 {% macro translations_for_page(page) %}
 2 <div>
 3 {% if page.translations %}
 4 <span class = "block">{{ page.lang }}</span>
 5 {% for translation in page.translations %}
 6 <a class = "inverted-block" href="{{ SITEURL }}/{{ translation.url }}" hreflang="{{ translation.lang }}">{{ translation.lang }}</a>
 7 {% endfor %}
 8 {% endif %}
 9 </div>
10 {% endmacro %}

This new macro is called translation for page. And then call it using the following jinja code (or copy-paste it from another template and adapt it):

1 {% import 'translations.html' as translations with context %}
2 {% if translations.translations_for_page(page) %}
3   {{ translations.translations_for_page(page) }}
4 {% endif %}

For good mesure, here is the content of the file content/pages/index_wiki-fr.rst, which corresponds to the french homepage of this wiki:

 1 Wiki
 2 ########
 3 
 4 :date: 2021-09-29
 5 :template: custom/index_wiki
 6 :save_as: wiki/index-fr.html
 7 :slug: wiki/index
 8 :category: wiki
 9 :lang: fr
10 :status: published

Custom home page#

To write properly

Do not modify the "index.html" file hoping to change the homepage: this file is also used for the category, tags and archives pages, which will probably be messed up if you change it. Instead, create a custom template.

follow https://stackoverflow.com/questions/55363180/how-do-i-choose-a-category-page-to-be-the-home-page-for-a-pelican-site /!template "homepage" must be in content/templates and exclude it, as weird as it is then, https://stackoverflow.com/questions/19283880/querying-for-specific-articles-via-tag-category-in-pelican-themes

Navigation menu with article list#

<-- As an example, this wiki uses this.

Both the default (and most popular) theme "notmyidea" and the basic boiletplate theme "simple" gives a confusing example: the way they iterate on categories is:

1 {% for cat, null in categories %}
2 <li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li>
3 {% endfor %}

It would be tempting to add a loop under it iterating every articles and checking if the current category being processed is the same, but as it turns out the return value being negated by the "null" is the list of the articles in the given category. So simply do:

1 {% for cat, category_articles in categories %}
2   <li><a class = "category" href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a>
3     <ul>
4         {% for artic in category_articles %}<li><a href = "{{ artic.url }}">{{ artic.title }}</a></li>
5         {% endfor %}
6     </ul></li>
7 {% endfor %}

Articles listed per year#

A common way to list articles on personal websites nowaday is to list them by year like so:

2021

  • I saw a nice rock yesterday

2020

  • I'm giving up on that blog
  • More articles to come this year!

2019

  • A long essay on consciousness
  • New blog!

To do so in pelican, here is a skeleton template to use within templates.index.html:

 1 <ul class = "article-list">
 2 {% for year, year_posts in articles_page.object_list|groupby('date.year')|reverse %}
 3   <h2>{{year}}</h2>
 4       {% for article in year_posts %}
 5           <li>
 6             <article>
 7               <header>
 8                 <h2>
 9                   <a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">
10                     {{ article.title }}
11                   </a>
12                 </h2>
13               </header>
14             </article>
15           </li>
16       {% endfor %}
17   {% endfor %}
18 </ul>

Explanation: the heavylifting is done by the articles_page.object_list|groupby('date.year')|reverse which groups articles per year with the newest ones at the top. The resulting list is unpacked as the year + the list of articles for that year, the latter being iterated on by a second loop.

List x articles#

If for instance you only want to list the first three articles of a category, you can use the counter tag like so (in this example, 3 articles will be listed):
1   <div class = "image-gallery">
2     {% set counter = namespace(value=0) %}{% for article in articles if ("miscellaneous_digital_drawings" in article.subcategories and counter.value < 3) %}<a href = "{{ SITEURL }}/{{ article.url }}"><img src="{{ article.cover_image }}" alt="{{ counter.value }}"></a>{% set counter.value = counter.value + 1 %}
3     {% endfor %}
4   </div>

Must have plugins#

Refer yourself to the `pelican-plugins<https://github.com/getpelican/pelican-plugins/>`__ repository for more informations

headerid: adds relative links to section headers

Troubleshooting#