Documentation
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.
Cette documentation couvre la version 1.18.3
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
wget https://www.stapy.net/download/stapy-1.18.3.tar.gz
tar zxvf stapy-1.18.3.tar.gz
zip
wget https://www.stapy.net/download/stapy-1.18.3.zip
unzip stapy-1.18.3.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.3
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.3
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 :
- source/theme.json
{
"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"
}
- La clé template indique le fichier HTML à générer pour la page. Si elle n'est pas définie ce sera par défaut un modèle vierge avec uniquement le bloc "content".
- La clé enabled indique si la page doit être créée lors de la génération. Par défault, si la valeur n'est pas définie, la page est considérée comme active.
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 :
- themes/{theme}/layout/xml.json
- themes/{theme}/layout/txt.json
- ...
Le fichier de configuration common.json
est partagé par toutes les pages (toutes les extensions).
- themes/{theme}/layout/common.json
Enfin, il est possible d'ajouter un fichier de configuration pour les sous-dossiers d'une page :
- themes/{theme}/layout/blog/html.json
- themes/{theme}/layout/blog/2022/html.json
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
- Toutes les variables entre les doubles accolades {{ }} seront remplacées par le texte déclaré dans le fichier json.
- Toutes les variables entre les simples accolades et pourcent {% %} seront remplacées par le contenu du fichier déclaré dans le fichier json.
- Le nom des méthodes entre les simples accolades et deux-points {: :} seront remplacées par le retour de la méthode dans un plugin.
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 (int) is True
- published = "1" (str) is False
{
"published": "1"
}
- published = 1 (int) is False
- published = "1" (str) is True
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 |
_identifier | Page identifier | 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 -->
<body class="{{ _identifier }}"></body>
<!-- <body class="blog-index-html"></body> -->
<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 :
- plugins/helloworld.py
- plugins/helloworld/main.py
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.
- Avec un hébergement classique, vous pouvez envoyer le contenu du dossier
web/prod
par SFTP. - Avec un outil de déploiement automatisé comme Netlify, Cloudflare Pages ou Vercel, vous devez créer un dépôt sur GitHub et versionner les fichiers (uniquement le contenu du dossier
web/prod
ou tous les fichiers du site Stapy en configurant l'outil pour déployer le contenu du dossierweb/prod
). - Avec GitHub Pages vous pouvez utiliser GitHub Actions pour générer et déployer le site (versionnez les fichiers du site sans le dossier
web
et ajoutez des étapes au flux pour créer le dossierweb/prod
et générer le site).
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 :