This commit is contained in:
Cyrille L 2023-12-28 00:31:19 +01:00
parent 03960f733b
commit 7849ca4f4b
44 changed files with 5592 additions and 4954 deletions

View File

@ -10,213 +10,5 @@ Tyto - Littérateur
# CURRENTLY IN DEV (in devel branch) ! # CURRENTLY IN DEV (in devel branch) !
## [1.9.39] ## [1.9.50]
- fix datepub in modules when "check domain" - Complete new code
- add Tyto - Littérateur generator links at HTML footer creation
- fix settings from command line options
- add "RSS" and "sitemap" links to default footer (not yet processed)
- add command "set 'MODULE_NAME'" to create default module (header, footer...)
- Create again all HTML modules with option -F, --force
- With option -E, --errors: only show warning and error logs
- Avoid creating a domain in a directory domain set yet
- better modules managed in "wip" process
- Added logs file (include ALL logs) in /var/log/tyto
- - current.log for current session only
- - archive.log for all sessions
- Sitemap (! In dev)
- - Create sitemaps in root and sub-directories
## [1.9.38]
- Moved: HTML footer in div site_container
- fix: main title in HTML page h1
- Fill Article HTML metas datas
- Working on files copies to wip server with "wip" command
- - some changes in post DB and check process
## [1.9.37]
- Preparing full HTML Page
- Some fixes
- Added web target (without index.html) in section "FILE" in post DB
- Added article footer contents
## [1.9.36]
- fix translations modules
- added metas module
- added command "show metas" to see those already set in page with post
## [1.9.35]
- Working on creating modules
- - Todo last: metas
- check
- - target: domain, wip, www to check directories and files
- - - domain: create .raw modules and wip modules (.html)
- wip
- - html values are now in wip.py
- fixes and more...
## [1.9.34]
- Working on creating modules
- - fixes typos codes
- - header, navbar, sidebar = ok
- - toto footer contents and testing things
- - Manage modules with an ini db file to avoid creating HTML updated modules
## [1.9.33]
- Working on creating modules (header, navbar, sidebar, header)
- - Added working header module
- - Finishing navbar module...
- - Todo Next: sidebar and footer modules
## [1.9.32]
- Working on creating modules (header, navbar, sidebar, header)
## [1.9.31]
- Fix replacing markers starting LINE with HTML
- Added tpl_files directory
- - addes styles.css (empty with classes used)
## [1.9.30]
- 'wip' process
- - About all markers done !
## [1.9.29]
- Translations
- - added for logs (english)
- - updated french logs
- wip (working on...)
- - added modules conversions (source to HTML)
- check
- - some fixes and updated code
- tools
- - check css content
- readme : updated article example with comments
## [1.9.28]
- readme
- - updated tyto article, with comments to show how to
- multiple targets
- - fix CSS classe name error
- - reset stats, post.error, HEADERS datas
- check process:
- - Added hr and br tags (CSS class name check)
- - fix blockquote title HTML
- - New method to split separator in article
- - fix icode, bcode contents HTML signs (converted)
## [1.9.27]
- fix when target article .tyto is missing
- Nearly all stats are added in DB from modules
- Only used tags and stats (or nearly) are added to DB
- cleaner code
- check:
- - added words_tags (strong, italics...)
- - "all" target now ready
## [1.9.26]
- user can indent titles in text
- prepared words markers (strong, bolds) (some to add soon)
## [1.9.25]
- fix typo when creating HTML list
- new anchors process (HTML prepared at 'check')
## [1.9.24]
- new list process (HTML prepared at 'check')
## [1.9.23]
- new quote process (HTML prepared at 'check')
## [1.9.22]
- new bcode process (html prepared for wip)
- new post database management values
- new icode process (html prepared for wip)
- bcodes and icodes are FIRST processed in text article
- generic check fonction for bcodes, quotes, lists, paragraphs
- lots more
## [1.9.21]
- new indentation (3 spaces)
- added 'raw:' marker
- (for wip process):
- - added html titles to post database
- - added html comments to post database (default: ';; a comment')
- - added val3 tag as html comment to content, and convert content to base64
- - - added values to post database
- cleaner code
## [1.9.20]
- working on 'check' process
- - updated 'logo:' process
- - added 'abbr:' process
## [1.9.19]
- working on 'check' process
- - Added post 'logo: URI' (for social network share + opt show on page)
## [1.9.18]
- working on 'check' process
- - added image: tag
- - added value 2 (tag line 2) uri check (generic, root, post)
- - some corrections for domain config file
- - prepared html values for wip
## [1.9.17]
- working on 'check' process
- - dev on checking file used in some post header tags
## [1.9.16]
- working on 'check' process
- - dev: post database values (lots are missing)
- - added links, files supports
- - testing some new markers
## [1.9.15]
- Added 'check' process for bcodes, quotes and paragraphs + stats
- - Their contents must be indented
- - replace with empty lines for bcodes and quotes
## [1.9.14]
- added 'check' process for block-codes
## [1.9.13]
- Check: One-Line needed tags, titles
- Added first stats
- move ini template from tyto to domain for domain config file
- Check valid date in article and form domain date
## [1.9.12]
- preparing check process : head tags in article
## [1.9.11]
- Preparing for multi-targets with "all"
- check if article is a .tyto format
- preparing values for articles
## [1.9.10]
- cleaner code
- start/stop domain by user
- User directories check/create when domain activated
- Check install configuration
## [1.9.9]
- cleaner code with new check/update domain process
## [1.9.8]
- Check/create/update a domain is ready
## [1.9.7]
- new process to check and update configuration file (bug at write some values)
## [1.9.6]
- Better management to create/update domain
## [1.9.5]
- Preparing post database
## [1.9.4]
- Added start/strop action to activate (or deactivate) domain
- Directories creation for user working domain
- better log management for user

View File

@ -629,7 +629,7 @@ 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 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. the "copyright" line and a pointer to where the full notice is found.
GSL Statique Littérateur Tyto - Littérateur
Copyright (C) 2022 Libre en Communs / Commissions / Infrastructure Copyright (C) 2022 Libre en Communs / Commissions / Infrastructure
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify

495
README.md
View File

@ -1,372 +1,195 @@
# Very early IN dev code # Very early IN dev code
This program can ve tested but not at all usable. Lots of work to do... This program can ve tested and should mainly work. Please report any problems
Tyto - Littérateur is translated in french and english.
# Commands # Commands
``` ```
# Get commands list help # Get commands help
tyto tyto
# Get all documentation
tyto help all
# How to write words tags (strong...) and anchors
tyto help words anchor
``` ```
# Create new domain # Create new domain
- create a domain directory, like www.domain.tld - create a domain directory, like www.domain.tld
- Go to this directory - Go to this directory
- type `tyto new domain` - type `tyto new domain`
- type `tyto check domain` - type `tyto check domain`
- create in new directory domain ".../articles/", an article (see tuto below) - create in new directory domain ".../articles/", an article (see help)
- type `check myfile.tyto` (or use "wip" instead of check) - type `wip myfile.tyto`
## Working on
- 'wip' action processes
- Create HTML full page from article
- Create/Manage modules (metas, header, navbar, sidebar, footer)
## ToDo
- thinking about creating an auto top article menu from titles
- create full HTML page
- sitemaps
- RSS
- 'publish' process
## Exemple d'article .tyto commenté ## Exemple d'article .tyto commenté
``` ```
#================================================# title: Tests
# Entete de l'article # about: Tests divers
# Fin de l'entête avec au moins 5 tirets "-----" # date: 2023-02-28
# Toute ligne de commentaire "# ..." est ignorée # tags: tests
# ! Recommandé de ne pas utiliser le signe "_" # authors: echolib
#================================================#
# Pour ne pas inclure cet article dans les sitemaps : abbr: CSS
! NoSitemap Cascading SteelSheet
en
# Données uniques sur UNE ligne link: le site est prêt
title: tests d'un article https://forge.a-lec.org
about: À propos de cet article de test La forge libre
tags: Tyto, tuto,
author: echolib
date: 2023-10-27
# Données unique sur UNE ligne optionnelle
# Si non définit, le logo du domaine est utilisé
# Ne sera affiché qu'avec _image:logo
logo: logo.png
# Données multiples sur 3 lignes
# [TAG]: Nom
# LIEN
# Text alternatif
# Reprendre dans l'article : __cliquer sur ce lien
link: cliquer sur ce Lien
https://
Text alternatif
link: Réservez ici
https://
Billets
# Reprendre dans l'article : --télécharger ce fichier
file: télécharger ce fichier
@/PDFs/hello.pdf
Un PDF !
# Reprendre dans l'article : _code:codetest
code: codetest
@RAWS/test.py
Exemple d'un code Python
# Reprendre dans l'article : _image:mypic
image: mypic
@hello.png
Text Alt
# Les abréviations :
# 2eme ligne: Texte alternatif
# 3ème ligne: valeur affichée dans l'article à la place du Nom
abbr: HTML
HuperText Markup Langage
HTML
# Reprendre dans l'article : ;;css
abbr: css
Cascading Stylesheet
CSS
----- -----
#=====================================================================# #1 Titre 1
# Contenu de l'article # ((
# Les classe optionnelles non renseignées deviennent celle du domaine # Un peu de ::CSS et ::le site est prêt
# /!\ Tout code HTML sera interprêté par le navigateur sauf si placé # (( note
# entre les marqueurs de block-code ou icode # Cet article est un test
#=====================================================================#
# La ligne suivante est un commentaire HTML "<!-- Commentaire -->"
# ----------------------------------------------------------------
;; Commentaire
# Ceci est une ancre avec l'ID uniq1 (ID unique)
# ----------------------------------------------
-> uniq1
-> top
# Créer un lien vers l'ancre "uniq1"
# ----------------------------------
>_uniq1: Go to uniq1 anchor_<
# Ceci est une ligne <hr> ayant pour classe hrcss
# -----------------------------------------------
-- hrcss
# Les Titres de l'article de #1 à #5 (<h2> à <h6>)
# ------------------------------------------------
#1 Titre en h2
# Contenu dans un paragraphe entre (( ... )) ayant pour classe "mypar"
# --------------------------------------------------------------------
(( mypar
Un long paragraphe...
# Retour à la ligne avec un <br /> de classe brcss
# (les retours à la ligne vides ne sont pas pris en compte)
# ---------------------------------------------------------
| brcss
# Reprise du Nom pour les liens de link: et file:
# Reprise des abréviations
Il faut __cliquer sur ce Lien, __Réservez ici et --télécharger ce fichier
ou encore faire une ::css pour du beau rendu ::HTML
# Un paragraphe dans le paragraphe ayant pour classe, celle du domaine
# --------------------------------------------------------------------
((
Un /_court_/ paragraphe de :_1984_: pour de ~_vrai_~ +_faux en gras_+
Il faut le ._souligner_. et *_Très Gras_*
)) ))
# Créer un icode (utilise la balise <code> HTML)
# /!\ ! Doit être sur une même ligne
# ----------------------------------------------
{_<ol>, <ul>_}
{_{_Afficher un icode brut_}_}
)) ))
# Un exemple de block code ayant pour classe python #2 Citation
# Tout contenu entre les marqueurs "{{" et "}}" est conservé ("
# ---------------------------------------------------------- cite: Auteur
#2 Block Code date: AAAA-MM-JJ
{{ python book: Nom du livre
# Un commentaire et du code lang: fr
def hello(world): link: https://...
world and print(world) or print("NoMore")
((
Citation complète dans un paragraphe
))
)"
#2 Code (bloc)
{{
# Écrire les marqueurs de mots
# Chaque marqueur à la classe CSS de la configuration
# Astuce : ** + ← + `` + ← + très gras
*`très gras`* => <strong>
+`gras`+ => <b>
/`italique`/ => <em>
;`italique`; => <i>
_`souligné`_ => <u>
~`effacé`~ => <del>
[`cité`] => <q> # Contenu
:`cité`: => <cite> # auteur, nom
|`perso`| => <span>
# Code dans un texte
# ! Les marqueurs d'ouverture et de fermeture de code sont sur la MEME LIGNE
{` <li>Une entée de liste</li> `} => <code>
# ! Dans certains cas, il faut ajouter un espace après le 1er marqueur
# et/ou avant le second. Ils seront automatiquement supprimés
*`DOMAIN/articles/ `* # évite /` : marqueur italique ouvert
}} }}
#2 Une liste
# Liste. Classe CSS possible (défaut : celle dans la configuration)
# Une entrée de liste peut être ordonnée avec le signe "+" ou non avec "="
# Une liste peut contenir des entrées mixées ("+" et "=")
# mais au changement de signe, ajouter un signe !
# Possible d'écrire une entrée sur plusieurs lignes
# Écrire en gras, italique... (=
# --------------------------- = Première entrée non ordonnée (ul)
#2 Marqueurs de mots == Sous entrée non ordonnée
(( +++ Première sous-sous entrée ordonnée (ol)
Même si, il est possible d'écrire directement des balises (HTML), Tyto +++ Seconde sous-sous entrée ordonnée
propose de les simplifier, en entourant les mots avec des marqueurs. La = Seconde entrée non ordonnée
classe CSS du domaine est utilisée pour chaque marqueur. = Troisième entrée ...
... non ordonnée
{{ )=
*_Très Gras_* > <strong>
+_En Gras_+ > <b>
[_Citer un texte_] > <q>
:_Citer une référence_: > <cite>
~_Texte barré_~ > <del>
._Text souligné_. > <u>
/_En italique_/ > <em>
;_En italique_; > <i>
# Marques multiples, ajouter "&"
*_&._Très gras et souligné_.&_*
}}
))
# Dans un block div [[ ... ]] (classe CSS mydiv),
# La citation entre [" ... "] (classe CSS mycite)
# est placée dans un paragraphe (classe CSS mycite)
# ! Tout commentaire "# ..." dans la citation sera affiché...
# -----------------------------------------------------------
#2 Citation
[[ mydiv
[" mycite
;; A great quote here !
cite: Someone
date: 2023-10-13
book: A History
lang: en
link: https://...
(( mycite
Here, i am
))
"]
]]
# Créer une liste ol/ul entre <: ... :> (classe mylist)
# dans un paragraphe (classe du domaine)
# "+" pour ol, "=" pour ul
# Ajouter toujours un signe pour un sous-item ou /!\ au changement de signe
# -------------------------------------------------------------------------
#2 Une liste mixée {_<ol>, <ul>_}
((
<: mylist
+ numeric ol item 1
++ numeric ol sub-Item 1
+++ numeric ol sub-sub-item 1
==== ul item >_top: Go to Top_<
==== ul item >_top: Another anchor_<
:>
))
# Afficher un block code avec le contenu d'un fichier
# Il doit avoir été configuré dans l'entête
# -----------------------------------------
#2 Un block code depuis un fichier
_code:codetest
# Afficher une image (1 tag par ligne)
# ------------------------------------
#2 Les images
# Placer le logo de l'article
_image:logo
# les options du marqueur:
# - c=CLASS < Sinon la classe est celle du domaine
# - w=WIDTH < longueur (si pas d'unité : défaut "px")
# - h=HEIGHT < Comme w=
# - f=Ma légende sous l'image (ajoute <figure><figcaption>)
# - - Recommandé d'utiliser cette option en dernier
# ! Les images dans cet exemple sont affichées horizontalement
# Placer "|", ou mettez chaque image dans un paragraphe "((...))"
# ou définir un style css de type display:block pour les afficher verticalement
((
_image:mypic
_image:mypic c=MYCSS
))
# Une image avec légende (<figure>) (jamais dans un paragraphe)
_image:mypic c=PIC w=60em h=30% f=echolib sur une chaise
``` ```
## Output HTML ## Output HTML
``` ```
<!-- Article Text: [tests d'un article] --> <body>
<!-- Commentaire --> <div id="site_container">
<a id="uniq1" class="anchor_target"></a>
<a id="top" class="anchor_target"></a>
<a class="tyto anchor_link" href="#uniq1">Go to uniq1 anchor</a>
<hr class="tyto">
<h2 class="tyto">Titre en h2</h2>
<p class="mypar">
Un long paragraphe...
<br class="brcss">
Il faut <a href="https://" class="tyto link" title="Text alternatif">cliquer sur ce Lien</a>, <a href="https://" class="tyto link" title="Billets">Réservez ici</a> et <a href="/files/PDFs/hello.pdf" class="tyto file" title="Un PDF !">télécharger ce fichier</a>
ou encore faire une <abbr class="tyto" title="Cascading Stylesheet">CSS</abbr> pour du beau rendu <abbr class="tyto" title="HuperText Markup Langage">HTML</abbr>
<p class="tyto">
Un <em class="tyto">court</em> paragraphe de <cite class="tyto">1984</cite> pour de <del class="tyto">vrai</del> <b class="tyto">faux en gras</b>
Il faut le <u class="tyto">souligner</u> et <strong class="tyto">Très Gras</strong>
</p>
<code class="tyto icode">&lt;ol&gt;, &lt;ul&gt;</code>
<code class="tyto icode">{_Afficher un icode brut_}</code>
</p>
<h3 class="tyto">Block Code</h3>
<code class="python bcode">
<p class="bcode"><span class="ln bcode">1</span><span class="line bcode"># Un commentaire et du code</span></p>
<p class="bcode"><span class="ln bcode">2</span><span class="line bcode">def hello(world):</span></p>
<p class="bcode"><span class="ln bcode">3</span><span class="line bcode"> world and print(world) or print(&quot;NoMore&quot;)</span></p>
</code>
<h3 class="tyto">Marqueurs de mots</h3>
<p class="tyto">
Même si, il est possible d'écrire directement des balises (HTML), Tyto
propose de les simplifier, en entourant les mots avec des marqueurs. La
classe CSS du domaine est utilisée pour chaque marqueur.
<code class="tyto bcode">
<p class="bcode"><span class="ln bcode">1</span><span class="line bcode">*_Très Gras_* &gt; &lt;strong&gt;</span></p>
<p class="bcode"><span class="ln bcode">2</span><span class="line bcode">+_En Gras_+ &gt; &lt;b&gt;</span></p>
<p class="bcode"><span class="ln bcode">3</span><span class="line bcode">[_Citer un texte_] &gt; &lt;q&gt;</span></p>
<p class="bcode"><span class="ln bcode">4</span><span class="line bcode">:_Citer une référence_: &gt; &lt;cite&gt;</span></p>
<p class="bcode"><span class="ln bcode">5</span><span class="line bcode">~_Texte barré_~ &gt; &lt;del&gt;</span></p>
<p class="bcode"><span class="ln bcode">6</span><span class="line bcode">._Text souligné_. &gt; &lt;u&gt;</span></p>
<p class="bcode"><span class="ln bcode">7</span><span class="line bcode">/_En italique_/ &gt; &lt;em&gt;</span></p>
<p class="bcode"><span class="ln bcode">8</span><span class="line bcode">;_En italique_; &gt; &lt;i&gt;</span></p>
<p class="bcode"><span class="ln bcode">9</span><span class="line bcode"></span></p>
<p class="bcode"><span class="ln bcode">10</span><span class="line bcode"># Marques multiples, ajouter &quot;&&quot;</span></p>
<p class="bcode"><span class="ln bcode">11</span><span class="line bcode">*_&._Très gras et souligné_.&_*</span></p>
</code>
</p>
<h3 class="tyto">Citation</h3>
<div class="mydiv">
<blockquote class="mycite" cite="https://..." lang="en" title="-- Someone - A History (2023-10-13)">
<time datetime="2023-10-13">
<!-- Quote -->
<!-- A great quote here ! -->
<p class="mycite"> <!--# include virtual="/template/header.html"-->
Here, i am <!--# include virtual="/template/navbar.html"-->
</p>
</time> <main id="article_sidebar"> <!-- Contains <article> and <aside> -->
<footer class="quote">
<p class="quote"><a class="tyto quote" href="https://..." target="_blank">-- Someone - <cite class="quote"> - A History</cite> (2023-10-13)</a></p> <article id="article">
</footer> <time datetime="2023-12-27 17:45:57">
</blockquote> <header id="article_header">
<h1 id="article_title">
<a id="article_title_link" href="https://tyto.echolib.re/test.html" title="Tests -- echolib, 28/02/2023">Tests</a>
</h1>
<div id="article_refs">
echolib, 28/02/2023 [<a id="post_code" class="tyto" href="./test.tyto" title="Code source : Tests">Code source</a>]
</div> </div>
<h3 class="tyto">Une liste mixée <code class="tyto icode">&lt;ol&gt;, &lt;ul&gt;</code></h3> </header>
<h2 id="toc_4" class="tyto">Titre 1</h2>
<p class="tyto"> <p class="tyto">
<ol class="mylist"> Un peu de <abbr class="tyto" title="Cascading SteelSheet" lang="en">CSS</abbr> et <a class="tyto" href="https://forge.a-lec.org" title="La forge libre">le site est prêt</a>
<li class="mylist">numeric ol item 1</li> <p class="note">
<ol> Cet article est un test
<li class="mylist">numeric ol sub-Item 1</li> </p>
<ol> </p>
<li class="mylist">numeric ol sub-sub-item 1</li> <h3 id="toc_4" class="tyto">Citation</h3>
<ul> <blockquote class="tyto" cite="https://..." lang="fr" title="-- Auteur, Nom du livre, AAAA-MM-JJ"><time datetime="AAAA-MM-JJ">
<li class="mylist">ul item <a class="tyto anchor_link" href="#top">Go to Top</a></li> <p class="tyto">
<li class="mylist">ul item <a class="tyto anchor_link" href="#top">Another anchor</a></li> Citation complète dans un paragraphe
</ul> </p>
</ol> </time><footer class="tyto"><a class="tyto" href="https://...">-- Auteur, Nom du livre, AAAA-MM-JJ</a></footer></blockquote>
</ol> <h3 id="toc_4" class="tyto">Code (bloc)</h3>
<pre class="bcode">
<code class="bcode"><span class="bcode ln">1</span><span class="bcode line"># Écrire les marqueurs de mots</span></code>
<code class="bcode"><span class="bcode ln">2</span><span class="bcode line"># Chaque marqueur à la classe CSS de la configuration</span></code>
<code class="bcode"><span class="bcode ln">3</span><span class="bcode line"># Astuce : ** + ← + `` + ← + très gras</span></code>
<code class="bcode"><span class="bcode ln">4</span><span class="bcode line"></span></code>
<code class="bcode"><span class="bcode ln">5</span><span class="bcode line">*`très gras`* =&gt; &lt;strong&gt;</span></code>
<code class="bcode"><span class="bcode ln">6</span><span class="bcode line">+`gras`+ =&gt; &lt;b&gt;</span></code>
<code class="bcode"><span class="bcode ln">7</span><span class="bcode line">/`italique`/ =&gt; &lt;em&gt;</span></code>
<code class="bcode"><span class="bcode ln">8</span><span class="bcode line">;`italique`; =&gt; &lt;i&gt;</span></code>
<code class="bcode"><span class="bcode ln">9</span><span class="bcode line">_`souligné`_ =&gt; &lt;u&gt;</span></code>
<code class="bcode"><span class="bcode ln">10</span><span class="bcode line">~`effacé`~ =&gt; &lt;del&gt;</span></code>
<code class="bcode"><span class="bcode ln">11</span><span class="bcode line">[`cité`] =&gt; &lt;q&gt; # Contenu</span></code>
<code class="bcode"><span class="bcode ln">12</span><span class="bcode line">:`cité`: =&gt; &lt;cite&gt; # auteur, nom</span></code>
<code class="bcode"><span class="bcode ln">13</span><span class="bcode line">|`perso`| =&gt; &lt;span&gt;</span></code>
<code class="bcode"><span class="bcode ln">14</span><span class="bcode line"></span></code>
<code class="bcode"><span class="bcode ln">15</span><span class="bcode line"># Code dans un texte</span></code>
<code class="bcode"><span class="bcode ln">16</span><span class="bcode line"># ! Les marqueurs d&apos;ouverture et de fermeture de code sont sur la MEME LIGNE</span></code>
<code class="bcode"><span class="bcode ln">17</span><span class="bcode line"></span></code>
<code class="bcode"><span class="bcode ln">18</span><span class="bcode line">{` &lt;li&gt;Une entée de liste&lt;/li&gt; `} =&gt; &lt;code&gt;</span></code>
<code class="bcode"><span class="bcode ln">19</span><span class="bcode line"></span></code>
<code class="bcode"><span class="bcode ln">20</span><span class="bcode line"># ! Dans certains cas, il faut ajouter un espace après le 1er marqueur</span></code>
<code class="bcode"><span class="bcode ln">21</span><span class="bcode line"># et/ou avant le second. Ils seront automatiquement supprimés</span></code>
<code class="bcode"><span class="bcode ln">22</span><span class="bcode line"></span></code>
<code class="bcode"><span class="bcode ln">23</span><span class="bcode line">*`DOMAIN/articles/ `* # évite /` : marqueur italique ouvert</span></code>
</pre>
<h3 id="toc_4" class="tyto">Une liste</h3>
<ul class="tyto">
<li class="tyto">Première entrée non ordonnée (ul)</li>
<ul>
<li class="tyto">Sous entrée non ordonnée</li>
<ol>
<li class="tyto">Première sous-sous entrée ordonnée (ol)</li>
<li class="tyto">Seconde sous-sous entrée ordonnée</li>
</ol> </ol>
</ul>
<li class="tyto">Seconde entrée non ordonnée</li>
<li class="tyto">Troisième entrée ... ... non ordonnée</li>
</ul>
</time>
</article>
</p> <!--# include virtual="/template/sidebar.html"-->
<h3 class="tyto">Un block code depuis un fichier</h3>
<code class="tyto bcode"> </main>
<!-- Exemple d'un code Python -->
<p class="bcode"><span class="ln bcode">1</span><span class="line bcode">#================================#</span></p> <!--# include virtual="/template/footer.html"-->
<p class="bcode"><span class="ln bcode">2</span><span class="line bcode"># Searching options in arguments #</span></p>
<p class="bcode"><span class="ln bcode">3</span><span class="line bcode">#--------------------------------#</span></p> </div> <!-- #site_container -->
<p class="bcode"><span class="ln bcode">4</span><span class="line bcode">def get_options():</span></p>
<p class="bcode"><span class="ln bcode">5</span><span class="line bcode"> global dlogs, force, erron</span></p> </body>
<p class="bcode"><span class="ln bcode">6</span><span class="line bcode"> </span></p>
<p class="bcode"><span class="ln bcode">7</span><span class="line bcode"> dlogs = force = erron = False</span></p>
<p class="bcode"><span class="ln bcode">8</span><span class="line bcode"> for arg in range(1, len(sys.argv)):</span></p>
<p class="bcode"><span class="ln bcode">9</span><span class="line bcode"> dlogs = sys.argv[arg] in tyto.debug_options</span></p>
<p class="bcode"><span class="ln bcode">10</span><span class="line bcode"> force = sys.argv[arg] in tyto.force_options</span></p>
<p class="bcode"><span class="ln bcode">11</span><span class="line bcode"> erron = sys.argv[arg] in tyto.debug_errors</span></p>
<p class="bcode"><span class="ln bcode">12</span><span class="line bcode"> </span></p>
<p class="bcode"><span class="ln bcode">13</span><span class="line bcode"> print(&quot;&lt;li&gt;my &apos;cafe !&lt;/li&gt;&quot;)</span></p>
<p class="bcode"><span class="ln bcode">14</span><span class="line bcode"></span></p>
<footer class="bcode">
<p class="bcode">
<a class="bcode" href="/files/RAWS/test.py" title="Exemple d'un code Python">Source</a>
</p>
</footer>
</code>
<h3 class="tyto">Les images</h3>
<a href="logo.png" class="post_logo image" title="tests d'un article"><img src="logo.png" class="post_logo" alt="tests d'un article" title="tests d'un article" /></a>
<p class="tyto">
<a href="/images/hello.png" class="tyto image" title="Text Alt"><img src="/images/hello.png" class="tyto" alt="Text Alt" title="Text Alt" /></a>
<a href="/images/hello.png" class="MYCSS image" title="Text Alt"><img src="/images/hello.png" class="MYCSS" alt="Text Alt" title="Text Alt" /></a>
</p>
<a href="/images/hello.png" class="PIC image" title="Text Alt"><figure class="PIC"><img src="/images/hello.png" class="PIC" alt="Text Alt" title="Text Alt" style="width:60em;height:30%;" /><figcaption class="PIC">echolib sur une chaise</figcaption></figure></a>
``` ```

2
debian/control vendored
View File

@ -1,5 +1,5 @@
Package: tyto Package: tyto
Version: 1.9.39 Version: 1.9.50
Section: custom Section: custom
Priority: optional Priority: optional
Architecture: all Architecture: all

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Version: 1.9.39 # version: 1.9.50
# Updated: 2023-11-11 1699742831
# Tyto - Littérateur # Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org> # Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
@ -16,102 +15,136 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Main binary to execute. # Description: Main executable
# Import modules and start checking/using arguments
# File: /usr/bin/tyto # File: /usr/bin/tyto
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#-------------------------
# Funny Stats Project
#-------------------------
# Project files :
# Project lines :
# Project comments :
# Project functions:
# Project program :
#
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import os, sys import os, sys
#===============# #===================================#
# Error message # # Check Tyto directoryies and files #
#---------------# # Insert directories #
def error_message(path): #-----------------------------------#
print("! Installation error, unused:", path)
sys.exit(1)
#======================================================#
# A little checker to be sure, all files are installed #
#------------------------------------------------------#
def check_install(): def check_install():
os.path.exists(libs) or error_message(libs) # Settings
os.path.exists(trfs) or error_message(trfs) error = False
lib_tyto = "/var/lib/tyto/%s"
for f in prog_files: log_m = "! Tyto installation. Unused"
f = os.path.join(libs, f + ".py")
if not os.path.exists(f):
error_message(f)
# Only default lang files # Files trees
for f in lang_files: files = {
f = os.path.join(trfs, f + ".py") "program" : (
if not os.path.exists(f): "args",
error_message(f) "check",
"debug",
"domain",
"feed",
"form",
"help",
"langs",
"modules",
"new",
"page",
"post",
"publish",
"sitemap",
"tools",
"wip",
),
"translations" : (
"logs_fr",
"logs_en",
"site_fr",
"site_en",
),
}
for d in files:
tyto_dir = lib_tyto%d
if not os.path.exists(tyto_dir):
print("%s directory:"%log_m, tyto_dir)
error = True
continue
# Insert path
sys.path.insert(0, tyto_dir)
for f in files[d]:
tyto_file = os.path.join(tyto_dir, f + ".py")
if not os.path.exists(tyto_file):
print("%s file:"%llog_m, tyto_file)
error = True
error and sys.exit(1)
#======#======================================================================= #=============================================================================#
# MAIN # # Main #
#======# #======#
if not __name__ == "__main__": if not __name__ == "__main__":
print("! Error: '%s' not '%s'"%(__name__, "__main__")) print("! Process error: '%s' not '%s'"%(__name__, "__main__"))
sys.exit(1) sys.exit(1)
# files list in /program/ # Check installed files
prog_files = {
"args",
"check",
"debug",
"domain",
"forms",
"help",
"langs",
"new",
"post",
"show",
"tools",
"tyto",
"userset",
"wip"
}
lang_files = {
"logs_en",
"logs_fr",
"website_en",
"website_fr"
}
# Set librairies to import app files
libs = "/var/lib/tyto/program"
trfs = "/var/lib/tyto/translations"
check_install() check_install()
sys.path.insert(0, libs)
sys.path.insert(0, trfs)
# Manage arguments from command line, start process # Load some Tyto libs
import args import langs, debug, args, domain
args.start_process()
#==========#
# Language #
#==========#
langs.load_logs() # Load logs language
#========#
# Domain #
#========#
domain.check_home() # Mainly check current directory
#===========#
# Arguments #
#===========#
args.get_arguments() # Get arguments from command line
# First starting logs
debug.out(200, os.getlogin(), domain.user_dir, False, 0, False)
debug.out(201, langs.logs_lang, langs.logs_uri, False, 0, False)
if domain.in_hole:
debug.out(4, "$PWD ?", domain.user_dir, True, 2, True)
if args.err_action: # [action] error (only)
debug.out(2, "[action]", args.err_action, False, 2, False)
#===============#
# Start process #
#===============#
import check, help, new, wip, publish
# From command line [ACTION], do
do = {
"check" : check.manage,
"help" : help.manage,
"new" : new.manage,
"publish" : publish.manage,
"wip" : wip.manage,
"start" : domain.manage,
"stop" : domain.manage,
}
do[args.action]()
"""
try: do[args.action]()
except KeyError: debug.out(90, args.action, "", True, 2, True)
"""

View File

@ -14,101 +14,177 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Manage arguments from command line # Description: Manage arguments from command line
# File: /var/lib/tyto/program/args.py # File: /var/lib/tyto/program/args.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#-------------------------
# Funny Stats Project
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import sys import sys
import langs, tyto, debug, help, new, check, userset, show, wip
#==================# #======================================================#
# Action Arguments # # Get action at first position #
#------------------# # Set options for other arguments (no position matter) #
def get_action(): #------------------------------------------------------#
global action def get_arguments():
global commands, action, err_action, set_module
commands = {
"actions" : {
"check" : False,
"wip" : False,
"publish" : False,
"new" : False,
"start" : False,
"stop" : False,
"help" : False,
},
"targets" : {
"*.tyto" : (),
"all" : False,
"domain" : False,
"sitemap" : False,
},
"modules" : {
"modules" : False,
"metas" : False,
"header" : False,
"navbar" : False,
"sidebar" : False,
"footer" : False,
},
"options" : {
"-v" : False,
"-E" : False,
},
}
# Arguments from command line
# ---------------------------
set_module = False
for user_arg in range(2, len(sys.argv)):
# Register articles
if sys.argv[user_arg].endswith(".tyto"):
commands["targets"]["*.tyto"] = \
commands["targets"]["*.tyto"] + (sys.argv[user_arg],)
# Register targets
for arg in commands["targets"]:
if not commands["targets"][arg] \
and sys.argv[user_arg] == arg:
commands["targets"][arg] = True
continue
# Register modules
for arg in commands["modules"]:
if not commands["modules"][arg] \
and sys.argv[user_arg] == arg:
commands["modules"][arg] = True
set_module = True
continue
# Register options
for arg in commands["options"]:
if sys.argv[user_arg] == arg:
commands["options"][sys.argv[user_arg]] = True
continue
# Action, the first argument
# --------------------------
err_action = True
try: action = sys.argv[1]
except: action = "help" ; err_action = False ; return
for arg in commands["actions"]:
if action == arg:
commands["actions"][arg] = True
err_action = False
break
if err_action:
err_action = action
action = "help"
return
"""
global err_act, action, actions, article, targets, logall, erron, force
hlp_actions = ("help", "-h", "--help")
actions = (
"check", "wip", "publish",
"start", "stop",
"new", hlp_actions,
)
debug_opts = ("--verbose", "-v")
erron_opts = ("--errors", "-E")
force_opts = ("--force", "-F")
# Options
err_act = article = targets = logall = erron = force = False
# First argument must be an action
# --------------------------------
try: action = sys.argv[1] try: action = sys.argv[1]
except: action = "" except: action = ""
if not action in actions or action in hlp_actions:
err_act = action
#==================#
# Target arguments #
#------------------#
def get_target():
global target, targets
try: target = sys.argv[2]
except: target = ""
targets = False
if target == "all": targets = True
#================================#
# Searching options in arguments #
# Done only once #
#--------------------------------#
def get_options():
global dlogs, force, erron, set_options
try: set_options ; return
except: pass
dlogs = force = erron = False
for arg in range(1, len(sys.argv)):
if sys.argv[arg] in tyto.debug_options: dlogs = True
if sys.argv[arg] in tyto.force_options: force = True
if sys.argv[arg] in tyto.debug_errors: erron = True
set_options = True
#===========#
# Show logs #
#-----------#
def valid_action():
global action
if not action in tyto.actions:
debug.out(1, "[action]", action, False, 2, False)
action = "help" action = "help"
return
# Set other arguments passed
# --------------------------
global domain, modules, metas, header, navbar, sidebar, footer, set_modules
global sitemap
sitemap = False
domain = modules = metas = header = navbar = sidebar = footer = False
set_modules = {
"modules" : "",
"metas" : "",
"header" : "",
"navbar" : "",
"sidebar" : "",
"footer" : "",
}
for arg in range(2, len(sys.argv)):
# ------
# Target
# ------
# Argument is a .tyto article
if not article and sys.argv[arg].endswith(".tyto"):
article = sys.argv[arg]
continue
# Other targets
# domain
if not domain: domain = bool(sys.argv[arg] == "domain")
if not sitemap: sitemap = bool(sys.argv[arg] == "sitemap")
# Modules
if not modules:
modules = set_modules["modules"] = bool(sys.argv[arg] == "modules")
if not metas:
metas = set_modules["metas"] = bool(sys.argv[arg] == "metas")
if not header:
header = set_modules["header"] = bool(sys.argv[arg] == "header")
if not navbar:
navbar = set_modules["navbar"] = bool(sys.argv[arg] == "navbar")
if not sidebar:
sidebar = set_modules["sidebar"] = bool(sys.argv[arg] == "sidebar")
if not footer:
footer = set_modules["footer"] = bool(sys.argv[arg] == "footer")
#==============# # -------
# Start action # # Options
#--------------# # -------
def start_process(): if not targets: targets = bool(sys.argv[arg] == "all")
# Set Lang logs if not logall: logall = bool(sys.argv[arg] in debug_opts)
langs.load_logs_lang() if not erron: erron = bool(sys.argv[arg] in erron_opts)
if not force: force = bool(sys.argv[arg] in force_opts)
get_options() """
get_action()
get_target()
valid_action()
do = {
"help" : help.show,
"check" : check.manage,
"new" : new.manage,
"set" : userset.manage,
"start" : userset.manage,
"stop" : userset.manage,
"show" : show.manage,
"wip" : wip.manage,
}
do[action](action, target)

File diff suppressed because it is too large Load Diff

View File

@ -14,95 +14,75 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Add more logs when debug is set in command line # Description: Show logs in console. Manage verbose mode (-v)
# File: /var/lib/tyto/program/debug.py # File: /var/lib/tyto/program/debug.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#-------------------------
# Funny Stats Project
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import sys import sys
import langs, args, logs import args, langs, domain
#===================# #===================#
# Messages for logs # # Messages for logs #
#-------------------# #-------------------#
def set_messages(): def set_messages():
global messages, got_messages global messages, loaded_messages
try: got_messages ; return try: loaded_messages ; return
except: pass except: pass
messages = \ messages = \
{ {
# ERRORS (1-100) # ERRORS (1-100)
1 : langs.logs.err_arg, 2 : langs.logs.err_argument,
2 : langs.logs.err_hole, 3 : langs.logs.err_domain_name,
3 : langs.logs.err_dir, 4 : langs.logs.err_in_hole,
4 : langs.logs.err_cd, 5 : langs.logs.err_file_create,
5 : langs.logs.err_no_file, 6 : langs.logs.err_dir_unused,
6 : langs.logs.err_no_dir, 7 : langs.logs.err_dir_create,
7 : langs.logs.err_cr_file, 8 : langs.logs.err_lang_no,
8 : langs.logs.err_lang, 9 : langs.logs.err_domain_dir,
9 : langs.logs.err_ini_file, 10 : langs.logs.err_dir_in,
10 : langs.logs.err_post_global, 11 : langs.logs.err_file_no,
11 : langs.logs.err_post_not_chk, 12 : langs.logs.err_post_db,
13 : langs.logs.err_post_not_www, 13 : langs.logs.err_post_srv_no,
20 : langs.logs.err_bad_uri, 30 : langs.logs.err_post_sep,
21 : langs.logs.err_post_sep, 31 : langs.logs.err_post_header_no,
22 : langs.logs.err_post_head, 32 : langs.logs.err_post_writer_no,
23 : langs.logs.err_post_empty, 33 : langs.logs.err_post_tag_set,
50 : langs.logs.err_date, 34 : langs.logs.err_post_tag_val,
51 : langs.logs.err_post_data, 35 : langs.logs.err_post_tag_id,
52 : langs.logs.err_post_title, 36 : langs.logs.err_post_mark_no,
53 : langs.logs.err_post_paired, 37 : langs.logs.err_post_mark_stop,
54 : langs.logs.err_post_id_yet, 38 : langs.logs.err_post_mark_miss,
55 : langs.logs.err_post_in_tag, 39 : langs.logs.warn_post_tupic,
56 : langs.logs.err_post_datatag, 90 : langs.logs.err_process,
# WARNINGS (100-200) 99 : langs.logs.err_kbd_stop,
100 : langs.logs.warn_no_dom, # WARNINGS
101 : langs.logs.domain_created, 100 : langs.logs.warn_domain_no,
102 : langs.logs.reset_dom, 101 : langs.logs.warn_date_format,
103 : langs.logs.website_lang, 102 : langs.logs.warn_domain_conf,
104 : langs.logs.domains_no, 199 : langs.logs.warn_maybe_later,
105 : langs.logs.domain_off, # INFOS (200-255)
106 : langs.logs.warn_post_chk, 200 : langs.logs.inf_dir_user,
# Great (200-255) 201 : langs.logs.inf_lang_logs,
200 : langs.logs.load_file, 202 : langs.logs.inf_domain_load,
201 : langs.logs.lang_logs_sys, 203 : langs.logs.inf_domain_conf,
202 : langs.logs.domain_found, 205 : langs.logs.inf_file_create,
203 : langs.logs.created_dir, 206 : langs.logs.inf_file_upd,
204 : langs.logs.domain_updated, 207 : langs.logs.inf_dir_create,
205 : langs.logs.domain_new, 210 : langs.logs.inf_check_post,
206 : langs.logs.created_file, 211 : langs.logs.inf_wip_process,
207 : langs.logs.updated_file, 212 : langs.logs.inf_mod_process,
208 : langs.logs.website_lang, 250 : langs.logs.inf_end_process,
209 : langs.logs.domain_on,
210 : langs.logs.post_chk_yet,
211 : langs.logs.checking_post,
212 : langs.logs.wipping_post,
214 : langs.logs.reading_domain,
215 : langs.logs.processing,
216 : langs.logs.create_sitemaps,
250 : langs.logs.completed,
254 : langs.logs.post_chk_ready,
255 : langs.logs.later,
} }
got_messages = True loaded_messages = True
#===================================# #===================================#
@ -118,18 +98,13 @@ def set_messages():
def out(nbr, var, val, show, color, stop): def out(nbr, var, val, show, color, stop):
set_messages() set_messages()
logs.add_line(nbr, messages[nbr], var, val) # File log printing
# -----------------
args.get_options() #logit(nbr, var, val)
logit = show or args.erron and color > 0 or args.dlogs
if args.erron:
if color == 0:
logit = False
if not logit:
return nbr
# Front User Console
# ------------------
# COlors # COlors
CS = '\033[0;0m' # Unset CS = '\033[0;0m' # Unset
CL = '\033[0;2m' # Gray CL = '\033[0;2m' # Gray
@ -144,18 +119,23 @@ def out(nbr, var, val, show, color, stop):
SC = CL # Default gray SC = CL # Default gray
if color == 0: SC = CG if color == 0: SC = CG
elif color == 1: SC = CY elif color == 1: SC = CY
elif color == 2: SC = CR elif color == 2: SC = CR
# Print, acoording to parameters
print("%s*%s %s%s%s > %s%s%s < %s%s%s"%(
SC, CS,
CL, messages[nbr], CS,
CB, var, CS,
CC, val, CS
)
)
if not show:
if args.commands["options"]["-v"]: show = True
force_show = bool(color > 0 and args.commands["options"]["-E"])
if show or force_show:
# Print, acoording to parameters
print("%s*%s %s%s%s > %s%s%s < %s%s%s"%(
SC, CS,
CL, messages[nbr], CS,
CB, var, CS,
CC, val, CS
)
)
# Exit if stop = True # Exit if stop = True
if stop: if stop:
if nbr >= 200: nbr = 0 if nbr >= 200: nbr = 0
@ -163,3 +143,11 @@ def out(nbr, var, val, show, color, stop):
return nbr return nbr
#============#
# Files logs #================================================================
#------------#
def logit(nbr, var, val):
try: domain.name
except: domain.name = ""
print(": debug. domain name", domain.name)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,191 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Create RSS/feed (when publish only)
# File: /var/lib/tyto/program/feed.py
#----------------------------------------------------------------------
import os, configparser, datetime, glob
import domain, args, debug, tools, langs
#=========================================#
# Create RSS/Feed file in www server only #
# Only published articles are included #
# Feed can be deactivated in domain conf #
#-----------------------------------------#
def create():
max_item = int(domain.cf.get("WEBSITE_MODULES", "rss_items"))
if max_item == 0:
return
db_dir = domain.wrk_dirs["db"]
# Scan DB directory and sort files by last updated
items = filter(os.path.isfile, glob.glob(db_dir + '*.ini'))
items = sorted(items, key = os.path.getmtime, reverse=True)
nbr_item = 0
feed_items = ""
bld_date = tools.nowdate("feed") # General Build date
for db_uri in items:
if nbr_item == max_item:
print("MAX")
break
if not check_db(db_uri):
continue
nbr_item += 1
pub_date = datetime.datetime.strptime(DB["date"], '%Y-%m-%d').date()
pub_date = pub_date.strftime("%a, %d %b %Y")
feed_item = \
feed_item_tpl%(
nbr_item, max_item,
DB["title"],
DB["www_url"],
DB["www_url"],
pub_date,
DB["about"],
DB["authors"],
DB["logo"],
"%s (logo)"%DB["title"],
DB["www_url"],
DB["tags"]
)
if not feed_items: feed_items = feed_item
else: feed_items = "%s\n%s"%(feed_items, feed_item)
feed = \
feed_tpl%(
bld_date,
domain.web["www"] + domain.cf.get("TEMPLATE_FILENAMES", "rss"),
domain.conf["title"],
domain.web["www"],
domain.conf["about"],
domain.web["www"] + "template/" + domain.cf.get("TEMPLATE_FILENAMES", "logo"),
"%s (logo)"%(domain.conf["title"]),
domain.web["www"],
domain.lang,
domain.conf["tags"],
bld_date,
domain.cf.get("WEBSITE", "license"),
domain.conf["mail"],
feed_items
)
feed_uri = domain.www + domain.cf.get("TEMPLATE_FILENAMES", "rss")
tools.create_file(feed_uri, feed)
debug.out(250, "RSS/Feed", feed_uri, True, 0, False)
#===============================#
# Check/Set article DB #
# Check if article can be added #
#-------------------------------#
def check_db(db_uri):
global DB
cf = configparser.ConfigParser()
cf.read(db_uri)
try:
DB = {
"uri_web" : cf.get("URIS", "web"),
"title" : cf.get("ARTICLE", "title"),
"about" : cf.get("ARTICLE", "about"),
"tags" : cf.get("ARTICLE", "tags"),
"date" : cf.get("ARTICLE", "date"),
"authors" : cf.get("ARTICLE", "authors"),
"logo" : cf.get("ARTICLE", "logo"),
"www_url" : cf.get("ARTICLE", "www_url"),
"hash_www" : cf.get("HASHES", "www"),
"norss" : cf.getboolean("MODULES", "norss"),
}
except:
return False
if not DB["hash_www"] or DB["norss"]:
return False
return True
#======#
# MAIN #=======================================================================
#======#
#=======================#
# RSS/Feed xml template #
#-----------------------#
feed_tpl = """<?xml version="1.0" encoding="utf-8" ?>
<!-- #==========================================# -->
<!-- # RSS/Feed generated by Tyto - Littérateur # -->
<!-- #==========================================# -->
<!-- Creation date: %s -->
<!-- Feed URL: %s -->
<rss version="2.0">
<channel>
<title>%s</title>
<link>%s</link>
<description>%s</description>
<image>'
<url>%s</url>
<title>%s</title>
<link>%s</link>
</image>
<language>%s</language>
<category>%s</category>
<lastBuildDate>%s</lastBuildDate>
<copyright>%s</copyright>
<webMaster>%s</webMaster>
<generator>Tyto - Littérateur</generator>
<!-- Articles ordred by last update -->
%s
</channel>
</rss>
"""
feed_item_tpl = """<!-- Item %s/%s -->
<item>
<title>%s</title>
<link>%s</link>
<guid>%s</guid>
<pubDate>%s</pubDate>
<description>%s</description>
<author>%s</author>
<image>
<url>%s</url>
<title>%s</title>
<link>%s</link>
</image>
<category>%s</category>
</item>"""

View File

@ -0,0 +1,179 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Manage forms. When a question is asked.
# File: /var/lib/tyto/program/forms.py
#----------------------------------------------------------------------
import os, sys
import langs, domain, debug, tools
#======================================================#
# Ask a question #
# in yes_mod, value is from local langs.logs.ok values #
# has a keyboad interrupt #
#------------------------------------------------------#
def ask(q, yes_mod, default):
answer = expected = ""
if yes_mod:
expected = langs.logs.ok
try:
answer = input(q)
except KeyboardInterrupt:
print()
debug.out(99, q, expected, False, 2, True)
if yes_mod:
for ok in expected:
if answer.lower() == ok.lower():
return True
debug.out(199, q, expected, True, 1, True)
if default and not answer: return default
elif not answer: return False
return answer
#=========================#
# Confirm domain creation #
#-------------------------#
def ask_domain_creation():
q = '- %s "%s"%s '%(
langs.logs.ask_domain_name, domain.name, langs.logs.q)
# Create domain configuration file
if ask(q, True, ""):
domain.cf_create()
#=================================================#
# Forms #
# Each questions are asked from domain.user_set() #
# In mismatch case, question is asked again #
#-------------------------------------------------#
#======================#
# Ask for domain title #
#----------------------#
def ask_domain_title(default):
q = '- %s "%s"%s '%(
langs.logs.ask_domain_title, default, langs.logs.q)
return ask(q, False, default) or ""
#==============================#
# Ask for domain creation date #
#------------------------------#
def ask_domain_date(default):
default = tools.nowdate("int-short").rsplit("-")[0] # Set year only
debug.out(101, "[YYYY[-MM][-DD]]", "", True, 0, False)
q = '- %s "%s"%s '%(
langs.logs.ask_domain_date, default, langs.logs.q)
return ask(q, False, default) or ""
#============================#
# Ask for domain description #
#----------------------------#
def ask_domain_about(default):
q = '- %s "%s"%s '%(
langs.logs.ask_domain_about, default, langs.logs.q)
return ask(q, False, default) or ""
#=====================#
# Ask for domain tags #
#---------------------#
def ask_domain_tags(default):
default = domain.cf.get("DOMAIN", "title") + ","
q = '- %s "%s"%s '%(
langs.logs.ask_domain_tags, default, langs.logs.q)
return ask(q, False, default) or ""
#============================#
# Ask for administrator mail #
#----------------------------#
def ask_domain_mail(default):
q = '- %s "%s"%s '%(
langs.logs.ask_domain_mail, default, langs.logs.q)
return ask(q, False, default) or ""
#=============================#
# Ask for wzbsite language #
# Check if translation exists #
#-----------------------------#
def ask_website_lang(default):
default = langs.logs_lang
q = '- %s "%s"%s '%(
langs.logs.ask_website_lang, default, langs.logs.q)
lang = ask(q, False, default)
if not os.path.exists(langs.lang_files["site"]["uri"]%lang):
debug.out(8, lang, langs.lang_files["site"]["uri"]%lang, True, 2, False)
lang = ""
return lang
#===============================#
# Ask for server root directory #
# Check if directory exists #
#-------------------------------#
def ask_srv_root(default):
srv_uris = (default, "/var/www/", "/srv/http/")
for srv_uri in srv_uris:
if os.path.exists(srv_uri):
default = srv_uri
break;
q = '- %s "%s"%s '%(
langs.logs.ask_srv_root, default, langs.logs.q)
srv_uri = ask(q, False, default)
if srv_uri:
if not srv_uri.endswith("/"):
srv_uri = srv_uri + "/"
if not os.path.exists(srv_uri):
debug.out(6, q, srv_uri, True, 2, False)
srv_uri = ""
return srv_uri
#
#
#
def ask_reset_modules():
q = "- %s%s "%(langs.logs.ask_reset_modules, langs.logs.q)
if ask(q, True, ""):
return

View File

@ -14,30 +14,64 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Show help commands # Description: Show help, and manage some arguments with it
# File: /var/lib/tyto/program/help.py # File: /var/lib/tyto/program/help.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#------------------------- import sys
# Funny Stats Project import args, langs
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import langs, debug #
#
#
def manage():
helps = {
"new" : langs.logs.help_new,
"check" : langs.logs.help_check,
"wip" : langs.logs.help_wip,
"modules" : langs.logs.help_modules,
"list" : langs.logs.help_list,
"cite" : langs.logs.help_cite,
"image" : langs.logs.help_image,
"link" : langs.logs.help_link,
"file" : langs.logs.help_file,
"code" : langs.logs.help_code,
"words" : langs.logs.help_words,
"abbr" : langs.logs.help_abbr,
"anchor" : langs.logs.help_anc,
"raw" : langs.logs.help_raw,
"article" : langs.logs.help_article,
}
norepeat = \
(
"list", "cite", "image", "link", "words", "code", "raw", "abbr", "anchor",
"file",
)
#==========================================# try:
# Show help in system langage # sys.argv[2]
# Help Contents is in translations/logs_XX # if not sys.argv[2] in helps: show_main = True
#------------------------------------------# else: show_main = False
def show(action, target): except:
print(langs.logs.help_contents) show_main = True
if show_main:
print(langs.logs.help_man)
if args.commands["targets"]["all"]:
for h in helps:
if not h in norepeat:
print(helps[h])
return
for arg in range(2, len(sys.argv)):
if sys.argv[arg] in helps:
print(helps[sys.argv[arg]])

View File

@ -14,127 +14,65 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Load lang logs file according to system language # Description: Set languages for logs and website
# Load lang site file according to domain configuration
# File: /var/lib/tyto/program/langs.py # File: /var/lib/tyto/program/langs.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#------------------------- import locale
# Funny Stats Project import domain
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import sys, locale, os
import args, debug, domain
trfs = "/var/lib/tyto/translations/" # Settings files
def set_lang_files():
global lang_files, files_set
#==================================# lang_files = \
# Check if translation file exists # {
# module: # "logs" : {
# - "logs": logs_xx # "name" : "logs_%s",
# - "website": website_xx # "uri" : "/var/lib/titi/translations/logs_%s.py",
# lang: 2 chars language # },
# out: exit if True "site" : {
# return True or False # "name" : "site_%s",
#----------------------------------# "uri" : "/var/lib/titi/translations/site_%s.py",
def translation_exists(module, lang, out): },
global tr_file }
modules = ("logs", "website") files_set = True
# in case of internal typo error
if not module in modules:
print("! langs: internal error: 'logs', 'website'")
sys.exit(254)
tr_file = "%s%s_%s.py"%(trfs, module, lang)
if not os.path.exists(tr_file):
debug.out(5, lang, tr_file, True, 2, False)
return False
return True
#=============================================================================# # Use logs from system language or default "en"
# LOGS | # def load_logs():
# Set and import file | # global logs, logs_lang, logs_uri
#=============================================================================#
#=============================#
# Get system Lang to set logs #
#-----------------------------#
def get_sys_lang():
global lang, tr_logs_uri
tr_logs_uri = "%slogs_%s.py"
try: lang = locale.getdefaultlocale()[0].rsplit("_")[0]
except: lang = "en"
if not translation_exists("logs", lang, False):
lang = "en"
tr_logs_uri = tr_logs_uri%(trfs, lang)
return lang
set_lang_files()
#===============================#
# Import logs lang file in logs #
#-------------------------------#
def load_logs_lang():
global logs, lang, set_logs
try: try:
set_logs logs_lang = locale.getdefaultlocale()[0].rsplit("_")[0]
except: except:
logs = __import__("logs_%s"%get_sys_lang()) logs_lang = "en"
debug.out(201, lang, tr_logs_uri, False, 0, False)
set_logs = True # Load language file
logs_name = lang_files["logs"]["name"]%logs_lang
logs_uri = lang_files["logs"]["uri"]%logs_lang
logs = __import__(logs_name)
#=============================================================================# # loan language website values
# WEBSITE | # def load_site():
# Get/Set and import file | # global site
#=============================================================================#
#=======================================#
# Get website lang from cf to set site #
#---------------------------------------#
def get_website_lang():
global site_lang, tr_website_uri
tr_website_uri = "%swebsite_%s.py" try: files_set
try: site_lang = domain.cf.get("WEBSITE", "lang") except: set_lang_files()
except: site_lang = get_sys_lang()
if not translation_exists("website", site_lang, False): try: site_lang = domain.lang
site_lang = get_sys_lang() # or default "en" except: site_lang = "en"
tr_website_uri = tr_website_uri%(trfs, site_lang)
return site_lang
try: site = __import__(lang_files["site"]["name"]%site_lang)
except: site = __import__(lang_files["site"]["name"]%"en")
#==================================#
# Import website lang file in site #
#----------------------------------#
def load_website_lang():
global site, site_lang, set_site
site = __import__("website_%s"%get_website_lang())
try:
set_site
except:
debug.out(208, site_lang, tr_website_uri, False, 0, False)
set_site = True

View File

@ -0,0 +1,601 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Manage modules creations (metas, to footer called by nginx)
# File: /var/lib/tyto/program/modules.py
#----------------------------------------------------------------------
import os, ast, configparser
import args, domain, langs, tools, debug, form
#=================================================#
# Manage modules creations #
# metas, header, navbar, sidebar, footer #
# - User must configure: navbar, sidebar #
# - Default contents: metas, header, footer #
# From [module].raw, create [module].html #
# All are called from file with nginx in web page #
#-------------------------------------------------#
def manage(module):
global files, wrk_dir, asked, mod_write, wip_dir
# Set directories
wrk_dir = domain.wrk_dirs["modules"]
wip_dir = domain.wip_dirs["template"]
www_dir = domain.www_dirs["template"]
debug.out(212, module, wrk_dir, False, 0, False)
files = {
"metas" : {
"wrk" : wrk_dir + "metas.raw",
"wip" : wip_dir + "metas.html",
"www" : www_dir + "metas.html",
"set" : set_metas_raw,
},
"header" : {
"wrk" : wrk_dir + "header.raw",
"wip" : wip_dir + "header.html",
"www" : www_dir + "header.html",
"set" : set_header_raw,
},
"navbar" : {
"wrk" : wrk_dir + "navbar.raw",
"wip" : wip_dir + "navbar.html",
"www" : www_dir + "navbar.html",
"set" : set_navbar_raw,
},
"sidebar" : {
"wrk" : wrk_dir + "sidebar.raw",
"wip" : wip_dir + "sidebar.html",
"www" : www_dir + "sidebar.html",
"set" : set_sidebar_raw,
},
"footer" : {
"wrk" : wrk_dir + "footer.raw",
"wip" : wip_dir + "footer.html",
"www" : www_dir + "footer.html",
"set" : set_footer_raw,
},
}
# Load/Create new modules in DB file
cf_mod_load()
mod_write = False
# Check/Set raw files in modules/
for mod in files:
# For all modules to reset, confirm once
if os.path.exists(files[mod]["wrk"]):
if module == "all":
try: asked
except: form.ask_reset_modules() ; asked = True
elif mod != module:
continue
mod_write = True
files[mod]["set"](mod)
tools.create_file(files[mod]["wrk"], files[mod]["usr"])
cid = tools.get_HID(files[mod]["wrk"], True)
cf_mod_set("RAWS", mod, cid)
cf_mod_write()
if args.action == "new":
debug.out(250, module, wrk_dir, True, 0, False)
# When an article is "wip"
# - create html modules
# - - if not already exists
# - - if raw module has changed
if module == "wip":
for mod in files:
if os.path.exists(files[mod]["wip"]):
raw_cid = tools.get_HID(files[mod]["wrk"], True)
try: db_id = cf_mod.get("RAWS", mod)
except: db_id = ""
if raw_cid == db_id:
continue
else:
cf_mod_set("RAWS", mod, raw_cid)
mod_write = True
create_htmls(mod)
cf_mod_write()
# When user wants to force update HTML modules
# using wip [MODULE] ; called in wip.py
elif module == "update":
# Create again ALL modules in wip/template
if args.commands["modules"]["modules"]:
for mod in files:
if os.path.exists(files[mod]["wip"]):
create_htmls(mod)
# Create again specific module from command line
else:
for mod in args.commands["modules"]:
if args.commands["modules"][mod]:
if os.path.exists(files[mod]["wip"]):
create_htmls(mod)
#=======================================#
# Manage modules configuration ini file #======================================
#---------------------------------------#
#================================#
# Create new modules DB ini file #
#--------------------------------#
def cf_mod_load():
global cf_mod_uri, cf_mod
cf_mod_uri = wrk_dir + "modules.ini"
if not os.path.exists(cf_mod_uri):
tools.create_file(cf_mod_uri, "")
cf_mod = False
cf_mod = configparser.ConfigParser()
cf_mod.read(cf_mod_uri)
#============================#
# Set datas to cf_mod ini DB #
#----------------------------#
def cf_mod_set(section, key, value):
global cf_mod
# Add section if not exists
try: cf_mod.add_section(section)
except: pass
cf_mod.set(section, key, value)
#==================================#
# Write to cf_mod ini DB if needed #
#----------------------------------#
def cf_mod_write():
if mod_write:
with open(cf_mod_uri, "w") as f:
cf_mod.write(f)
#==================================================#
# Create default raw modules in modules/ directory #===========================
#--------------------------------------------------#
#=====================================#
# Set metas.raw with default contents #
#-------------------------------------#
def set_metas_raw(mod):
global files
files["metas"]["usr"] = langs.logs.metas_raw%(
domain.conf["title"],
domain.wrk_dirs["articles"],
files[mod]["wrk"],
files[mod]["wip"],
files[mod]["www"],
) + metas_raw
#======================================#
# Set header.raw with default contents #
#--------------------------------------#
def set_header_raw(mod):
global files, header_raw
# ----------------------------------- #
# Fille values for generic header_raw #
# ----------------------------------- #
header_raw = \
header_raw%(
"%s %s"%(langs.site.home, domain.conf["title"]),
"/template/%s"%domain.cf.get("TEMPLATE_FILENAMES", "logo"),
"logo: %s"%domain.conf["title"],
domain.conf["title"],
domain.conf["about"]
)
files["header"]["usr"] = langs.logs.header_raw%(
domain.conf["title"],
domain.wrk_dirs["articles"],
files[mod]["wrk"],
files[mod]["wip"],
files[mod]["www"],
) + header_raw
#===============================#
# Set navbar.raw with help only #
#-------------------------------#
def set_navbar_raw(mod):
global files
files["navbar"]["usr"] = langs.logs.metas_raw%(
domain.conf["title"],
domain.wrk_dirs["articles"],
files[mod]["wrk"],
files[mod]["wip"],
files[mod]["www"],
)
#================================#
# Set sidebar.raw with help only #
#--------------------------------#
def set_sidebar_raw(mod):
global files
files["sidebar"]["usr"] = langs.logs.sidebar_raw%(
domain.conf["title"],
domain.wrk_dirs["articles"],
files[mod]["wrk"],
files[mod]["wip"],
files[mod]["www"],
)
#======================================#
# Set footer.raw with default contents #
#--------------------------------------#
def set_footer_raw(mod):
global files, footer_raw
# ---------------------------------- #
# Fill values for generic footer_raw #
# ---------------------------------- "
footer_menu = "<!-- Links from domain configuration [WEBSITE_FOOTER] -->"
footer_link = \
'\n<li class="footer_item"><a href="%s" title="%s %s">%s</a></li>'
for key, value in domain.cf.items("WEBSITE_FOOTER"):
try:
tupvalues = ast.literal_eval(value)
footer_menu = footer_menu + \
footer_link%(tupvalues[1], tupvalues[2], "", tupvalues[0])
except:
debug.out(102, "%s = %s"%(key, value), domain.cf_uri, True, 1, False)
if domain.conf["sitemaps"]:
footer_menu = footer_menu + \
footer_link%(
"/sitemap.html",
langs.site.sitemap, domain.conf["title"],langs.site.sitemap
)
footer_menu = footer_menu + \
footer_link%(
"/" + domain.cf.get("TEMPLATE_FILENAMES", "rss"),
langs.site.feed_rss, domain.conf["title"], langs.site.feed_rss
)
# Copyright link
cur_date = tools.nowdate("year")
if domain.conf["date"][:4] != cur_date:
cur_date = "%s - %s"%(domain.conf["date"][:4], cur_date)
footer_copy = "Copyright © %s %s"%(cur_date, domain.conf["title"])
# Tyto credit links
tyto_site_link = "https://tyto.echolib.re/"
tyto_code_link = "https://forge.a-lec.org/echolib/tyto-litterateur"
footer_tyto = \
'%s <a class="footer_item_link" href="%s" title="%s">'%(
langs.site.tyto_credit, tyto_site_link, langs.site.off_website) + \
'Tyto - Littérateur' + \
'</a> [<a href="%s" title="Tyto - Littérateur">'%tyto_code_link + \
'%s'%langs.site.source_code + \
'</a>]'
# Create full footer_raw contents
footer_raw = footer_raw%(
# <h2>
langs.site.about, domain.conf["title"],
# About
domain.conf["about"],
# Menu list items
footer_menu,
# Copyright and Tyto credit
footer_copy,
footer_tyto,
)
files["footer"]["usr"] = langs.logs.footer_raw%(
domain.conf["title"],
domain.wrk_dirs["articles"],
files[mod]["wrk"],
files[mod]["wip"],
files[mod]["www"],
) + footer_raw
#===================================================#
# Create HTML modules files in template/ wip only #============================
# At publish process, files are copied, not created #
#---------------------------------------------------#
#============================#
# Create [module].html files #
#----------------------------#
def create_htmls(mod):
html_file = "<!-- %s from user file configuration: %s.raw -->"%(mod, mod)
if mod == "navbar":
html_file = create_navbar_html(html_file)
elif mod == "sidebar":
html_file = create_sidebar_html(html_file)
else:
with open(files[mod]["wrk"], "r") as f:
for line in f.read().rsplit("\n"):
if not line or line.isspace() or line.lstrip().startswith("#"):
continue
html_file = "%s\n%s"%(html_file, line)
tools.create_file(files[mod]["wip"], html_file)
debug.out(250, mod, wip_dir, True, 0, False)
#==============================================#
# Create HTML navbar contents, from navbar.raw #
#----------------------------------------------#
def create_navbar_html(html_file):
menu_links = ""
with open(files["navbar"]["wrk"], "r") as f:
for ln, line in enumerate(f.read().rsplit("\n"), 1):
line = line.lstrip()
if not line or line.isspace() or line.startswith("#"):
continue
# Item has a "title" set with # as separator
set_dir = line.rstrip()
# Set_dir begins with "/" ; apply correction
if set_dir.startswith("/"): set_dir = set_dir[1:]
if set_dir.endswith("/"): set_dir = set_dir[:-1]
if not set_dir: # No home / in navbar menu
continue
dir_uri = domain.wrk_dirs["articles"] + set_dir
# Set_dir must exists
if not os.path.exists(dir_uri) or \
not os.path.isdir(dir_uri):
debug.out(6, "%s. navbar '%s'"%(
ln, set_dir
), files["navbar"]["wrk"], True, 1, False)
continue
# Check for index.tyto, load DB if exists, get title
if cf_load(ln, set_dir + "/index.tyto", "navbar"):
try: set_title = cf.get("ARTICLE", "title")
except: continue
# Check and inform if not index.html exists
srv_index_html = domain.wip + "/index.html"
if not os.path.exists(srv_index_html):
debug.out(11, "%s. navbar (404) wip/%s/index.html"%(
ln, set_dir
), files["navbar"]["wrk"], True, 1, False)
# Create menu links
if not menu_links:
menu_links = navbar_link%(
"/" + set_dir, set_title, os.path.basename(set_dir)
)
else:
menu_links = "%s\n%s"%(
menu_links,
navbar_link%(
"/" + set_dir, set_title, os.path.basename(set_dir)
)
)
if menu_links:
html_file = html_file + navbar_html%menu_links
return html_file
#================================================#
# Create HTML sidebar contents, from sidebar.raw #
#------------------------------------------------#
def create_sidebar_html(html_file):
# Set from config file max links. Return if 0
max_items = int(domain.cf.get("WEBSITE_MODULES", "sidebar_items"))
sidebar_title = domain.cf.get("WEBSITE_MODULES", "sidebar_title")
if max_items == 0:
return html_file
html_items = ""
items = 0
with open(files["sidebar"]["wrk"], "r") as f:
for ln, line in enumerate(f.read().rsplit("\n"), 1):
if items == max_items:
break
line = line.lstrip()
if not line or \
line.isspace() or \
line.startswith("#") or \
not line.endswith(".tyto"):
continue
# Target begins with "/"... Remove it
if line.startswith("/"): line = line[1:]
# check/load article files
if not cf_load(ln, line, "sidebar"):
continue
items += 1
print("> -",items, cf.get("ARTICLE", "title"))
if not html_items:
html_items = sidebar_item%(
# <h3>
cf.get("URIS", "web"),
cf.get("ARTICLE", "title"),
# <p title="">
cf.get("ARTICLE", "title"),
cf.get("ARTICLE", "authors"),
cf.get("ARTICLE", "local_date"),
# Show about
cf.get("ARTICLE", "about")
)
else:
html_items = "%s\n%s"%(html_items, sidebar_item%(
cf.get("ARTICLE", "title"),
cf.get("ARTICLE", "about")
)
)
if html_items:
html_file = html_file + sidebar_html%(sidebar_title, html_items)
return html_file
#==========================================#
# Get DB post from URI, and load if exists #
#------------------------------------------#
def cf_load(ln, line, modname):
global cf
uri = domain.wrk_dirs["articles"] + line
if not os.path.exists(uri):
debug.out(11, "%s. %s '%s'"%(
ln, modname, line
), files[modname]["wrk"], True, 1, False)
return False
db_uri = domain.wrk_dirs["db"] + tools.get_HID(uri, False) + ".ini"
if not os.path.exists(db_uri):
debug.out(12, "%s. %s '%s'"%(
ln, modname, line
), files[modname]["wrk"], True, 1, False)
return False
cf = configparser.ConfigParser()
cf.read(db_uri)
try: srv_uri = cf.get("URIS", "wip")
except: srv_uri = ""
if not srv_uri or not os.path.exists(srv_uri):
debug.out(13, "%s. %s '%s'"%(
ln, modname, line
), files[modname]["wrk"], True, 1, False)
return False
return True
#===============#
# MAIN settings #==============================================================
#---------------#
# ---------------------------- #
# Default raw modules contents #
# ---------------------------- #
#-------#
# Metas #
#-------#
metas_raw = """
<meta charset="UTF-8">
<meta name="robots" content="all">
<meta name="medium" content="website">
<meta name="revisit-after" content="3 days">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
"""
#--------#
# Header #
#--------#
header_raw = """
<a id="site_link" href="/" title="%s">
<header id="header">
<div id="header_logo">
<img id="header_logo_image" src="%s" alt="%s">
</div>
<div id="header_abouts">
<p id="header_title">%s</p>
<p id="header_about">%s</p>
</div<
</header>
</a>
"""
#--------#
# Navbar #
#--------#
navbar_html = """
<nav id="menu" aria-labelledby="navigation-1">
<menu id="menu_items">
%s
</menu>
</nav>
"""
navbar_link = '<li class="menu_item">' + \
'<a class="menu_link" href="%s" title="%s">%s</a>' + \
'</li>'
#---------#
# Sidebar #
#---------#
sidebar_html = """
<aside id="sidebar">
<h2 id="sidebar_title">%s</h2>
<nav id="sidebar_menu" aria-labelledby="navigation-2">
<ul id="sidebar_items">
%s
</ul>
</nav>
</aside>
"""
sidebar_item = """<li class="sidebar_item">
<h3 class="sidebar_post_title"><a class="sidebar_post_link" href="%s">%s</a></h3>
<div class="sidebar_post_about">
<p title="%s -- %s, %s">%s</p>
</div>
</li>"""
#--------#
# Footer #
#--------#
footer_raw = """
<footer id="footer">
<h2 id="footer_title">%s %s</h2>
<div id="footer_about_menu">
<div id="footer_about">
<p id="footer_about">%s</p>
</div>
<nav id="footer_menu" aria-labelledby="navigation-3">
<ul id="footer_items">
%s
</ul>
</nav>
</div>
<div id="footer_credit">
<p id="footer_copyright">%s</p>
<p id="footer_tyto">%s</p>
</div>
</footer>
"""

View File

@ -14,60 +14,37 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: When user wants to create something new # Description: When something new must be created (domain, modules...)
# File: /var/lib/tyto/program/new.py # File: /var/lib/tyto/program/new.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#------------------------- import args, domain, form, modules, sitemap
# Funny Stats Project
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import os def manage():
import args, domain, debug, sitemaps if args.commands["targets"]["domain"]:
domain.set_name()
#====================================#
# Manage arguments from command line #
# Specific to action "new" #
#------------------------------------#
def manage(action, target):
do = {
"domain" : create_domain,
"sitemaps" : sitemaps.manage,
}
do[target]()
#===================================#
# From command line "new domain" #
# Create and set only if not exists #
# or if user "force" option
#-----------------------------------#
def create_domain():
# Check if in a domain set yet
d_uri = "/"
dirs = domain.user_dir.rsplit("/")
for d in dirs:
if not d:
continue
d_uri = d_uri + d + "/" if not domain.cf_exists():
if os.path.exists(d_uri + "tyto_domain.ini"): form.ask_domain_creation()
debug.out(202, d, d_uri, True, 2, True)
domain.cf_create() domain.cf_load()
domain.cf_update()
return
domain.is_ready()
if args.commands["modules"]["modules"]:
modules.manage("all")
return
args.commands["modules"]["metas"] and modules.manage("metas")
args.commands["modules"]["header"] and modules.manage("header")
args.commands["modules"]["navbar"] and modules.manage("navbar")
args.commands["modules"]["sidebar"] and modules.manage("sidebar")
args.commands["modules"]["footer"] and modules.manage("footer")
args.commands["modules"]["sitemap"] and sitemap.create()

View File

@ -0,0 +1,219 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Create full HTML page
# File: /var/lib/tyto/program/page.py
#----------------------------------------------------------------------
import os
import domain, post, wip, langs, tools
#========================================#
# Main function to create Full HTML Page #
#----------------------------------------#
def create():
global pub_date, article, local_date
pub_date = tools.nowdate("int-full")
local_date = tools.local_date(post.needed_tags["date"])
# Create specific article metas tags
post_metas = article_metas()
# Create references in div
post_refs = article_refs()
# Create full HTML page
article = template%(
# First line comment
post.needed_tags["title"], domain.conf["title"],
# lang=""
domain.lang,
# nginx metas
nginx_mods%"metas",
# metas
post_metas,
# nginx header, navbar
nginx_mods%"header",
nginx_mods%"navbar",
# <time>
pub_date,
# h1 Main Title link
post.datas["www_url"],
post.needed_tags["title"], post.needed_tags["authors"], local_date,
post.needed_tags["title"],
# article references
post_refs,
# Article contents
wip.html_article,
# nginx sidebar
nginx_mods%"sidebar",
# nginx footer
nginx_mods%"footer",
)
#======================================#
# Create all '<meta...' and '<link...' #
#--------------------------------------#
def article_metas():
post_metas = \
template_metas%(
# From domain
domain.web["www"],
domain.web["lang"],
domain.conf["mail"],
domain.web["license"],
# From article
post.needed_tags["title"],
post.needed_tags["authors"],
post.needed_tags["about"],
post.needed_tags["tags"],
# Canonical, template
post.datas["www_url"],
post.datas["rpa"], domain.cf.get("TEMPLATE_FILENAMES", "styles"),
os.path.splitext(
domain.cf.get("TEMPLATE_FILENAMES", "favicon")
)[1].rsplit(".")[1],
post.datas["rpa"], domain.cf.get("TEMPLATE_FILENAMES", "favicon"),
post.datas["rpa"], domain.cf.get("TEMPLATE_FILENAMES", "rss"),
domain.conf["title"],
# Open Graph
domain.conf["title"],
post.needed_tags["title"],
post.datas["www_url"],
post.needed_tags["about"],
post.needed_tags["logo"],
# Date, <title>
pub_date,
post.needed_tags["title"],
post.needed_tags["authors"],
domain.cf.get("WEBSITE", "separator"),
domain.conf["title"]
)
return post_metas
#========================================#
# Create div inclding article references #
# In template, is put under main title #
#----------------------------------------#
def article_refs():
link_post_code = ""
if domain.conf["post_code"]:
link_post_code = \
' [<a id="post_code" class="%s" href="./%s" title="%s%s %s">%s</a>]'%(
domain.web["css"], post.datas["src_file"],
langs.site.source_code, langs.logs.pp, post.needed_tags["title"],
langs.site.source_code
)
post_refs = template_refs%(
post.needed_tags["authors"], local_date, link_post_code
)
return post_refs
#===============#
# MAIN settings #==============================================================
#---------------#
nginx_mods = '<!--# include virtual="/template/%s.html"-->'
template = """<!-- %s - %s (page generated by Tyto - Littérateur) -->
<!DocType html>
<html lang="%s">
<head>
%s
%s
</head>
<body>
<div id="site_container">
%s
%s
<main id="article_sidebar"> <!-- Contains <article> and <aside> -->
<article id="article">
<time datetime="%s">
<header id="article_header">
<h1 id="article_title">
<a id="article_title_link" href="%s" title="%s -- %s, %s">%s</a>
</h1>
%s
</header>
%s
</time>
</article>
%s
</main>
%s
</div> <!-- #site_container -->
</body>
</html>
"""
template_metas = """<!-- Metas/Links from domain and article -->
<meta name="generator" content="Tyto - Littérateur">
<!-- metas generated from domain -->
<meta name="url" content="%s">
<meta name="language" content="%s">
<meta name="reply-to" content="%s">
<meta name="copyright" content="%s">
<!-- metas generated from article -->
<meta name="title" content="%s">
<meta name="author" content="%s">
<meta name="description" content="%s">
<meta name="keywords" content="%s">
<!-- metas links to template files -->
<link rel="canonical" href="%s">
<link rel="stylesheet" href="%stemplate/%s">
<link rel="shortcut icon" type="image/%s" href="%stemplate/%s">
<link rel="alternate" type="application/rss+xml" href="%s%s" title="RSS 2.0 %s">
<!-- Open Graph data -->
<meta property="og:site_name" content="%s">
<meta property="og:title" content="%s">
<meta property="og:type" content="article">
<meta property="og:url" content="%s">
<meta property="og:description" content="%s">
<meta property="og:image" content="%s">
<!-- Publication date and title -->
<meta itemprop="datePublished" content="%s" id="date">
<title>%s (%s) %s %s</title>"""
# Article referencies (author, date, source code)
template_refs = """<div id="article_refs">
%s, %s%s
</div>"""

View File

@ -14,581 +14,266 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: About post (from target) database, uri... # Description: Manage datas and DB from/for article
# File: /var/lib/tyto/program/new.py # File: /var/lib/tyto/program/post.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#------------------------- import os, configparser
# Funny Stats Project import args, domain, debug, tools
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
import os, sys, configparser
import args, domain, debug, tools, tyto, check
error = 0 #=============================================================#
write = False # When updating database in cf_set(), cf_write() # Set full article file URI from current directory and target #
#-------------------------------------------------------------#
def set_uri(target):
global uri, uid, db, db_uri
if target == "sitemap.tyto" or args.commands["targets"]["all"]:
domain.user_dir = domain.wrk_dirs["articles"]
uri = domain.user_dir + target
uid = tools.get_HID(uri, False)
# DB exists ?
db_uri = domain.wrk_dirs["db"] + uid + ".ini"
db = bool(os.path.exists(db_uri))
#============================================# #=====================================#
# Check if current directory is in articles/ # # Exit if article file does not exist #
# Check if article from target exists # #-------------------------------------#
# Set post configuration file database # def exists(target):
# load database # found = bool(os.path.exists(uri))
#--------------------------------------------# found or debug.out(11, "!?", uri, True, 2, True)
def is_article(target):
global error
debug.out(211, '"%s"'%target, "", False, 0, False)
# User MUST be in articles/
domain.user_dir.startswith(domain.wrk_articles) or \
debug.out(2, "-> articles/", domain.wrk_articles, True, 2, True)
# Target URI most be from legacy directory or not begins with
if target.startswith(tyto.notarget):
error = debug.out(20, "./, ../", target, True, 2, False)
return False
# Article exists
global uri
if args.targets: cur_dir = domain.wrk_articles
else: cur_dir = domain.user_dir
uri = os.path.join(cur_dir, target)
if not os.path.exists(uri):
error = debug.out(5, "False", uri, True, 2, False)
return False
# Article is a Tyto format and not empty (exit on errors)
if not is_tyto_format():
return False
global uri_id, wrk_id, cf_uri, wrk_target, web_target
# Set post ID from...
uri_id = tools.get_filesum(uri, False) # ...URI
wrk_id = tools.get_filesum(uri, True) # ...CONTENTS
# Set post configuration file database
cf_uri = os.path.join(domain.wrk_db, uri_id + ".ini")
# Set target from articles/
wrk_target = uri.rsplit(domain.wrk_articles)[1]
# Set web target, replace last .tyto with .html
web_target = wrk_target[:-4] + "html"
# Load Database, get and compare values
cf_load()
cf_datas()
compare_datas()
return True
#=========================================#
# Article is in Tyto format and not empty #
# Return True or False #
#-----------------------------------------#
def is_tyto_format():
global head_contents, text_contents, contents
global head_lines, text_lines, lines
head_contents = text_contents = ""
separator = False
with open(uri, "r") as contents:
contents = contents.read()
for line in contents.rsplit("\n"):
if not line:
line = " "
if line.startswith(sep):
separator = True
continue
if separator:
if not text_contents: text_contents = line
else: text_contents = "%s\n%s"%(text_contents, line)
else:
if not head_contents: head_contents = line
else: head_contents = "%s\n%s"%(head_contents, line)
if not separator:
error = debug.out(21, sep, uri, True, 2, False)
return False
if not head_contents:
error = debug.out(22, "?", uri, True, 2, False)
return False
if not text_contents:
error = debug.out(23, "?", uri, True, 2, False)
return False
head_lines = len(head_contents.splitlines()) + 1 # Count with sep line
text_lines = len(text_contents.splitlines())
lines = head_lines + text_lines
return True
#=======================================#
# Load post database #
# return True, or False if unused (yet) #
#---------------------------------------#
def cf_load():
global cf, not_chk
cf = not_chk = False
if not os.path.exists(cf_uri):
not_chk = True
tools.create_file(cf_uri, ini_template)
cf = configparser.ConfigParser()
cf.read(cf_uri)
debug.out(200, uri_id, cf_uri, False, 0, False)
return True
#======================================#
# Load another post configuration file #
# Used by modules: navbar, sidebar #
#--------------------------------------#
def tmp_load(module, wrk_post, wrk_post_uri):
global error
db_uri = os.path.join(
domain.wrk_db,
tools.get_filesum(wrk_post_uri, False) + ".ini"
)
if not os.path.exists(db_uri):
error = \
debug.out(11, '%s. "%s"'%(module, wrk_post), db_uri, True, 2, False)
return False
global tmp_cf
tmp_cf = ""
tmp_cf = configparser.ConfigParser()
tmp_cf.read(db_uri)
debug.out(200, '%s. "%s"'%(module, wrk_post), db_uri, False, 0, False)
if tmp_cf.getboolean("CHECK", "errors"):
error = \
debug.out(10, "check: %s"%langs.logs.error, db_uri, True, 2, False)
return False
global tmp_title, tmp_about, tmp_logo
try:
tmp_title = tmp_cf.get("HEADERS", "title")
except:
error = \
debug.out(51, "title:", wrk_post_uri, True, 2, False)
return False
try:
tmp_about = tmp_cf.get("HEADERS", "about")
except:
error = \
debug.out(51, "about:", wrk_post_uri, True, 2, False)
return False
try:
tmp_logo = tmp_cf.get("HEADERS", "logo")
except:
error = \
debug.out(51, "logo:", wrk_post_uri, True, 2, False)
return False
return True
#=======================================#
# Load in tmp mode db post for sitemaps #
#---------------------------------------#
def tmp_load_db(article_uri):
global tmp_db, tmp_db_error
tmp_db_error = False
target = article_uri.rsplit(domain.wrk_articles)[1]
db_uri = domain.wrk_db + tools.get_filesum(article_uri, False) + ".ini"
if not os.path.exists(db_uri):
debug.out(11, 'Sitemap. "%s"'%target, db_uri, False, 0, False)
return False
tmp_db = ""
tmp_db = configparser.ConfigParser()
tmp_db.read(db_uri)
debug.out(200, 'Sitemap. "%s"'%target, db_uri, False, 0, False)
if not tmp_db_item("HEADERS", "sitemap", True):
tmp_db_error = True
return True
# Must be published to be included in sitemap
www_uri = tmp_db_item("WWW", "uri", False)
if not tmp_db_item("WWW", "hash", False):
tmp_db_error = True
debug.out(13, 'Sitemap. "%s"'%target, www_uri, False, 1, False)
return True
# Article is not in www server
if not os.path.exists(www_uri):
debug.out(5, 'Sitemap. "www/"', www_uri, False, 1, False)
tmp_db_error = True
return True
#===============================================#
# Get DB value (for sitemap, with tmp_load_db() #
#-----------------------------------------------#
def tmp_db_item(section, key, boolean):
if boolean:
try: return tmp_db.getboolean(section, key)
except: return False
try: return tmp_db.get(section, key)
except: return ""
#================================# #================================#
# Return value from section, key # # Create datas from article file #
#--------------------------------# #--------------------------------#
def cf_get(section, key, boolean): def set_datas():
if boolean: global datas
try: return cf.getboolean(section, key)
except: return False
try: return cf.get(section, key) src_target = uri.rsplit(domain.wrk_dirs["articles"])[1]
except: return "" srv_target = src_target[:-4] + "html"
src_file = os.path.basename(uri)
dir_target = src_target.replace(src_file, "")
# Settings
datas = \
{
"cid" : tools.get_HID(uri, True),
"src_target" : src_target,
"srv_target" : srv_target,
"src_file" : src_file,
"src_rpa" : "/" + src_target,
"web_rpa" : "/" + srv_target,
"www_url" : domain.web["www"] + src_target[:-4] + "html",
"dir_target" : dir_target,
"rpa" : get_rpa(),
# URIS format, "files" : {f_nbr : (full_uri, web_uri)}
"files" : {},
}
#=============================================# #=====================================================#
# Get and prepare new vars from post database # # Return relative path for files from root, like "./" #
#---------------------------------------------# #-----------------------------------------------------#
def cf_datas(): def get_rpa():
# [CHECK] src_link = "/" + uri.rsplit(domain.wrk_dirs["articles"])[1]
# ------- rpa = src_link.count("/")
global chk_hash, chk_date, chk_static, chk_errors
chk_hash = cf_get("CHECK", "hash", False) if rpa > 1: rpa = rpa -1 ; rpa = rpa * "../"
chk_date = cf_get("CHECK", "date", False) else: rpa = "./"
chk_static = cf_get("CHECK", "static", True)
chk_errors = cf_get("CHECK", "errors", True)
# [WIP] return rpa
# -----
global wip_hash, wip_date, wip_uri, wip_static
wip_hash = cf_get("WIP", "hash", False)
wip_date = cf_get("WIP", "date", False)
wip_uri = cf_get("WIP", "uri", False)
wip_static = cf_get("WIP", "static", True)
# [WIP]
# -----
global www_hash, www_date, www_uri, www_static
www_hash = cf_get("WWW", "hash", False)
www_date = cf_get("WWW", "date", False)
www_uri = cf_get("WWW", "uri", False)
www_static = cf_get("WWW", "static", True)
global set_title, set_about, set_date, set_tags, set_author
set_title = cf_get("HEADERS", "title", False)
set_about = cf_get("HEADERS", "about", False)
set_date = cf_get("HEADERS", "date", False)
set_tags = cf_get("HEADERS", "tags", False)
set_author = cf_get("HEADERS", "authors", False)
global sub_uri, www_logo
sub_uri = cf_get("FILE", "sub_uri", False)
www_logo = cf_get("HEADERS", "logo", False)
#===============================#
# Do some datas comparisons #
# after post cf_datas() setup #
# Used to do processes (mainly) #
#-------------------------------#
def compare_datas():
global do_chk, do_wip, has_changed
# check can be done ?
has_changed = False
if wrk_id != chk_hash:
has_changed = True
do_chk = False
if chk_errors or \
chk_static != domain.static or \
args.force == True:
do_chk = True
else:
do_chk = tools.compare_values(wrk_id, chk_hash)
# wip can be done
do_wip = False
if wip_static != domain.static or \
args.force:
do_wip = True
else:
do_wip = tools.compare_values(chk_hash, wip_hash)
#===============================#
# Set a value in post database #
# only if changed value #
# check for [section] in file #
# or create it with key and val #
# config file must be loaded #
#-------------------------------#
def cf_set(section, key, value):
global write
try: cf
except: cf_load()
if not cf.has_section(section):
cf.add_section(section)
try: curval = cf.get(section, key)
except: curval = ""
if not curval or curval != value:
cf.set(section, key, value)
write = True
#=====================#
# Write post database #
# if new value is set #
#---------------------#
def cf_write():
global write
if write:
with open(cf_uri, "w") as f:
cf.write(f)
debug.out(207, uri_id, cf_uri, False, 0, False)
write = False
#====================================================#
# Search and return .tyto file in domain root folder #
#----------------------------------------------------#
def find_tyto_article():
nothere = (domain.wrk_files, domain.wrk_images)
os.chdir(domain.wrk_articles)
for root, dirs, files in os.walk(domain.wrk_articles):
if root.startswith(nothere):
continue
for f in files:
if f.endswith(".tyto"):
f_uri = os.path.join(root, f)
target = f_uri.rsplit(domain.wrk_articles)[1]
args.action == "check" and check.is_article(target)
#======#
# MAIN #=======================================================================
#======#
# Statistics
# ==========
stats_bcodes_lines = 0
stats_bcodes = 0
stats_quotes = 0
stats_parags = 0
stats_lists = 0
stats_divs = 0
stats_links = 0
stats_images = 0
stats_files = 0
stats_raws = 0
stats_codes = 0
stats_abbrs = 0
stats_text_links = 0
stats_text_files = 0
stats_text_images = 0
stats_text_abbrs = 0
stats_text_codes = 0
stats_text_raws = 0
# head_contents
#==============
# Optional Tags
nositemap = "! NoSitemap" # Article will not be included in sitemap
# One Line needed
sep = "-----" # Splitter between header and article texts
# Will replace "False" with data value (check process)
title = ("title:", False)
about = ("about:", False)
date = ("date:", False)
tags = ("tags:", False)
author = ("author:", False)
logo = ("logo:", False) # optional
# Multiple lines (3) markers
ml_tags = (
"link:",
"image:",
"file:",
"raw:",
"code:",
"abbr:"
)
ml_tags_marks = {
ml_tags[0] : "__",
ml_tags[2] : "--",
ml_tags[1] : "_image:",
ml_tags[5] : "::",
ml_tags[3] : "_raw:",
ml_tags[4] : "_code:"
}
ml_tags_stats = {
ml_tags[0] : stats_links,
ml_tags[2] : stats_files,
ml_tags[1] : stats_images,
ml_tags[5] : stats_abbrs,
ml_tags[3] : stats_raws,
ml_tags[4] : stats_codes,
}
# Markers with uri in value2
value2s_uri = (ml_tags[1], ml_tags[2], ml_tags[3], ml_tags[4])
value2s_ext_uris = ("http", "ftp")
# text_contents
# =============
# Paired markers
ptags = (
("{{", "}}", "bcodes"),
('["', '"]', "quotes"),
("((", "))", "parags", '<p class="%s">', "</p>"),
("<:", ":>", "lists", "=", "+"),
("[[", "]]", "divs", '<div class="%s">', "</div>")
)
ptags_stats = {
ptags[0][2] : stats_bcodes,
ptags[1][2] : stats_quotes,
ptags[2][2] : stats_parags,
ptags[3][2] : stats_lists,
ptags[4][2] : stats_divs,
}
# Tyto Titles #1 = <h2>
tyto_titles = ("#1", "#2", "#3", "#4", "#5")
html_titles = {
"#1" : '<h2 class="%s">%s</h2>',
"#2" : '<h3 class="%s">%s</h3>',
"#3" : '<h4 class="%s">%s</h4>',
"#4" : '<h5 class="%s">%s</h5>',
"#5" : '<h6 class="%s">%s</h6>',
}
html_brline = ("|", '<br class="%s">')
html_hrline = ("--", '<hr class="%s">')
text_comments = (";;", "<!--")
anchor_target = ("->", '<a id="%s" class="anchor_target"></a>')
anchor_link = (">_", "_<")
anchor_set = (">_%s_<", '<a class="%s anchor_link" href="#%s">%s</a>')
quote_metas = ("cite:", "date:", "book:", "lang:", "link:")
# Words tags
words_tags = (
("*_", "_*", "strongs", '<strong class="%s">', '</strong>'),
("+_", "_+", "bolds", '<b class="%s">', '</b>'),
("[_", "_]", "cites", '<q class="%s">', '</q>'),
(":_", "_:", "refs", '<cite class="%s">', '</cite>'),
("~_", "_~", "dels", '<del class="%s">', '</del>'),
("._", "_.", "underlines", '<u class="%s">', '</u>'),
("/_", "_/", "emphasis", '<em class="%s">', '</em>'),
(";_", "_;", "italics", '<i class="%s">', '</i>'),
)
words_ml_tag = "&"
# Specifics convertion
words_markers = \
(
("{_{_", "_}_}", '<code class="%s icode">{_', '_}</code>', "IC21", "IC22"),
("{_", "_}", '<code class="%s icode">', '</code>', "IC11", "IC12"),
)
#=============================# #=============================#
# articles configuration file # # Read article DB (if exists) #
#-----------------------------# #-----------------------------#
ini_template = """[DOMAIN] def cf_load():
global cf
[FILE] cf = configparser.ConfigParser()
cf.read(db_uri)
[HEADERS]
[TEXTS] #==============================================#
# Create sections, keys, values for article DB #
# Called after wip, published #
#----------------------------------------------#
def cf_set(section, key, value):
global cf
[CHECK] # Add section if not exists
try: cf.add_section(section)
except: pass
[WIP] cf.set(section, key, value)
[WWW]
[COMMENTS] #
#
#
def cf_read():
global hash_www, date_www
[TITLES] try: hash_www = cf.get("HASHES", "www")
except: hash_www = ""
try: date_www = cf.get("DATES", "www")
except: date_www = ""
# Remove to create a new DB
try: os.remove(db_uri)
except: return
[ANCHORS] #
# Write new Article DB
#
def cf_write():
with open(db_uri, "w") as f:
cf.write(f)
[LINKS]
[FILES] #==============================#
# Create new article DB file #
#------------------------------#
def cf_create():
# Create new DB
tools.create_file(db_uri, "")
cf_load()
cf_set("URIS", "wrk", uri)
cf_set("URIS", "wip", domain.wip + datas["srv_target"])
cf_set("URIS", "www", domain.www + datas["srv_target"])
cf_set("URIS", "web", datas["web_rpa"].replace("index.html", ""))
cf_set("HASHES", "www", hash_www)
cf_set("DATES", "www", date_www)
for tag in needed_tags:
cf_set("ARTICLE", tag, needed_tags[tag])
cf_set("ARTICLE", "local_date", tools.local_date(needed_tags["date"]))
cf_set("ARTICLE", "www_url", datas["www_url"])
cf_set("MODULES", "nomap", str(options_marks["! NOMAP"]))
cf_set("MODULES", "norss", str(options_marks["! NORSS"]))
cf_set("MODULES", "toc", str(options_marks["! TOC"]))
cf_write()
[IMAGES]
[BCODES] #
#
#
def set_default_vars():
global anchors, block_tags, icodes, options_marks
global needed_tags, option_tags, titles
[QUOTES] # form NBR : (source, html)
anchors = {}
# Put in blocks source lines
block_tags = \
{
"bcodes" : {
"marks" : ("{{", "}}"),
"sources" : {}, # {} : {NBR : ("SOURCE", "B64")}
},
"bquotes" : {
"marks" : ('("', ')"'),
"sources" : {}, # {} : {NBR : ("SOURCE", "B64")}
},
"lists" : {
"marks" : ("(=", ")=", "=", "+"),
"sources" : {}, # {} : {NBR : ("SOURCE", "B64")}
},
}
# Inline codes
icodes = {
"sources" : {}, # {} : {NBR : ("SOURCE", "B64")}
}
# Marks to cinfigure article
options_marks = {
"! NOMAP" : False,
"! NORSS" : False,
"! TOC" : False,
}
# Tags that must be set in header
needed_tags = {
"title" : "",
"about" : "",
"date" : "",
"tags" : "",
"authors" : "",
"logo" : "",
}
# Tags on 3 lines and optional
# Format for each to set "..." : {NBR : ("F1", "F2", "F3", "HTML")}
option_tags = {
"link" : {},
"file" : {},
"image" : {},
"code" : {},
"raw" : {},
"abbr" : {},
}
titles = ()
[LISTS]
[RAWS] #===============#
# MAIN settings #==============================================================
#---------------#
# --------------------------- #
# markers and tags in article #
# --------------------------- #
# Delimitor for header and writer contents
separator = "-----"
[CODES] # 2nd line tag is not an URI file to check
nofile_tags = ("link", "abbr")
[ABBRS]
[ICODES] # Markers used in article writer for optional tags
# HTML
marker_tags = "::%s"
[SOURCE_FILES] html_comments = ";;"
[STATS_FILE] indep_tags = {
"hrs" : "--",
"brs" : "|",
"anchors" : "->",
}
[STATS_HEADERS] # Starting line and paired tags
sl_ptags = {
"divs" : ("[[", "]]"),
"paragraphs" : ("((", "))"),
}
words_tags = {
"strongs" : ("*`", "`*"),
"bolds" : ("+`", "`+"),
"underlines" : ("_`", "`_"),
"emphasis" : ("/`", "`/"),
"italics" : (";`", "`;"),
"quotes" : ("[`", "`]"),
"cites" : (":`", "`:"),
"dels" : ("~`", "`~"),
"customs" : ("|`", "`|"),
"icodes" : ("{`", "`}"),
"anc_links" : (">`", "`<"),
}
title_marks = ("#1", "#2", "#3", "#4", "#5") # h2 to h6
[STATS_TEXTS]
"""

View File

@ -0,0 +1,178 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Manage publishing
# (mainly copies from wip server)
# Launch feed and sitemap creation
# File: /var/lib/tyto/program/publish.py
#----------------------------------------------------------------------
import os
import domain, args, wip, post, debug, tools, sitemap, feed
#========================================================#
# From command "publish", copy wip article to www server #
#--------------------------------------------------------#
def manage():
domain.is_ready()
global out
if args.article:
wip.in_dir_articles()
# Multiple targets ("all"): out is False
# (out to exit on error at single target)
out = not(args.targets)
article()
# Copy sitemap if activated and exists
if domain.conf["sitemaps"]:
sitemap.create()
args.article == "sitemap.tyto"
article()
feed.create()
tools.copy_template_dir("www")
#==============================#
# Set DB, check and copy files #
#------------------------------#
def article():
post.set_uri(args.article)
post.exists(args.article)
db_uri = domain.wrk_dirs["db"] + post.uid + ".ini"
if not bool(os.path.exists(db_uri)):
debug.out(12, "wip %s"%args.article, post.uid, True, 2, out)
post.cf_load()
if not check_db():
debug.out(12, "wip %s"%args.article, post.uri, True, 2, out)
copy_wip_to_www()
post.cf_set("HASHES", "www", DB["hash_wip"])
post.cf_set("DATES", "www", pub_date)
post.cf_write()
debug.out(250, DB["article_title"], DB["uri_www"], True, 0, False)
#===========================
# Check needed Datas in DB #
# Return True or False #
#--------------------------#
def check_db():
global DB
try:
DB = {
"uri_wrk" : post.cf.get("URIS", "wrk"),
"uri_wip" : post.cf.get("URIS", "wip"),
"uri_www" : post.cf.get("URIS", "www"),
"uri_web" : post.cf.get("URIS", "web"),
"article_title" : post.cf.get("ARTICLE", "title"),
"hash_wip" : post.cf.get("HASHES", "wip"),
}
except:
return False
for item in DB:
if not DB[item]:
return False
if not os.path.exists(DB["uri_wip"]):
debug.out(11, "!?", db["uri_wip", True, 2, out])
# Check if files exists in wip directory
try:
error = False
for key, value in post.cf.items("FILES"):
if not os.path.exists(domain.wip + value):
debug.out(11, "!?", domain.wip + value, True, 2, False)
error = True
except:
error = False
if error:
return False
# Ensure Article source code is in wip directory if set in config domain
if domain.conf["post_code"]:
global wip_post_src
wip_post_src = \
DB["uri_wrk"].replace(domain.wrk_dirs["articles"], domain.wip)
if not os.path.exists(wip_post_src):
debug.out(11, "!?", wip_post_src, True, 2, out)
return False
return True
#===============================================#
# Update article contents with new publish date #
# Laucn from copu_wip_to_www, before file copy #
#-----------------------------------------------#
def update_article():
global pub_date
pub_date = tools.nowdate("int-full")
meta_date = '<meta itemprop="datePublished" content="%s" id="date">'%pub_date
time_date = '<time datetime="%s">'%pub_date
with open(DB["uri_wip"], "r") as f:
www_article = f.read().rsplit("\n")
for ln, line in enumerate(www_article):
if line.lstrip().startswith('<meta itemprop="datePublished"'):
www_article[ln] = meta_date
elif line.startswith('<time datetime="'):
www_article[ln] = time_date
# <time datetime is the last item to change
break
with open(DB["uri_www"], 'w') as f:
for line in www_article:
# write each item on a new line
f.write("%s\n"%line)
#=============================================#
# Copy all needed article files to www server #
#---------------------------------------------#
def copy_wip_to_www():
# Copy needed files used by article
try:
for key, value in post.cf.items("FILES"):
# Extract directories and create them
tools.create_dirs(domain.www + value.rsplit(os.path.basename(value))[0])
tools.copy_files(domain.wip + value, domain.www + value)
except:
pass
# Create article contents file to server
tools.create_dirs(DB["uri_www"].rsplit(os.path.basename(DB["uri_www"]))[0])
update_article()
# Copy Article source code in wip directory, if set in domain configuration
if domain.conf["post_code"]:
www_post_src = \
DB["uri_wrk"].replace(domain.wrk_dirs["articles"], domain.www)
tools.copy_files(wip_post_src, www_post_src)

View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re
#
# Description: Create sitemap
# File: /var/lib/tyto/program/sitemap.py
#----------------------------------------------------------------------
import os, configparser, glob
import args, domain, tools, langs, wip
#======================================#
# Loop from all articles DB #
# Check if article is ready to include #
#--------------------------------------#
def create():
if not domain.conf["sitemaps"]:
return
os.chdir(domain.wrk_dirs["articles"])
db_dir = domain.wrk_dirs["db"]
# Scan DB directory and sort files by last updated
items = filter(os.path.isfile, glob.glob(db_dir + '*.ini'))
items = sorted(items, key = os.path.getmtime, reverse=True)
list_items = ""
nbr = 0
for db_uri in items:
if not check_db(db_uri):
continue
nbr += 1
ol_item = link%(
domain.web["css"],
DB["uri_web"],
# title=""
DB["about"], DB["authors"], DB["date"],
# Content link
DB["title"]
)
if not list_items: list_items = "%s"%ol_item
else: list_items = "%s\n%s"%(list_items, ol_item)
if not list_items:
list_items = " = %s"%langs.site.sitemap_empty
sitemap_tyto = \
sitemap_post%(
langs.site.sitemap,
langs.site.sitemap_gen,
langs.site.sitemap,
tools.nowdate("int-short"),
langs.site.posts_list, nbr,
list_items
)
tools.create_file(domain.wrk_dirs["articles"] + "sitemap.tyto", sitemap_tyto)
args.article = "sitemap.tyto"
wip.article()
#===============================#
# Check/Set article DB #
# Check if article can be added #
#-------------------------------#
def check_db(db_uri):
global DB
cf = configparser.ConfigParser()
cf.read(db_uri)
try:
DB = {
"uri_web" : cf.get("URIS", "web"),
"title" : cf.get("ARTICLE", "title"),
"about" : cf.get("ARTICLE", "about"),
"date" : cf.get("ARTICLE", "local_date"),
"authors" : cf.get("ARTICLE", "authors"),
"hash_www" : cf.get("HASHES", "www"),
"nomap" : cf.getboolean("MODULES", "nomap"),
}
except:
return False
if not DB["hash_www"] or DB["nomap"]:
return False
return True
#======#
# Main #=======================================================================
#======#
# Template sitemap.tyto with lists (pages links, posts links)
sitemap_post = """# Tyto - Littérateur
! NOMAP
! NORSS
title: %s
about: %s Tyto - Littérateur
tags: %s
authors: Tyto
date: %s
-----
#1 %s (%s)
(( sitemap
(= sitemap_items
%s
)=
))
"""
# Generic list item with HTML link for sitemap
link = ' + <a class="%s sitemap" href="%s" title="%s -- %s, %s">%s</a>'

View File

@ -14,97 +14,79 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>..
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# XMPP: echolib > im@echolib.re # XMPP: echolib (im@echolib.re)
# #
# Description: Tools used by Tyto - Littérateur # Description: Some tools
# File: /var/lib/tyto/program/tools.py # File: /var/lib/tyto/program/tools.py
#---------------------------------------------------------------------- #----------------------------------------------------------------------
#------------------------- import os, datetime, shutil, glob
# Funny Stats Project
#-------------------------
# file lines :
# file comments :
# file functions:
# file program :
#--------------------------
from hashlib import blake2b from hashlib import blake2b
import sys, os, configparser, datetime, time, base64, shutil import args, debug, domain, post, wip
import args, langs, debug, domain, post
#=========================================#
# Write to post database, error was found #
# Return if error found (multi articles) #
# Exit if error found (single article) #
#-----------------------------------------#
def exit(targets, error):
post.cf_set("CHECK", "errors", "True")
post.cf_write()
if targets:
return
sys.exit(error)
#==============================# #==============================#
# Set and return date and time # # Set and return date and time #
# short: True > Y-m-d
#------------------------------# #------------------------------#
def nowdate(): def nowdate(fmt):
now = datetime.datetime.now() formats = {
return(now.strftime('%Y-%m-%d %H:%M:%S')) "int-full" : "%Y-%m-%d %H:%M:%S",
"int-short" : "%Y-%m-%d",
"year" : "%Y",
"feed" : "%a, %d %b %Y %H:%M:%S",
}
try: formats[fmt]
except: fmt = "int-full"
now = datetime.datetime.now()
return(now.strftime(formats[fmt]))
# #==========================#
# convert int date to local date # Return local date format #
# Return date (only year) for post database #--------------------------#
#
def local_date(date): def local_date(date):
date = date.rsplit(" ")[0] # if nowdate() if domain.lang == "en":
return date
year = date.rsplit("-")[0] local_dates = {
month = date.rsplit("-")[1] "fr" : "%s/%s/%s",
day = date.rsplit("-")[2] }
dates = { y = date.rsplit("-")[0]
"fr" : "%s/%s/%s"%(day, month, year), m = date.rsplit("-")[1]
"en" : date, d = date.rsplit("-")[2]
}
return local_dates[domain.lang]%(d,m,y)
return dates[domain.lang]
#========================# #=================================#
# Return sum of src file # # Return sum of src file #
# src: True = Content # # file: True = Contents file ID #
# False = URI # # False = string ID #
#------------------------# #---------------------------------#
def get_filesum(path, src): def get_HID(string, file):
file_sum = blake2b(digest_size=4) HID = blake2b(digest_size=4)
if src: file_sum.update(open(path, 'rb').read()) if file: HID.update(open(string, 'rb').read())
else: file_sum.update(path.encode()) else: HID.update(string.encode())
return file_sum.hexdigest() return HID.hexdigest()
#========================================# #================================#
# Check directory: exit if out and log # # 2 values generic comparison #
# Mainly used to check domain server dir # # return True if values are same #
#----------------------------------------# #--------------------------------#
def dir_exists(dir_path, out): def same_values(a, b):
if not bool(os.path.exists(dir_path)): return bool(a == b)
debug.out(6, "False", dir_path, out, 2, out)
return False
return True
#====================# #====================#
# Create directories # # Create directories #
#--------------------# #--------------------#
@ -112,93 +94,28 @@ def create_dirs(path):
if not os.path.exists(path): if not os.path.exists(path):
try: try:
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
debug.out(203, langs.logs.success, path, True, 0, False) debug.out(207, "Ok", path, False, 0, False)
except: except:
# Exit if not created # Exit if not created
debug.out(6, langs.logs.error, path, True, 2, True) debug.out(7, "!?", path, False, 2, True)
#============================# #==============================#
# Create a new file and logs # # Create a new file and log it #
#----------------------------# #------------------------------#
def create_file(file_path, contents): def create_file(path, contents):
up = bool(os.path.exists(file_path)) up = bool(os.path.exists(path))
try: try:
with open(file_path, "w") as f: with open(path, "w") as f:
f.write(contents) f.write(contents)
except: except:
# Exit if not created debug.out(5, "!?", path, True, 2, True)
debug.out(7, langs.logs.error, file_path, True, 2, True)
# log "update" or "new" # log "update" or "new"
file_name = os.path.basename(file_path) file_name = os.path.basename(path)
if up: debug.out(207, file_name, file_path, False, 0, False) if up: debug.out(206, file_name, path, False, 0, False)
else: debug.out(206, file_name, file_path, True, 0, False) else: debug.out(205, file_name, path, False, 0, False)
#============#
# Copy files #
#------------#
def copy_files(src, dst):
# Copy file, check if dst exists
up = bool(os.path.exists(dst))
try:
shutil.copy2(src, dst, follow_symlinks=False)
except:
debug.out(7, langs.logs.copy, dst, True, 2, False)
return
# log "update" or "new"
file_name = os.path.basename(src)
if up: debug.out(207, file_name, dst, False, 0, False)
else: debug.out(206, file_name, dst, True, 0, False)
#===========================================#
# Update ini file, replacing existing value #
#-------------------------------------------#
def update_ini_file(file_path, section, key, val):
# Exit if no file
os.path.exists(file_path) or debug.out(5, "False", file_path, True, 2, True)
# Load ini file
config = configparser.ConfigParser()
config.read(file_path)
# New value is same as registred
try:
if config.get(section, key) == val:
return
except:
config.add_section(section)
# Update file with new value
config.set(section, key, val)
with open(file_path, "w") as f:
config.write(f)
#====================#
# Base64 Convertions #
#--------------------#
def b64_convert(action, content):
if action == 'encode':
global b64_content
b64_base64 = ''
content_bytes = content.encode("utf8")
base64_bytes = base64.b64encode(content_bytes)
b64_content = base64_bytes.decode("utf8")
return b64_content
elif action == 'decode':
global src_content
src_content = ''
content_bytes = content.encode("utf8")
base64_bytes = base64.b64decode(content_bytes)
src_content = base64_bytes.decode("utf8")
return src_content
#================================# #================================#
@ -218,36 +135,104 @@ def convert_html_signs(string):
#==========================# #==========================#
# Get CSS value after mark # # Get CSS value after mark #
# Only take the first name # # Only take the first name #
# Exit all process if _... #
#--------------------------# #--------------------------#
def get_css(line, mark, ln): def get_css(line, mark):
css = line.rsplit(mark)[1].lstrip().rsplit(" ")[0] css = line.rsplit(mark)[1].lstrip().rsplit(" ")[0] or domain.web["css"]
css = css or domain.css
# Tyto use _abc as markers
if "_" in css:
post.error = \
debug.out(56, '%s) (CSS) "_" : "%s"'%(ln, css), post.uri, True, 2, False)
return "NameError"
return css return css
#==========================================# #================================================#
# Compare 2 values # # At wip process, only, copy files to wip server #
# used for hashes and more... # # At publish, files are copied, not generated #
# return True if not same values # #------------------------------------------------#
# ex: do_chk = True # def copy_to_srv(article):
# (as chk_hash and wip_hash are different) # # Copy needed files used by article
#------------------------------------------# try:
def compare_values(val1, val2): for key, value in post.cf.items("FILES"):
# Mainly for check comparison values # Extract directories and create them
if args.action == "check": dirs = domain.wip + value.rsplit(os.path.basename(value))[0]
if val1 != val2: create_dirs(dirs)
return True copy_files(
domain.wrk_dirs["articles"] + value,
domain.wip + value
)
except:
pass
# Create article contents file to server
wip_uri = post.cf.get("URIS", "wip")
dirs = wip_uri.rsplit(os.path.basename(wip_uri))[0]
create_dirs(dirs)
create_file(post.cf.get("URIS", "wip"), article)
# Copy Article source code if set in domain configuration
if domain.conf["post_code"]:
copy_files(
post.uri,
post.uri.replace(domain.wrk_dirs["articles"], domain.wip)
)
#====================#
# Copy file with log #
#--------------------#
def copy_files(src, dst):
try:
shutil.copy2(src, dst)
debug.out(205, os.path.basename(src), dst, False, 0, False)
except:
debug.out(5, os.path.basename(src), dst, True, 2, True)
#
#
#
def copy_template_dir(process):
src_dirs = {
"wip" : domain.wrk_dirs["template"],
"www" : domain.wip_dirs["template"],
}
dst_dirs = {
"wip" : domain.wip_dirs["template"],
"www" : domain.www_dirs["template"],
}
# Others, like wip, publish create_dirs(dst_dirs[process])
else: shutil.copytree(src_dirs[process], dst_dirs[process], dirs_exist_ok=True)
if val1 == val2: return True
return False
#====================================================#
# Sorte dict by length name #
# To avoid a bug with replace for similar tags names #
# Mostly used with wip processes #
# codes, raws, images #
# Return new sorted dict #
#----------------------------------------------------#
def sort_dict(d):
set_dict = {}
for nbr in d:
set_dict[d[nbr][0]] = d[nbr]
list_dict = list(set_dict)
return(set_dict, sorted(list_dict, reverse=True))
#============================#
# With "all" in command line #
# Search for all .tyto files #
# in work domain articles/ #
# start wip process for each #
#----------------------------#
def get_articles():
root_dir = domain.wrk_dirs["articles"]
files = filter(os.path.isfile,
glob.glob(root_dir + '**/*.tyto', recursive=True)
)
for uri in files:
args.article = uri.rsplit(root_dir)[1]
wip.article()

File diff suppressed because it is too large Load Diff

View File

@ -36,121 +36,73 @@ ok = ("yes", "y")
# Form # Form
q = "?" q = "?"
pp = ":" pp = ":"
error = "Error"
success = "Success"
configure_domain = "Configure domain"
domain = "Domain"
domain_title = "Domain title"
domain_date = "Creation date"
domain_about = "Domain description"
domain_mail = "Admin mail"
domain_tags = "Domain tags [1,2,3]"
domain_lang = "Website lang"
domain_srv = "Server URI"
copy = "Copy"
create_sitemaps = "Creating sitemaps"
completed = "Completed"
# Misc # Misc
anchor_title = "Anchor title" error = "Error"
# logs for debug # logs for debug
#--------------- #---------------
# Errors # Errors
err_arg = "Argument error" err_in_hole = "Current directory deleted"
err_hole = "Current directory error" err_process = "Process error"
err_date = "Date error" err_argument = "Argument error"
err_lang = "Lang error" err_domain_name = "Domain name error"
err_dir = "Directory error" err_domain_dir = "Domain name directory error"
err_no_dir = "Directory unused" err_kbd_stop = "Keyboard interrupt"
err_cd = "Directory not created" err_file_create = "File not created"
err_no_file = "File unused" err_file_no = "Unused file"
err_cr_file = "File not created" err_dir_create = "Directory not created"
err_bad_uri = "URI error" err_dir_unused = "Unused Directory"
err_post_sep = "Separator unused" err_dir_in = "Not in needed directory"
err_post_head = "Header is empty" err_lang_no = "Unavailable language"
err_post_empty = "Article is empty" err_post_sep = "Unused separator"
err_ini_file = "Configuration error" err_post_header_no = "Empty header"
err_post_data = "Data unused" err_post_writer_no = "Empty article"
err_post_title = "Title error" err_post_tag_set = "Unused datas"
err_post_paired = "Markers not paired" err_post_tag_val = "Data error"
err_post_in_tag = "Markers without contents" err_post_tag_id = "Identity set yet"
err_post_datatag = "Data reserved" err_post_mark_no = "Mark not found"
err_post_id_yet = "ID used yet" err_post_mark_stop = "Mark not closed"
err_post_global = "Article Error" err_post_mark_miss = "Missing Mark?"
err_post_not_chk = "Article not checked" err_post_db = "Article not configured"
err_post_not_www = "Article not published" err_post_srv_no = "Article not in server"
# Warnings # Warnings
warn_no_dom = "Domain not configured" warn_domain_no = "Domain not configured"
domain_off = "Domain deactivated" warn_domain_conf = "Bad domain configuration"
reset_dom = "RESET domain" warn_date_format = "Date format"
warn_post_chk = "Article changed" warn_post_tupic = "Parameters format"
warn_maybe_later = "Maybe later..."
# infos # infos
load_file = "File loaded" inf_dir_user = "Starting Tyto"
lang_logs_sys = "Logs lang" inf_lang_logs = "Logs language"
website_lang = "Website lang" inf_domain_load = "Domain loaded"
domains_no = "Domain not found" inf_domain_conf = "Configuring domain"
domain_found = "Domain exists" inf_file_upd = "File updated"
domain_on = "Domain activated" inf_file_create = "File created"
domain_created = "Domain updated yet" inf_dir_create = "Directory created"
domain_updated = "Domain updated" inf_check_post = "Checking article"
domain_new = "Domain created" inf_wip_process = "Converting"
created_dir = "Directory created" inf_mod_process = "Checking modules"
created_file = "File created" inf_end_process = "Tyto!"
updated_file = "File Updated"
reading_domain = "Preparing domain"
processing = "Process"
checking_post = "Checking article"
wipping_post = "Converting article"
post_chk_yet = "Article checked yet"
post_chk_ready = "Article checked"
later = "Maybe later..."
# # Form
# Show HELP ask_domain_name = "Configure domain"
# ask_domain_title = "Domain title"
help_contents = """ ask_domain_date = "Domain creation date"
tyto [action] [target] [options] ask_domain_about = "Domain description"
[action] ask_domain_tags = "Domain global tags"
new : create new domain (reset with -F) ask_domain_mail = "Administrator mail"
set : [server, date, title, mail, footer, header, ...] ask_website_lang = "Website language"
start : activate domain (default "no" when created) ask_srv_root = "Server root directory"
stop : deactivate domain ask_reset_modules = "Reset all modules"
show : [domains, metas]
check : [domain, wip, www, [name].tyto]
wip : [name].tyto: create HTML page in "wip/" server
publish :
[target] # Modules
domain : all about current domain home metas_raw = """
domains : list all registred domains
title : [set]: domain title
date : [set]: domain creation date
about : [set]: domain description
mail : [set]: domain admin mail
tags : [set]: domain tags (added to all articles)
lang : [set]: Website language
server : [set]: server local URI
footer : [set]: Create default module (also header...)
wip : [check] Directories and files
www : [check] Directories and files
[options] ; multi-set
--force, -F : force doing things...
--debug, -D : show more logs
--errors, -E : Show mainly warnings and errors logs
"""
#
# Modules (metas, navbar, sidebar...)
#
metas_header = """#
#=========================================================# #=========================================================#
# HTML Metas cnfiguration file used by Tyto - Littérateur # # HTML Metas cnfiguration file used by Tyto - Littérateur #
#---------------------------------------------------------# #---------------------------------------------------------#
#
# Domain Name : %s # Domain Name : %s
# Domain URI : %s # Domain URI : %s
@ -167,18 +119,31 @@ metas_header = """#
# ? Type "tyto show metas" to show all others to be added # ? Type "tyto show metas" to show all others to be added
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
<meta charset="UTF-8">
<meta name="robots" content="all">
<meta name="medium" content="website">
<meta name="revisit-after" content="3 days">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
""" """
navbar_header = """# header_raw = """
#======================================================#
# Header configuration file used by Tyto - Littérateur #
#------------------------------------------------------#
# Domain Name : %s
# Domain URI : %s
# WRK : %s
# WIP : %s
# WWW : %s
# How to configure this file:
# - Edit, replace HTML code
#------------------------------------------------------------------------------
"""
navbar_raw = """
#=====================================================# #=====================================================#
# Navbar cnfiguration file used by Tyto - Littérateur # # Navbar cnfiguration file used by Tyto - Littérateur #
#-----------------------------------------------------# #-----------------------------------------------------#
#
# Domain Name : %s # Domain Name : %s
# Domain URI : %s # Domain URI : %s
@ -189,32 +154,29 @@ navbar_header = """#
# How to configure this file: # How to configure this file:
# - Add one directory URI per line # - Add one directory URI per line
# - - URI is from domain directory articles/ # - - URI is from domain directory articles/
# ! Must contains an article "index.tyto" ready # ! Server directory must contains an article "index.html"
# Option:
# - Default link name : directory
# - Add "# Link Name" to change it
# Examples: # Examples:
# news ! # news
# about/website # about/website
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
""" """
sidebar_header = """# sidebar_raw = """
#========================================================================# #=======================================================#
# Fichier de config de la barre latérale utilisée par Tyto - Littérateur # # Sidebar configuration file used by Tyto - Littérateur #
#------------------------------------------------------------------------# #-------------------------------------------------------#
#
# Nom du domaine : %s # Domain Name : %s
# URI du domaine : %s # Domain URI : %s
# WRK : %s # WRK : %s
# WIP : %s # WIP : %s
# WWW : %s # WWW : %s
# How to configure this file: # How to configure this file:
# - Add one article file URI per line # - Add 1 article file URI (.tyto) per line
# - - URI is from domain directory articles/ # - - URI is from domain directory articles/
# Examples: # Examples:
@ -223,3 +185,488 @@ sidebar_header = """#
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
""" """
footer_raw = """
#======================================================#
# Footer configuration file used by Tyto - Littérateur #
#------------------------------------------------------#
# Domain Name : %s
# Domain URI : %s
# WRK : %s
# WIP : %s
# WWW : %s
# How to configure this file:
# - Edit, replace HTML code
#------------------------------------------------------------------------------
"""
help_man = """
tyto [ACTION] [ARGUMENTS]
- [ACTION] : main argument
help * > this help * see [help]
new ? add a domain / reset a module
start > activate current domain
stop > deactivate current domain
check ? + [domain] : check current domain
wip ? create HTML in "wip/" server
publish ? publish in "www/" server
- [ARGUMENTS] : multiples (random order)
domain > create new/check domain
[abc].tyto > target [abc].tyto (multiples)
all > [wip] : target all .tyto files in current domain
[MODULES] : (Useless with [publish])
metas > target metas module
header > target header module
navbar > target navbar module
sidebar > target sidebar module
footer > target footer module
modules > target all modules (metas,...,footer)
[DIVERS]
-v > show all logs
-E > show warns and errors logs only
- [help] * : * multiples. Documentation
"?" > see argument name before "?"
all > show all helps
modules > use modules
article > write .tyto article
list > write a list
cite > write a blockquote
image > show image
link > write a link
file > write a link to a file
words > write in strong, emphasis...
abbr > write abbr
anchor > set and link to anchor
code > show code from content file
raw > paste content file
"""
help_new = """
: [new]
- [new domain]
Add a new domain in current directory. Directory name must be at least
abc.tld format. Forms must be answered. Domain is deactivated by default.
CHECK first datas in configuration file tyto_domain.ini.
Activate/Deacvtivate with [tyto start] and [tyto stop], or change to
"False"/"True" the "activated" key in [DOMAIN] section
- [new sitemap]
Create a new sitemap.html in "www/" server. Can be usefull if some header
datas in an article were changed, but, the sitemap is generated at each
[publish] command
- [new [MODULES]]
see [help modules]
"""
help_check = """
: [check]
- [check domain]
Ask Tyto to check the current domain configuration. Show configuration
file contents. Works everywhere from root domain directory.
Configuration file is named "tyto_domain.ini".
Process is also done if the configuration file has changed.
"""
help_wip = """
: [wip]
- [wip [abc].tyto]
Check article and create HTML article. Create then, full HTML page in
"wip/" server. Copy all needed by article files. Also copy all files
from /template/ domain to /wip/template/ server.
- [wip [MODULES]]
voir [help modules]
"""
help_modules = """
: [MODULES] (metas...footer, modules)
- [new modules]
La première création des fichiers de configuration des modules est
automatique dans le dossier du domaine "/modules/". Cette commande
demande de réinitialiser TOUS les modules, ce qui videra les entrées
de dossiers dans le fichier navbar.raw, et les entrées d'articles dans
le fichier sidebar.raw. Les autres modules (metas, header, footer)
auront leur contenus par défaut. Si vous modifiez le fichier de
configuration du domaine, et notamment les valeurs dans la section
[WEBSITE_FOOTER], il faudra réinitialiser le module footer.
- [new [MODULE]]
Réinitialiser uniquement le [MODULE] concerné.
- [wip modules]
Cette action force la (re)génération HTML de TOUS les modules
dans le serveur "wip/template/". Peut être utile, notamment
pour les modules navbar et sidebar lorsque le titre ou la
descrtiption d'un article se trouvant dedans a été modifié.
- [wip [MODULE]]
(re)générer uniquement le [MODULE] concerné
! Pour que ces modules puissent être affichés dans les pages du site, il
faut ajouter dans le fichier de configuration du serveur nginx
(/etc/nginx/sites-available/DOMAINE.TLD) :
...
ssi on;
ssi_last_modified on;
absolute_redirect off;
...
"""
# --------- #
# Help list #
# --------- #
help_list = """
# Liste. Classe CSS possible (défaut : celle dans la configuration)
# Une entrée de liste peut être ordonnée avec le signe "+" ou non avec "="
# Une liste peut contenir des entrées mixées ("+" et "=")
# mais au changement de signe, ajouter un signe !
# Possible d'écrire une entrée sur plusieurs lignes
(=
= Première entrée non ordonnée (ul)
== Sous entrée non ordonnée
+++ Première sous-sous entrée ordonnée (ol)
+++ Seconde sous-sous entrée ordonnée
= Seconde entrée non ordonnée
= Troisième entrée ...
... non ordonnée
)=
"""
# --------- #
# Help cite #
# --------- #
help_cite = """
# Citation (bloc). Classe CSS possible (défaut : celle dans la configuration)
# Les métadonnées d'une citation sont toutes optionnelles.
# (Vous pouvez mettre que cite: et book:)
("
cite: Auteur
date: AAAA-MM-JJ
book: Nom du livre
lang: fr
link: https://...
((
Citation complète dans un paragraphe
))
)"
"""
help_3lines = """# Ces marqueurs multiples doivent toujours être configurés sur 3 lignes
# Les noms doivent tous être uniques et reportés dans le contenu
# rédactionnel avec "::" devant le nom"""
# ---------- #
# Help image #
# ---------- #
help_image_header = """
image: Image001
URI
Texte alternatif
"""
help_image_writer = """
# Images. (Nom reporté de l'entête)
# Options possibles devant respecter le format :
# ::Nom "c=ClassCSS", "w=WIDTH", "h=HEIGHT", "f=Légende de l'image"
# ! Une seule option termine par ","
::Image001
::Image001 "f=Une légende",
"""
help_image = """
%s
%s
-----
%s
"""%(
help_3lines,
help_image_header,
help_image_writer,
)
# --------- #
# Help link #
# --------- #
help_link_header = """
link: Nom du lien
URL / uri (non vérifié)
Texte alternatif
"""
help_link_writer = """
# Écrire un lien (Nom reporté de l'entête)
::Nom du lien
"""
help_link = """
%s
%s
-----
%s"""%(
help_3lines,
help_link_header,
help_link_writer,
)
# --------- #
# Help Code #
# --------- #
help_code_header = \
"""code: CodePython001
URI
Description placée en commentaire HTML"""
help_code_writer = """
# Afficher le code source d'un fichier
# Code. (Nom reporté de l'entête)
::CodePython001
"""
help_code = """
%s
%s
-----
%s
"""%(
help_3lines,
help_code_header,
help_code_writer,
)
# -------- #
# Help Raw #
# -------- #
help_raw_header = """
raw: CodeHTML001
URI
Description placée en commentaire HTML
"""
help_raw_writer = """
# Coller le contenu du fichier brut (exécuté par le navigateur)
# Raw. (Nom reporté de l'entête)
::CodeHTML001
"""
help_raw = """
%s
%s
-----
%s
"""%(
help_3lines,
help_raw_header,
help_raw_writer,
)
# --------- #
# Help ABBR #
# --------- #
help_abbr_header = """
abbr: CSS
Cascading SteelSheet
lang (en, fr, es...)
"""
help_abbr_writer = """
# Abréviations. (Nom reporté de l'entête)
::CSS
"""
help_abbr = """
%s
%s
-----
%s
"""%(
help_3lines,
help_abbr_header,
help_abbr_writer,
)
# --------- #
# Help ABBR #
# --------- #
help_anc_header = """
# Définir une ancre
# ! Chaque ancre doit avoir une ID unique
# (et ne pas commencer par "toc_")
-> top
"""
help_anc_writer = """
# Lien vers une ancre définie
# format : >`ID: Message à afficher`<
>`top:Aller en haut`<
"""
help_anc = """%s%s
"""%(
help_anc_header,
help_anc_writer,
)
# ---------- #
# Words tags #
# ---------- #
help_words = """
# Écrire les marqueurs de mots
# Chaque marqueur à la classe CSS de la configuration
# Astuce : ** + ← + `` + ← + très gras
*`très gras`* => <strong>
+`gras`+ => <b>
/`italique`/ => <em>
;`italique`; => <i>
_`souligné`_ => <u>
~`effacé`~ => <del>
[`cité`] => <q> # Contenu
:`cité`: => <cite> # auteur, nom
|`perso`| => <span>
# Code dans un texte
# ! Les marqueurs d'ouverture et de fermeture de code sont sur la MEME LIGNE
{` <li>Une entée de liste</li> `} => <code>
# ! Dans certains cas, il faut ajouter un espace après le 1er marqueur
# et/ou avant le second. Ils seront automatiquement supprimés
*`DOMAIN/articles/ `* # évite /` : marqueur italique ouvert
"""
# ------------ #
# Full article #
# ------------ #
help_article = """
: [.tyto]
Un fichier-article .tyto est séparé en 2 par au moins 5 tirets
"-----" pour définir l'entête (les métadonnées), et le contenu
rédactionnel. Voici le contenu d'un fichier-article .tyto avec
tous les marqueurs possibles. Les lignes commençant par "#" sont
des commentaires (que vous pouvez aussi utiliser dans vos articles)
# Début du fichier
# ----------------
# Ces 5 marqueurs sont obligatoires
# ----------
title: Titre <h1> de l'article
about: Description de l'article
tags: étiquette1,étiquette longue,étiquette3
date: AAAA-MM-JJ
authors: auteur1,auteur2
# Tous les marqueurs suivants sont optionnels
# ----------
# Si non renseigné, le logo de l'article sera celui du site
logo: URI
%s
%s%s%s%s%s%s
# URI
# - Commence par @ :
# - - le fichier est dans articles/images/ pour les marqueurs logo: et image:
# - - le fichier est dans articles/files/ pour les autres marqueurs
# - Commence par / :
# - - le fichier est depuis la racine articles/ du domaine
# - Commence pas par @ ou / :
# - - le fichier est depuis le dossier où se trouve le fichier-article .tyto
# Marqueur pour NE PAS inclure l'article dans le sitemap
! NOMAP
# Marqueur pour NE PAS inclure l'article dans le flux RSS
! NORSS
# Séparateur
-----
%s
# Marqueur pour créer la table des matières en fonction des titres
! TOC
# Les titres (#1 à #5, équivalents de <h2> à <h6>)
#1 Titre 1
#2 Sous-titre 1
# Paragraphe. Classe CSS possible (défaut : celle dans la configuration)
((
Un paragraphe
(( note
Un sous-paragraphe ayant la classe CSS "note"
))
))
# Block-Code. Classe CSS possible (défaut : celle dans la configuration)
# ! Le marqueur d'ouverture est à la même position que le marqueur de fermeture
{{ PyCode
...
...
}}
%s
%s
%s
%s
%s
%s
%s
%s
%s
# Retour à la ligne. Classe CSS possible (défaut : celle dans la configuration)
# La ligne commence par le symbôle "|" (<br>)
::Image001
|
::Image001 "c=Maclasse",
# Tirer un trait. Classe CSS possible (défaut : celle dans la configuration)
# La ligne contient juste "--" (<hr>)
--
%s
# --------------
# Fin du fichier
"""%(
help_3lines,
help_link_header,
help_file_header,
help_image_header,
help_code_header,
help_raw_header,
help_abbr_header,
help_anc_header,
help_list,
help_cite,
help_image_writer,
help_raw_writer,
help_code_writer,
help_link_writer,
help_file_writer,
help_words,
help_abbr_writer,
help_anc_writer,
)

View File

@ -28,7 +28,7 @@
# - Copier ce contenu dedans, et traduire les variables # - Copier ce contenu dedans, et traduire les variables
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Rappel: Fichier au format python # Note : fichier python
# Réponses valides (! NON sensible à la case : oui = OUI, Oui...) # Réponses valides (! NON sensible à la case : oui = OUI, Oui...)
ok = ("oui", "o") ok = ("oui", "o")
@ -36,122 +36,74 @@ ok = ("oui", "o")
# Form # Form
q = " ?" q = " ?"
pp = " :" pp = " :"
error = "Erreur"
success = "Succès"
configure_domain = "Configurer le domaine"
domain = "Domaine"
domain_title = "Titre du domaine"
domain_date = "Date de création"
domain_about = "Description du domaine"
domain_mail = "Courriel de l'administration"
domain_tags = "Mots-clés du domaine [1,2,3]"
domain_lang = "Langue du site web"
domain_srv = "URI du serveur"
copy = "Copie"
create_sitemaps = "Création des plans du site"
completed = "Terminé"
# Misc # Misc
anchor_title = "Titre de l'ancre" error = "Erreur"
# logs for debug # logs for debug
#--------------- #---------------
# Errors # Errors
err_arg = "Argument invalide" err_in_hole = "Dossier courant supprimé"
err_hole = "Dossier courant invalide" err_process = "Processus invalide"
err_date = "Date invalide" err_argument = "Argument non valide"
err_lang = "Format de langue invalide" err_domain_name = "Nom de domaine non valide"
err_dir = "Dossier non compatible" err_domain_dir = "Dossier de domaine non valide"
err_no_dir = "Dossier inexistant" err_kbd_stop = "Interruption clavier"
err_cd = "Dossier non créé" err_file_create = "Fichier non créé"
err_no_file = "Fichier manquant" err_file_no = "Fichier manquant"
err_cr_file = "Fichier non créé" err_dir_create = "Dossier non créé"
err_bad_uri = "URI non compatible" err_dir_unused = "Dossier manquant"
err_post_sep = "Séparateur manquant" err_dir_in = "Pas dans le bon dossier"
err_post_head = "Entête vide" err_lang_no = "Langue non disponible"
err_post_empty = "Article vide" err_post_sep = "Séparateur manquant"
err_ini_file = "Configuration invalide" err_post_header_no = "Entête vide"
err_post_data = "Donnée manquante" err_post_writer_no = "Article vide"
err_post_title = "Titre invalide" err_post_tag_set = "Données manquantes"
err_post_paired = "Marqueurs non apairés" err_post_tag_val = "Donnée non valide"
err_post_in_tag = "Marqueurs sans contenu" err_post_tag_id = "Identité déjà utilisée"
err_post_datatag = "Donnée réservée" err_post_mark_no = "Marqueur non trouvé"
err_post_id_yet = "Identité déjà utilisée" err_post_mark_stop = "Marqueur non fermé"
err_post_global = "Article erronné" err_post_mark_miss = "Marqueur manquant ?"
err_post_not_chk = "Article non vérifié" err_post_db = "Article non configuré"
err_post_not_www = "Article non publié" err_post_srv_no = "Article manquant sur le serveur"
# Warnings # Warnings
warn_no_dom = "Domaine non configuré" warn_domain_no = "Domaine non configuré"
domain_off = "Domaine désactivé" warn_domain_conf = "Domaine mal configuré"
reset_dom = "RÉINITIALISE le domaine" warn_date_format = "format de date"
warn_post_chk = "Article modifié" warn_post_tupic = "Format de paramètres"
warn_maybe_later = "Peut-être plus tard..."
# infos # infos
load_file = "Fichier chargé" inf_dir_user = "Démarrage de Tyto"
lang_logs_sys = "Langue des logs" inf_lang_logs = "Langue des logs"
website_lang = "Langue du site web" inf_domain_load = "Domaine chargé"
domains_no = "Aucun domaine trouvé" inf_domain_conf = "Configuration du domaine"
domain_found = "Domaine présent" inf_file_upd = "Fichier mis à jour"
domain_on = "Domaine activé" inf_file_create = "Fichier créé"
domain_created = "Domaine déjà créé" inf_dir_create = "Dossier créé"
domain_updated = "Domaine mis à jour" inf_check_post = "Vérification de l'article"
domain_new = "Domaine créé" inf_wip_process = "Conversion"
created_dir = "Dossier créé" inf_mod_process = "Vérification des modules"
created_file = "Fichier créé" inf_end_process = "Tyto!"
updated_file = "Fichier mis à jour"
reading_domain = "Préparation du domaine"
processing = "Process"
checking_post = "Vérification de l'article"
wipping_post = "Convertion de l'article"
post_chk_yet = "Article déjà vérifié"
post_chk_ready = "Article vérifié"
later = "Peut-être plus tard..."
# # Form
# Show HELP ask_domain_name = "Configurer le domaine"
# ask_domain_title = "Titre du domaine"
help_contents = """ ask_domain_date = "Date de création du domaine"
tyto [action] [target] [options] ask_domain_about = "Description du domaine"
[action] ask_domain_tags = "Étiquettes globales du domaine"
new : créer un nouveau domaine (réinitialisé avec -F) ask_domain_mail = "Courriel de l'administrateur"
set : [server, date, title, mail, footer, header, ...] ask_website_lang = "Langue du site web"
start : activer le domaine (défaut "no" à la création) ask_srv_root = "Dossier du serveur root"
stop : désactiver le domaine ask_reset_modules = "Réinitialiser tous les modules"
show : [domains, metas]
check : [domain, wip, www, [name].tyto]
wip : [name].tyto: crée la page HTML dans le serveur "wip/"
publish :
[target] # Modules
domain : pour tout ce qui concerne un domaine metas_raw = """
domains : montre tous les domaines enregistrés
title : [set]: titre du domaine
date : [set]: date de création du domaine
about : [set]: description du domaine
mail : [set]: courriel de l'administrateur
tags : [set]: étiquettes du domaine (ajoutées aux articles)
lang : [set]
server : [set]: URI du serveur local
footer : [set]: Crée le module par défaut (aussi header...)
wip : [check] dossiers et fichiers
www : [check] dossiers et fichiers
[options] ; multiples
--force, -F : forcer à faire quelque chose...
--debug, -D : montrer plus de logs
--errors, -E : montrer surtout les avertissements et erreurs
"""
#
# Modules (metas, navbar, sidebar...)
#
metas_header = """#
#========================================================================# #========================================================================#
# Fichier de configuration des metas HTML utilisé par Tyto - Littérateur # # Fichier de configuration des metas HTML utilisé par Tyto - Littérateur #
#------------------------------------------------------------------------# #------------------------------------------------------------------------#
#
# Nom du domaine : %s # Nom du domaine : %s
# URI du domaine : %s # URI du domaine : %s
@ -167,18 +119,31 @@ metas_header = """#
# ? Taper "tyto show metas" pour voir toutes celles ajoutées # ? Taper "tyto show metas" pour voir toutes celles ajoutées
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
<meta charset="UTF-8">
<meta name="robots" content="all">
<meta name="medium" content="website">
<meta name="revisit-after" content="3 days">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
""" """
navbar_header = """# header_raw = """
#=====================================================================#
# Fichier de configuration de l'entête utilisé par Tyto - Littérateur #
#---------------------------------------------------------------------#
# Nom du domaine : %s
# URI du domaine : %s
# WRK : %s
# WIP : %s
# WWW : %s
# Comment configurer ce fichier :
# - Modifier, remplacer le code HTML
#------------------------------------------------------------------------------
"""
navbar_raw = """
#======================================================================# #======================================================================#
# Fichier de config de la barre de menu utilisé par Tyto - Littérateur # # Fichier de config de la barre de menu utilisé par Tyto - Littérateur #
#----------------------------------------------------------------------# #----------------------------------------------------------------------#
#
# Nom du domaine : %s # Nom du domaine : %s
# URI du domaine : %s # URI du domaine : %s
@ -189,23 +154,20 @@ navbar_header = """#
# Comment configurer ce fichier : # Comment configurer ce fichier :
# - Ajouter un URI de dossier par ligne # - Ajouter un URI de dossier par ligne
# - - URI depuis le dossier articles/ du domaine # - - URI depuis le dossier articles/ du domaine
# ! Doit contenir un article "index.tyto" prêt # ! Le dossier serveur doit contenir un article "index.html"
# Option :
# - Nom du lien par défaut : directory
# - Ajouter "# Nom du Lien" pour le changer
# Exemples: # Exemples:
# news # news
# a-propos/website # a-propos/website
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
""" """
sidebar_header = """# sidebar_raw = """
#========================================================================# #=======================================================================#
# Fichier de config de la barre latérale utilisée par Tyto - Littérateur # # Fichier de config de la barre latérale utilisé par Tyto - Littérateur #
#------------------------------------------------------------------------# #-----------------------------------------------------------------------#
#
# Nom du domaine : %s # Nom du domaine : %s
# URI du domaine : %s # URI du domaine : %s
@ -214,7 +176,7 @@ sidebar_header = """#
# WWW : %s # WWW : %s
# Comment configurer ce fichier : # Comment configurer ce fichier :
# - Ajouter un URI d'article par ligne # - Ajouter 1 URI d'article (.tyto) par ligne
# - - URI depuis le dossier articles/ du domaine # - - URI depuis le dossier articles/ du domaine
# Exemples : # Exemples :
@ -223,3 +185,518 @@ sidebar_header = """#
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
""" """
footer_raw = """
#=========================================================================#
# Fichier de configuration du pied de page utilisé par Tyto - Littérateur #
#-------------------------------------------------------------------------#
# Nom du domaine : %s
# URI du domaine : %s
# WRK : %s
# WIP : %s
# WWW : %s
# Comment configurer ce fichier :
# - Modifier, remplacer le code HTML
#------------------------------------------------------------------------------
"""
help_man = """
tyto [ACTION] [ARGUMENTS]
- [ACTION] : argument principal
help * > cette aide * voir [help]
new ? ajouter un domaine / réinitialiser un module
start > activer le domain actuel
stop > désactiver le domaine actuel
check ? + [domain] : vérifier le domaine actuel
wip ? créer de l'HTML dans le serveur "wip/"
publish ? publier dans le serveur "www/"
- [ARGUMENTS] : multiples (ordre aléatoire)
domain > créer/vérifier un domaine
[nom].tyto > cible le fichier [nom].tyto (plusieurs possibles)
all > [wip] : cible tous les fichiers .tyto du domaine courant
[MODULES] : (inutiles avec [publish])
metas > cible le module metas
header > cible le module header
navbar > cible le module navbar
sidebar > cible le module sidebar
footer > cible le module footer
modules > cible tous les modules (metas,...,footer)
[DIVERS]
-v > montrer tous les logs
-E > ne montrer que les erreurs et avertissements
- [help] * : * multiples. Documentation
"?" > voir le nom de l'argument avant le signe "?"
all > montre toutes les informations d'aides
modules > utiliser les modules
article > écrire un article .tyto
list > écrire une liste
cite > écrire une citation
image > afficher une image
link > écrire un lien
file > écrire un lien vers un fichier
words > écrire en gras, italique...
abbr > écrire une abréviation
anchor > définir, lier une ancre
code > afficher un bloc de code d'un fichier
raw > coller le contenu d'un fichier
"""
help_new = """
: [new]
- [new domain]
Ajouter un nouveau domaine dans le dossier courant. Le nom du dossier
courant doit être au moins au format nom.tld. Un formulaire devra être
rempli. Le domaine est désactivé par défaut. VÉRIFIEZ d'abord les données
dans le fichier tyto_domain.ini, avant de l'activer.
Pour activer/désactiver le domaine, utiliser [tyto start] et [tyto stop]
ou, changer la valeur "False"/"True" de la clé "activated" dans la
section [DOMAIN]
- [new sitemap]
Créer un nouveau sitemap.html dans le serveur "www/". Peut-être utile en
cas de changements de données dans l'entête d'un article, mais le sitemap
est généré automatiquement à chaque commande [publish].
- [new [MODULES]]
voir [help modules]
"""
help_check = """
: [check]
- [check domain]
Demander à Tyto de vérifier le domaine actuellement configuré.
Montre le contenu du fichier de configuration. Il faut être dans
l'arborescence du dossier de travail du domaine. Le fichier de
configuration est nommé "tyto_domain.ini". Ce processus est également
fait automatiquement lorsque le fichier de configuration a changé.
"""
help_wip = """
: [wip]
- [wip [NOM].tyto]
Vérifie le format de l'article et le transforme en contenu HTML. Crée
ensuite la page HTML complète dans le serveur "wip/" et copie les
fichiers utilisés par l'article. Copie les fichiers de /template/ du
domaine vers le serveur /wip/template/.
- [wip [MODULES]]
voir [help modules]
"""
help_modules = """
: [MODULES] (metas...footer, modules)
- [new modules]
La première création des fichiers de configuration des modules est
automatique dans le dossier du domaine "/modules/". Cette commande
demande de réinitialiser TOUS les modules. Les fichier sidebar.raw et
navbar.raw seront vidés. Les autres modules (metas, header, footer)
auront leur contenus par défaut.
Si vous modifiez le fichier de configuration du domaine, et notamment les valeurs dans la section
[WEBSITE_FOOTER], il faudra réinitialiser le module footer.
- [new [MODULE]]
Réinitialiser uniquement le [MODULE] concerné.
- [wip modules]
Cette action force la (re)génération HTML de TOUS les modules
dans le serveur "wip/template/". Peut être utile, notamment
pour les modules navbar et sidebar lorsque le titre ou la
descrtiption d'un article se trouvant dedans a été modifié.
- [wip [MODULE]]
(re)générer uniquement le [MODULE] concerné
! Pour que ces modules puissent être affichés dans les pages du site, il
faut ajouter dans le fichier de configuration du serveur nginx
(/etc/nginx/sites-available/DOMAINE.TLD) :
...
ssi on;
ssi_last_modified on;
absolute_redirect off;
...
"""
# --------- #
# Help list #
# --------- #
help_list = """
# Liste. Classe CSS possible (défaut : celle dans la configuration)
# Une entrée de liste peut être ordonnée avec le signe "+" ou non avec "="
# Une liste peut contenir des entrées mixées ("+" et "=")
# mais au changement de signe, ajouter un signe !
# Possible d'écrire une entrée sur plusieurs lignes
(=
= Première entrée non ordonnée (ul)
== Sous entrée non ordonnée
+++ Première sous-sous entrée ordonnée (ol)
+++ Seconde sous-sous entrée ordonnée
= Seconde entrée non ordonnée
= Troisième entrée ...
... non ordonnée
)=
"""
# --------- #
# Help cite #
# --------- #
help_cite = """
# Citation (bloc). Classe CSS possible (défaut : celle dans la configuration)
# Les métadonnées d'une citation sont toutes optionnelles.
# (Vous pouvez mettre que cite: et book:)
("
cite: Auteur
date: AAAA-MM-JJ
book: Nom du livre
lang: fr
link: https://...
((
Citation complète dans un paragraphe
))
)"
"""
help_3lines = """# Ces marqueurs multiples doivent toujours être configurés sur 3 lignes
# Les noms doivent tous être uniques et reportés dans le contenu
# rédactionnel avec "::" devant le nom"""
# ---------- #
# Help image #
# ---------- #
help_image_header = """
image: Image001
URI
Texte alternatif
"""
help_image_writer = """
# Images. (Nom reporté de l'entête)
# Options possibles devant respecter le format :
# ::Nom "c=ClassCSS", "w=WIDTH", "h=HEIGHT", "f=Légende de l'image"
# ! Une seule option termine par ","
::Image001
::Image001 "f=Une légende",
"""
help_image = """
%s
%s
-----
%s
"""%(
help_3lines,
help_image_header,
help_image_writer,
)
# --------- #
# Help link #
# --------- #
help_link_header = """
link: Nom du lien
URL / uri (non vérifié)
Texte alternatif
"""
help_link_writer = """
# Écrire un lien (Nom reporté de l'entête)
::Nom du lien
"""
help_link = """
%s
%s
-----
%s"""%(
help_3lines,
help_link_header,
help_link_writer,
)
# --------- #
# Help link #
# --------- #
help_file_header = """
file: ce fichier
URI
Texte alternatif
"""
help_file_writer = """
# Écrire un lien vers un fichier (Nom reporté de l'entête)
::ce fichier
"""
help_file = """
%s
%s
-----
%s"""%(
help_3lines,
help_file_header,
help_file_writer,
)
# --------- #
# Help Code #
# --------- #
help_code_header = """
code: CodePython001
URI
Description placée en commentaire HTML
"""
help_code_writer = """
# Afficher le code source d'un fichier
# Code. (Nom reporté de l'entête)
::CodePython001
"""
help_code = """
%s
%s
-----
%s
"""%(
help_3lines,
help_code_header,
help_code_writer,
)
# -------- #
# Help Raw #
# -------- #
help_raw_header = """
raw: CodeHTML001
URI
Description placée en commentaire HTML
"""
help_raw_writer = """
# Coller le contenu du fichier brut (exécuté par le navigateur)
# Raw. (Nom reporté de l'entête)
::CodeHTML001
"""
help_raw = """
%s
%s
-----
%s
"""%(
help_3lines,
help_raw_header,
help_raw_writer,
)
# --------- #
# Help ABBR #
# --------- #
help_abbr_header = """
abbr: CSS
Cascading SteelSheet
lang (en, fr, es...)
"""
help_abbr_writer = """
# Abréviations. (Nom reporté de l'entête)
::CSS
"""
help_abbr = """
%s
%s
-----
%s
"""%(
help_3lines,
help_abbr_header,
help_abbr_writer,
)
# --------- #
# Help ABBR #
# --------- #
help_anc_header = """
# Définir une ancre
# ! Chaque ancre doit avoir une ID unique
# (et ne pas commencer par "toc_")
-> top
"""
help_anc_writer = """
# Lien vers une ancre définie
# format : >`ID: Message à afficher`<
>`top:Aller en haut`<
"""
help_anc = """%s%s
"""%(
help_anc_header,
help_anc_writer,
)
# ---------- #
# Words tags #
# ---------- #
help_words = """
# Écrire les marqueurs de mots
# Chaque marqueur à la classe CSS de la configuration
# Astuce : ** + ← + `` + ← + très gras
*`très gras`* => <strong>
+`gras`+ => <b>
/`italique`/ => <em>
;`italique`; => <i>
_`souligné`_ => <u>
~`effacé`~ => <del>
[`cité`] => <q> # Contenu
:`cité`: => <cite> # auteur, nom
|`perso`| => <span>
# Code dans un texte
# ! Les marqueurs d'ouverture et de fermeture de code sont sur la MEME LIGNE
{` <li>Une entée de liste</li> `} => <code>
# ! Dans certains cas, il faut ajouter un espace après le 1er marqueur
# et/ou avant le second. Ils seront automatiquement supprimés
*`DOMAIN/articles/ `* # évite /` : marqueur italique ouvert
"""
# ------------ #
# Full article #
# ------------ #
help_article = """
: [.tyto]
Un fichier-article .tyto est séparé en 2 par au moins 5 tirets
"-----" pour définir l'entête (les métadonnées), et le contenu
rédactionnel. Voici le contenu d'un fichier-article .tyto avec
tous les marqueurs possibles. Les lignes commençant par "#" sont
des commentaires (que vous pouvez aussi utiliser dans vos articles)
# Début du fichier
# ----------------
# Ces 5 marqueurs sont obligatoires
# ----------
title: Titre <h1> de l'article
about: Description de l'article
tags: étiquette1,étiquette longue,étiquette3
date: AAAA-MM-JJ
authors: auteur1,auteur2
# Tous les marqueurs suivants sont optionnels
# ----------
# Si non renseigné, le logo de l'article sera celui du site
logo: URI
%s
%s%s%s%s%s%s
# URI
# - Commence par @ :
# - - le fichier est dans articles/images/ pour les marqueurs logo: et image:
# - - le fichier est dans articles/files/ pour les autres marqueurs
# - Commence par / :
# - - le fichier est depuis la racine articles/ du domaine
# - Commence pas par @ ou / :
# - - le fichier est depuis le dossier où se trouve le fichier-article .tyto
# Marqueur pour NE PAS inclure l'article dans le sitemap
! NOMAP
# Marqueur pour NE PAS inclure l'article dans le flux RSS
! NORSS
# Séparateur
-----
%s
# Marqueur pour créer la table des matières en fonction des titres
! TOC
# Les titres (#1 à #5, équivalents de <h2> à <h6>)
#1 Titre 1
#2 Sous-titre 1
# Paragraphe. Classe CSS possible (défaut : celle dans la configuration)
((
Un paragraphe
(( note
Un sous-paragraphe ayant la classe CSS "note"
))
))
# Block-Code. Classe CSS possible (défaut : celle dans la configuration)
# ! Le marqueur d'ouverture est à la même position que le marqueur de fermeture
{{ PyCode
...
...
}}
%s
%s
%s
%s
%s
%s
%s
%s
%s
# Retour à la ligne. Classe CSS possible (défaut : celle dans la configuration)
# La ligne commence par le symbôle "|" (<br>)
::Image001
|
::Image001 "c=Maclasse",
# Tirer un trait. Classe CSS possible (défaut : celle dans la configuration)
# La ligne contient juste "--" (<hr>)
--
%s
# --------------
# Fin du fichier
"""%(
help_3lines,
help_link_header,
help_file_header,
help_image_header,
help_code_header,
help_raw_header,
help_abbr_header,
help_anc_header,
help_list,
help_cite,
help_image_writer,
help_raw_writer,
help_code_writer,
help_link_writer,
help_file_writer,
help_words,
help_abbr_writer,
help_anc_writer,
)

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: English website translation file
# File: /var/lib/tyto/translations/site_en.py
#
# How to translate:
# - create filename "site_xx.py" in /var/lib/tyto/translations/
# where xx are 2 first lang letters (es, for spanish...)
# - copy its contents and translate values
#----------------------------------------------------------------------
# Note: python file
sidebar_title = "Featured posts"
home = "Home"
directory = "Directory"
sitemap = "Sitemap"
toc = "Table of Contents"
sitemap_gen = "%s generated by"
sitemap_empty = "Nothing to show yet"
posts_list = "Articles list"
source_code = "Source code"
permalink = "Permalink"
license = "License"
license_title = "Website %s of"%license
legals = "Legal Mentions"
legals_title = "%s of"%legals
terms = "Terms of Service"
terms_title = "%s of"%terms
bylaws = "Bylaws"
bylaws_title = "%s of"%bylaws
feed_rss = "RSS feed 2.0"
generated_by = "Generated by"
off_website = "Official website"
about = "About"
tyto_credit = "Website %s"%generated_by

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# Tyto - Littérateur
# Copyright (C) 2023 Cyrille Louarn <echolib+tyto@a-lec.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>..
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Fichier français de traduction des sites web
# File: /var/lib/tyto/translations/site_fr.py
#
# Comment traduire :
# - créer un fichier nommé "site_xx.py" dans /var/lib/tyto/translations/
# oû xx sont les 2 première lettres de la langue (es, pour espagnol...)
# - Copier ce contenu dedans, et traduire les variables
#----------------------------------------------------------------------
# Note: Fichier python
sidebar_title = "Articles recommandés"
home = "Accueil"
directory = "Dossier"
sitemap = "Plan du site"
toc = "Table des matières"
sitemap_gen = "%s généré par"%sitemap
sitemap_empty = "Rien à afficher pour le moment."
posts_list = "Liste des articles"
source_code = "Code source"
permalink = "Permalien"
license = "Licence"
license_title = "%s du site web de"%license
legals = "Mentions légales"
legals_title = "%s de"%legals
terms = "C.G.U"
terms_title = "%s de"%terms
bylaws = "Statuts"
bylaws_title = "%s de"%bylaws
feed_rss = "Flux RSS 2.0"
generated_by = "Géneré par"
off_website = "Site web officiel"
about = "À propos de"
tyto_credit = "Site web %s"%generated_by