Static Site Generator

Documentation

AnglaisFrançais

Stapy est un générateur de site statique. Il fonctionne avec Python sur n'importe quel système d'exploitation sans paquet supplémentaire.

Télécharger 1.18.1

Cette documentation couvre la version 1.18.1

Une question ? Contactez-nous !

Summary

Compatibilité

Compatible à partir de la version 3.6 de Python, sur n'importe quel système d'exploitation.

Demo

Installation

Téléchargez la dernière version de Stapy puis décompressez l'archive.

tar.gz

stapy-1.18.1.tar.gz

wget https://www.stapy.net/download/stapy-1.18.1.tar.gz
tar zxvf stapy-1.18.1.tar.gz

zip

stapy-1.18.1.zip

wget https://www.stapy.net/download/stapy-1.18.1.zip
unzip stapy-1.18.1.zip

Serveur

Démarrez le serveur HTTP pour commencer à travailler sur le site.

Unix

python3 stapy.py

Accédez ensuite à http://127.0.0.1:1985 dans le navigateur.

Ajoutez un argument pour servir sur un hôte spécifique :

python3 stapy.py 0.0.0.0:8080

Vous pouvez servir sur IPv6 en mettant l'adresse entre crochets:

python3 stapy.py [::]:8080

Note: Cela implique de modifier l'URL dans le fichier source/pages.json.

Astuce: Créez un alias pour démarrer rapidement le serveur pour votre site :

alias stapy='python3 /absolute/path/to/stapy.py'

Windows

Double-cliquez sur le fichier stapy.py (Python est requis, installez-le facilement depuis le Microsoft Store si nécessaire).

Si l'extension py est associé à un éditeur, effectuez un clic droit puis : Ouvrir avec... Python 3.X

Accédez ensuite à http://127.0.0.1:1985 dans le navigateur.

=^..^= Welcome to Stapy 1.18.0
Serving under http://127.0.0.1:1985

Générateur

Lorsque le site est prêt, vous devez le générer pour publication.

Unix

python3 stapy.py build

Par défaut, tous les environnements seront générés. Une liste spécifique d'environnement à générer peut être indiqué en paramètre.

python3 stapy.py build devel prod

Windows

Double-cliquez sur le fichier build.py.

Si l'extension py est associé à un éditeur, effectuez un clic droit puis : Ouvrir avec... Python 3.X

=^..^= Welcome to Stapy 1.18.0
Build in progress...
[prod] 56 pages generated in 0.1456 seconds
[devel] 56 pages generated in 0.1348 seconds

Environnements

Les fichiers statiques sont générés dans le dossier web. Il contient les dossiers correspondant aux différents environnements (devel, prod...).

Pour la production, ajoutez un dossier prod dans le dossier web. Il contiendra toutes les resources que vous devez déployer (html, css, js, images...).

Thème

Le fichier theme.json dans le dossier source indique le thème à appliquer :

{
  "theme": "stapy",
  "parent": "default"
}

Les thèmes sont placés dans le dossier themes à la racine.

Définissez le nom du dossier du thème pour la configuration theme, et un thème parent si nécessaire.

Les fichiers du thème principal surchargent les fichiers du thème parent.

Lorsque le fichier de configuration du thème est modifié, il est nécessaire de redémarrer Stapy.

Ressources

Toutes les ressources nécessaires comme les js, css ou images sont copiées automatiquement à partir des dossiers source/assets et themes/{theme}/assets vers le dossier web pour tous les environnements (exemple : web/prod).

Routes

Pour une page, le serveur recherche un fichier json dans le dossier source/pages. Le nom du fichier json doit être le même que le chemin de l'URL. Exemples :

URL Path Json file
/ index.html.json
/hello.html hello.html.json
/hello/world.html hello/world.html.json
/hello/world/ hello/world/index.html.json

Routes réservées

Route Result (json)
/_pages List all the pages
/_pages/data List all the pages with the data
/_environments List the environments
/_page/hello.html Get the data of the given path
/_content/content/hello.html Get the content of the given file
/_cache/clear Manually clear Json query data cache

Données de la page

Le fichier json contient toutes les données nécessaires à la génération de la page :

{
  "template": "template/default.html",
  "enabled": true,

  "content": "content/index.html",

  "meta_title": "Page meta title",
  "meta_description": "Page meta description",
  "title": "Page title"
}

Les autres clés sont libres, elles sont utilisées dans le template de la page.

Les variables spécifiques à l'environnement sont déclarées avec le suffixe correspondant à l'environnement :

{
  "my_var.local": "This is the local environment",
  "my_var.prod": "This is the production environment"
}

Le suffixe est identique au nom du dossier de l'environnement. Pour le rendu du site sur le serveur local, le suffixe est toujours "local".

Une variable peut présenter une valeur par défaut :

{
  "my_text": "All environments display this text",
  "my_text.prod": "Except the prod with this"
}

Layout

Un fichier nommé html.json dans le dossier themes/{theme}/layout est utilisé pour la configuration par défaut des pages HTML. Il sera mergé avec le fichier json de la page. Le fichier est trés pratique pour appliquer une configuration commune à toutes les pages.

themes/{theme}/layout/html.json

{
  "title": "Default title",
  "template": "template/default.html"
}

source/pages/index.html.json

{
  "title": "Home title",
  "content": "content/index.html"
}

themes/{theme}/layout/html.json + source/pages/index.html.json

{
  "title": "Home title",
  "template": "template/default.html",
  "content": "content/index.html"
}

Vous pouvez ajouter un fichier de configuration par défaut pour toutes les extensions dont vous avez besoin :

Le fichier de configuration common.json est partagé par toutes les pages (toutes les extensions).

Enfin, il est possible d'ajouter un fichier de configuration pour les sous-dossiers d'une page :

JSON Poids Obligatoire
themes/{theme}/layout/common.json 1 N
themes/{theme}/layout/html.json 2 N
themes/{theme}/layout/blog/common.json 3 N
themes/{theme}/layout/blog/html.json 4 N
themes/{theme}/layout/blog/2022/html.json 5 N
source/pages.json 6 N
source/pages/blog/2022/my-first-post.html.json 7 O

Les données Json des fichiers avec un poids plus élevé remplaceront et étendront les données des fichiers de poids inférieur.

Astuce : Ajoutez _page/ devant le chemin pour obtenir les données json de la page.

http://127.0.0.1:1985/_page/index.html
http://127.0.0.1:1985/_page/hello.html

Template

Le fichier template est le squelette de la page :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>{{ meta_title }}</title>
        <meta name="description" content="{{ meta_description }}" />
        <link rel="stylesheet" href="{{ url }}css/style.css" />
    </head>
    <body>
        <header>
            {% block.header %}
        </header>
        <main>
            {% content %}
        </main>
        <footer>
            {% block.footer %}
        </footer>
    </body>
</html>

Le template principal contient des blocs. Le chemin du template des blocs est défini dans les layouts.

Un fichier template du thème peut être surchargé dans le dossier source. Par example, en copiant themes/{theme}/template/block/header.html dans source/template/block/header.html.

Expressions

Note : Échappez le caractère accolade pour ne pas analyser une expression :

\{% block.keep.exp %\}

Bloc enfant

Afficher le template d'un bloc enfant avec l'expression {% %} :

{
  "block.post": "template/block/post.html"
}
{% block.post %}

Ajoutez des arguments au bloc :

{% block.post firstname:"John" lastname:"Doe" %}

Les arguments seront accessibles dans le template "block.post" avec le signe $ devant la variable :

Hello {{ $firstname }} {{ $lastname }}!

Utilisez la configuration d'un json spécifique dans le contenu enfant avec le séparateur + (les espaces sont obligatoires) :

{% block.post + my-first-post.html %}

Les données du fichier json/my-first-post.html.json seront accessibles dans le template "block.post" avec le signe $ devant la variable :

<a href="{{ url }}{{ $_path }}">{{ $post_title }}</a>

Pour parcourir les données de plusieurs json avec une requête, utilisez ~ comme séparateur (les espaces sont obligatoires):

{% block.post ~ SELECT ITEMS {start}-{end} WHERE {conditions} ORDER BY {key} {dir} %}

Exemple:

{% block.post ~ SELECT ITEMS 1-10 WHERE "post" in tags AND published = 1 ORDER BY date desc %}

Cette requête récupère les pages 1 à 10, avec la valeur post contenue dans tags et published à 1, triées par date. Les variables tags, published et date doivent figurer dans les données json des pages :

{
  "date": "2022-01-01",
  "published": 1,
  "tags": ["post"]
}

Les données du json seront accessibles dans le template "block.post" avec le signe $ devant la variable.

Ajoutez un séparateur entre les blocs grâce au paramètre delimiter :

{% block.post delimiter:"<br />" ~ SELECT ITEMS 1-10 WHERE "post" in tags AND published = 1 ORDER BY date desc %}

Notes :

Le type de la valeur doit correspondre au type dans les données JSON :

{
  "published": 1
}
{
  "published": "1"
}

Les expressions sur plusieurs lignes sont autorisées :

{% block.post
    delimiter:"<br />"
    ~ SELECT ITEMS 1-10 WHERE "post" in tags AND published = 1 ORDER BY date desc
%}

Un bloc appelé recursivement dans le même bloc n'engendre pas une boucle infinie. Le bloc enfant est ignoré.

Variables réservées

Liste

Variable Description Exemple
_path Chemin de page nettoyé blog/
_full_path Chemin de page complet blog/index.html
_env Nom de l'environement prod

Exemples

{{ url }}{{ _path }}
<!-- https://www.example.com/blog/ -->
{{ url }}{{ _full_path }}
<!-- https://www.example.com/blog/index.html -->
<script type="text/javascript" src="{{ url }}js/init.{{ _env }}.js"></script>
<!-- https://www.example.com/js/init.prod.js -->

Plugins

Un plugin permet d'écrire du code personnalisé pour interférer avec le rendu de la page.

Method Description Method argument 1 Method argument 2 (dict) Return
file_content_opened Update any file content (html, json, md, css, js...) File content (str or bytes) {path, mode, _stapy} str or bytes
page_data_merged Update the current page json data Page data (dict) {path, _stapy} dict
before_content_parsed Update the page template content before parsing Page content (str) {data, env, path, _stapy} str
after_content_parsed Update the page template content after parsing Page content (str) {data, env, path, _stapy} str
child_content_data Update child data before content generation Child data (dict) {key, env, path, data, _stapy} dict
child_content_query_result Update data result before content generation All child data (list) {key, env, path, data, _stapy} list
custom_http_response Send custom response on a request Response result (tuple or None) {path, request, _stapy} tuple or None
http_request_initialized Execute an action when HTTP request is initialized Current page path {_stapy} None
http_request_sent Execute an action when HTTP request was sent Current page path {_stapy} None
? A free named method called with {: :} syntax Page data (dict) {env, path, _stapy, ?} str

Un plugin est un script Python dans le dossier plugins, ou script nommé main.py dans un dossier :

La clé _stapy de l'argument 2 contient les objets StapyFileSystem, StapyJsonQuery, StapyParser, StapyGenerator et StapyPlugins.

Exemple

Pour convertir du Markdown vers du HTML, ajoutez un script mdtohtml.py dans le dossier plugins avec le code suivant :

import markdown


def file_content_opened(content: str, args: dict) -> str:
    if args['path'].endswith('.md'):
        return markdown.markdown(content)
    return content

pip3 install markdown

Vous pouvez maintenant écrire des contenus en Markdown :

Hello World!

[Back to home]({{ url }})

Méthode personnalisée

Dans n'importe quel fichier du template, vous pouvez appeler la méthode d'un plugin avec la syntaxe {: :}.

Recherche de la méthode dans tous les plugins :

{: my_plugin_method :}

Exécuter la méthode dans un plugin spécifique :

{: custom.my_plugin_method :}

Le nom du plugin correspond au nom du fichier sans l'extension .py, ou le nom du dossier contenant le script.

custom.my_plugin_method retourne le résultat de la méthode my_plugin_method du script plugins/custom.py ou plugins/custom/main.py.

# plugins/custom.py

def my_plugin_method(data: dict, args: dict) -> str:
    return '<strong>Hello World!</strong>'

Ajoutez des arguments à la méthode :

{: custom.my_plugin_method firstname:"John" lastname:"Doe" :}
# plugins/custom.py

def my_plugin_method(data: dict, args: dict) -> str:
    return '<strong>Hello ' + args['firstname'] + ' ' + args['lastname'] + '</strong>'

Les arguments de la méthode sont optionnels lorsque vous n'en avez pas besoin :

# plugins/custom.py

def my_plugin_method() -> str:
    return '<strong>Hello World!</strong>'

Méthodes protégées

Toutes les méthodes du plugin autres que la méthode appelée dans le template doivent être préfixées par un underscode (tiret du bas). Elles ne pourront jamais être utilisées en dehors du script.

# plugins/custom.py

def custom_plugin_method(data: dict, args: dict) -> str:
    return _my_method(data)

def _my_method(data: dict) -> str:
    return '<strong>Hello World!</strong>!'

{: custom_plugin_method :} <!-- OK -->
{: _my_method :} <!-- Forbidden -->

Déploiement

Tous les fichiers de l'environnement de production (exemple : web/prod) doivent être exposés.

Mode sécurité

Stapy peut être hébergé sur un serveur distant (en proxy avec Apache) et utilisé par plusieurs utilisateurs pour le travail partagé.

Dans ce cas, nous recommandons de protéger l'éditeur avec "htpasswd" et d'utiliser Stapy en mode sécurité pour désactiver les routes réservées et cacher les messages d'erreur (les messages d'erreur contiennent le chemin absolu des fichiers).

Pour activer le mode sécurité, ajoutez un fichier secure à la racine de Stapy :

touch secure

Bug Tracking

Pour signaler un problème, ajoutez un nouveau ticket :

Bug Tracking