diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75c6ac6..54e5f86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,16 +1,10 @@
# Changelog
Tyto - Littérateur
-- Repository: https://git.a-lec.org/echolib/tyto
-- Issues: https://git.a-lec.org/echolib/tyto/-/issues
-- Changelog: https://git.a-lec.org/echolib/tyto/-/blob/main/CHANGELOG.md
-- License: https://git.a-lec.org/echolib/tyto/-/blob/main/LICENSE
-
-Tyto - Litterateur is a Libre project to create and manage multiple
-websites from articles' files. Tyto uses its own syntax to convert
-your articles in HTML5 static pages. Tyto works on a GNU/Linux system
-and needs minimal dependancies.
-- python3 rsync nano gawk curl
-
-## [0.9.0] First Commit version (pre-final)
+- Repository: https://git.a-lec.org/echolib/tyto-litterateur
+- Issues: https://git.a-lec.org/echolib/tyto-litterateur/-/issues
+- Changelog: https://git.a-lec.org/echolib/tyto-litterateur/-/blob/master/CHANGELOG.md
+- License: https://git.a-lec.org/echolib/tyto-litterateur/-/blob/master/LICENSE
+## [0.9.0]
+- Last testings before final
diff --git a/FR_Installation.md b/FR_Installation.md
deleted file mode 100644
index f508aec..0000000
--- a/FR_Installation.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Installation manuelle
-
-Si vous utilisez Debian, il est plus que recommandé de procéder à
-l'installation par le paquet .deb
-
-## Préparer les dossiers
-````
-sudo mkdir -p /var/lib/tyto /etc/tyto /var/log/tyto
-sudo touch /usr/local/bin/tyto
-sudo chown -R USER:GROUP /var/lib/tyto /etc/tyto /var/log/tyto
-sudo chown USER:GROUP /usr/local/bin/tyto
-sudo chmod +x /usr/local/bin/tyto
-
-git clone https://git.a-lec.org/echolib/tyto
-rsync -a folders'repo/ to /folders/
-
-# Créer votre dossier pour le serveur web
-# On utilise en général /var/www/
-# Celui-ci sera à renseigner lors de la création d'un domaine dans Tyto
-sudo mkdir -p /var/www/
-````
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2482287
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# file: Makefile
+
+# By neox
+
+# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007
+
+default: debian/tyto.deb
+
+### MAKE DEB
+
+.INTERMEDIATE: debian/debian-binary debian/control.tar.gz debian/data.tar.gz
+
+.PHONY: clean
+
+debian/tyto.deb: debian/debian-binary debian/control.tar.gz debian/data.tar.gz
+ ar -r debian/tyto.deb debian/debian-binary debian/control.tar.gz debian/data.tar.gz
+
+debian/data.tar.gz:
+ cd src && tar czvf ../debian/data.tar.gz usr var
+
+debian/control.tar.gz:
+ tar czvf debian/control.tar.gz debian/control #preinst postinst prerm postrm
+
+debian/debian-binary:
+ echo 2.0 > debian/debian-binary
+
+clean:
+ -rm debian/*.tar.gz
+ -rm debian/debian-binary
+ -rm debian/*.deb
diff --git a/README.md b/README.md
index eae71ee..50bf72d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,243 @@
# Tyto
+Pour obtenir de l'aide, taper juste la commande tyto
## Répertoire de code du projet Tyto
+TODO
+
+## Comment définir les métas
+```
+# Obligatoires uniques
+# Ces marqueurs se configurent sur UNE ligne
+title: Titre
+about: Infos de l'article
+author: Auteur
+tags: mot-clé-1,mot 2,
+date: YYYY-MM-DD (AAAA-MM-JJ)
+
+# L'image doit être configurée avec le même Nom
+# dans les marqueurs multiples
+# Utilise l'image précisée comme "avatar" dans les réseaux sociaux
+snpic: Nom
+# Optionnels multiples
+# Ces marqueurs se configurent sur 3 lignes
+link: Nom du lien
+ URL
+ Texte Alternatif
+
+image: Nom
+ URI
+ Texte Alternatif
+
+file: Nom du lien
+ URL
+ Texte Alternatif
+
+raw: Nom
+ URI
+ Texte Alternatif
+
+abbr: abbrev
+ Définition de abbrev
+ ABBR (forme à afficher dans l'artile (optionnel))
+
+# Séparateur d'au moins 5 "-" pour définir la fin
+# des métadonnées d'entête de l'article
+----------
+```
+
+## Comment écrire un article
+
+### Écrire des titres
+```
+# Les titres HTML vont de 1 à 6.
+# Utiliser #N, où N est entre 1 et 6.
+# Si du contenu existe entre les titres, une
est ajoutée
+# Astuce: on commence en général par #2 dans l'article
+
+# Source
+#1 Titre 1
+Contenu 1
+
+#2 Titre 2
+
+#3 Titre 3
+contenu 2
+
+#4 Titre 4
+
+# HTML
+
Titre 1
+
+Contenu 1
+
+
Titre 2
+
Titre 3
+
+contenu 2
+
+
Titre 4
+```
+
+### Paragraphes
+```
+(( CSS
+Un paragraphe
+))
+```
+
+### Code Brut
+```
+{{ CSS
+def hello_world():
+ print("Hello")
+}}
+```
+
+### Listes ul/ol
+```
+-( CSS
+= Liste ul
+== Sous-liste ul
+=== Sous-sous-liste ul
+++++ Sous-sous-sous-liste ol
++++ Sous-sous-liste ol
+-)
+```
+
+### Ancres
+```
+# Source de l'ancre cible. "id" est son identité
+-> id
+
+# HTML
+
+
+# Source de l'ancre d'appel
+# Définir l'identité cible et le texte du lien
+((
+>_id:Retourner au point d'ancre id_<
+))
+
+# HTML
+Retourner au point d'ancre id
+```
+
+### Retour à la ligne HTML
+```
+# Source
+|
+
+# HTML
+
+```
+
+### Lien vers URL
+```
+Voir ce _Nom du lien # Ouverture même fenêtre
+Voir ce _Nom du lien+ # ouverture nouvelle fenêtre
+```
+
+### Lien vers fichier
+```
+Voir ce __Nom du lien # Ouverture même fenêtre
+Voir ce __Nom du lien+ # ouverture nouvelle fenêtre
+```
+
+Note:
+Vous pouvez avoir un Nom identique pour les marqueur `file:` et `link:`
+
+
+### Gras, Strong, italique...
+```
+*_très gras_* #
++_gras léger_+ #
+/_en italique_/ #
+[_en italique_] #
+~_texte barré_~ #
+:_Citation rapide_: #
+%_Classe personnalisée_% >>>
+._Souligné_. #
+{_Code_} #
+
+# Montrer comment écrire du code dans Tyto:
+# Bypass avec \ devant {_ et _}
+{_\{_Comme ça\_}_}
+
+```
+
+### Abréviations
+```
+# abbrev sera remplacé par "ABBR" dans la page si défini en entête
+# sinon, abbrev sera conservé
+# - Toujours écrire dans l'article :
+# - entre parenthèses ET majuscules les "(ABBREV)"
+
+
+Avec cette (ABBREV).
+# HTML: ABBR
+```
+
+### Images
+```
+# Chaque image doit être placée en début de ligne
+# Placer dans un paragraphe pour chacune ou après "|",
+# sinon, affichage les une à côté des autres
+# ! Si pas d'unité pour w= et h= : défaut "px"
+
+_image:Nom
+_image:Nom c=CSS
+_image:Nom c=css w=1080
+_image:Nom w=640em h=480em
+_image:Nom t=+ # Rend l'image interne cliquable
+_image:Nom t=https://...# Donne un lien web à l'image
+_image:Nom c=CSS t=https://... w=320px h=240 # 240px
+```
+
+### Code brut depuis un fichier
+```
+_raw:Nom
+```
+
+### Citations
+Possibilité dans toute citation d'utiliser les marqueurs
+optionnels `_xxx:`. Pour la date, utilisez le FORMAT INTERNATIONAL
+```
+# Source: citation complète
+[[ CSS_TEST
+_cite: echolib
+_date: 2022-12-28
+_lang: fr
+_link: https://tyto.echolib.re
+_book: Référence
+((
+Pfff, vraiment !
+))
+]]
+
+# HTML
+
+````
+```
+# Source: citation basique
+[[
+Une citation simple, et sans paragraphe
+]]
+
+# HTML
+
+ Une citation simple, et sans paragraphe
+
+```
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..962e249
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,10 @@
+Package: tyto
+Version: 0.9.0
+Section: custom
+Priority: optional
+Architecture: all
+Essential: no
+Depends: nano,python3
+Installed-Size: `du -ks .|cut -f 1`
+Maintainer: echolib
+Description: Tyto - Litterateur is a libre project to create and manage multiple websites from articles files. Tyto uses its own syntax to convert your articles in HTML5 pages. Tyto works on a GNU/Linux system and needs minimal dependencies.
diff --git a/src/usr/bin/tyto b/src/usr/bin/tyto
new file mode 100755
index 0000000..1824ec3
--- /dev/null
+++ b/src/usr/bin/tyto
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# Version: 0.9.0
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Main binary to execute
+# File: /usr/bin/tyto
+#----------------------------------------------------------------------
+
+#------------
+# funny stats
+#------------
+# scripts files:
+# app lines:
+# app comments:
+# app functions:
+# lines:
+# functions:
+# comments:
+#----------------------------------------------------------------------
+
+#**********************************************************************
+
+import sys
+sys.path.insert(0, '/var/lib/tyto/program')
+
+
+#====================#
+# MAIN #
+# Treat Arguments #
+#--------------------#-------------------------------------------------
+import logs
+
+if not __name__ == "__main__":
+ logs.out("14", '', True)
+
+# Check arguments
+import args
+action = args.set_action()
+target = args.set_target()
+
+# Check domain
+import dom, status
+status.domain()
+
+
+# Command start argument
+import check, form, html, new, publish, show, wip, infos
+actions = {
+ 'check' : check.manage,
+ 'help' : infos.tyto,
+ 'edit' : show.manage,
+ 'edit-about' : show.manage,
+ 'edit-db' : show.manage,
+ 'edit-wip' : show.manage,
+ 'edit-www' : show.manage,
+ 'new' : new.manage,
+ 'publish' : publish.manage_publish,
+ 'show' : show.manage,
+ 'show-about' : show.manage,
+ 'show-db' : show.manage,
+ 'show-wip' : show.manage,
+ 'show-www' : show.manage,
+ 'status' : status.check,
+ 'template' : publish.manage_publish,
+ 'wip' : wip.manage,
+ }
+
+
+# Start action
+# Argument's Check done in args.py
+#---------------------------------
+actions[action](target)
+
diff --git a/src/usr/local/bin/tyto b/src/usr/local/bin/tyto
deleted file mode 100755
index 3f29b90..0000000
--- a/src/usr/local/bin/tyto
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-# Version: 0.9.0
-# Name: Tyto - Littérateur
-# Type: Executable
-# Description: Multiple Static Websites generator and manager
-# file: tyto
-# Folder: /usr/local/bin/
-# By echolib (XMPP: im@echolib.re)
-# Repo: https://git.a-lec.org/echolib/tyto.git
-# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007
-
-#------------
-# funny stats
-#------------
-# scripts files:
-# app lines:
-# app comments:
-# app functions:
-# lines:
-# functions:
-# comments:
-#----------------------------------------------------------------------
-
-'''
-All scripts are in /var/lib/tyto/program/
-Directories that must be writeable by Tyto
- - /var/lib/tyto/db/
- - /var/log/tyto/
- - /[LOCAL Server]/ (registred per domain)
-'''
-
-#**********************************************************************
-
-# Import needed libs and tyto's libs
-import sys
-sys.path.insert(0, '/var/lib/tyto/program')
-import check, wip, domain, log
-
-#=======#
-# Tools #
-#=======#--------------------------------------------------------------
-#=======================================#
-# Check argument from main command line #
-# # total arguments #
-#---------------------------------------#
-def tyto_args(ta):
- global file_post, Opt
- Domain = False
- Opt = ''
- Target = ''
-
- # Start process accoring to first argument action
- Actions = { 'check' : check.manage_check,
- 'wip' : wip.manage_wip,
- 'domain' : domain.manage_domain,
- 'log' : log.manage_log
- }
-
- # Dict for Options
- Options = {
- '-R' : "Remove", 'remove' : "Remove",
- '-n' : "New", 'new' : "New",
- '-e' : "Edit", 'edit' : "Edit",
- '-F' : "Force", 'force' : "Force"
- }
-
- # Set Opt from other arguments options
- # Set Target for article or new domain name
- if sys.argv[1] == 'domain': Domain = True
-
- for i in range(2, ta):
- if sys.argv[i].endswith('.tyto'):
- Target = sys.argv[i]
- if Target[0] == '/':
- Target = Target[1:len(Target)]
- else:
- try:
- Opt = Options[sys.argv[i]]
- except:
- if Domain:
- Target = sys.argv[i]
- else:
- print(':( Invalid option "%s"'%sys.argv[i])
- sys.exit(0)
-
- try:
- Actions[sys.argv[1]](Target, Opt)
- except KeyError:
- print(':( Invalid action "%s"'%sys.argv[1])
-
-
-
-
-
-#====================#
-# MAIN #
-# Treat Arguments #
-#--------------------#-------------------------------------------------
-tyto_args(len(sys.argv))
diff --git a/src/var/lib/tyto/help/CHANGELOG.md b/src/var/lib/tyto/help/CHANGELOG.md
new file mode 100644
index 0000000..75c6ac6
--- /dev/null
+++ b/src/var/lib/tyto/help/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Changelog
+
+Tyto - Littérateur
+- Repository: https://git.a-lec.org/echolib/tyto
+- Issues: https://git.a-lec.org/echolib/tyto/-/issues
+- Changelog: https://git.a-lec.org/echolib/tyto/-/blob/main/CHANGELOG.md
+- License: https://git.a-lec.org/echolib/tyto/-/blob/main/LICENSE
+
+Tyto - Litterateur is a Libre project to create and manage multiple
+websites from articles' files. Tyto uses its own syntax to convert
+your articles in HTML5 static pages. Tyto works on a GNU/Linux system
+and needs minimal dependancies.
+- python3 rsync nano gawk curl
+
+## [0.9.0] First Commit version (pre-final)
+
diff --git a/src/var/lib/tyto/help/README.md b/src/var/lib/tyto/help/README.md
new file mode 100644
index 0000000..b9f905e
--- /dev/null
+++ b/src/var/lib/tyto/help/README.md
@@ -0,0 +1,167 @@
+# Tyto
+
+## Répertoire de code du projet Tyto
+
+## Comment définir les métas
+```
+# Obligatoires uniques
+title: Titre
+about: Infos de l'article
+author: Autheur
+tags: mots-clé-1,mots-clé-2
+date: YYYY-MM-DD (AAAA-MM-JJ)
+
+# Optionnels myltiples
+link: Nom du lien
+ URL
+ Texte Alternatif
+
+image: Nom
+ URI
+ Texte Alternatif
+
+file: Nom du lien
+ URL
+ Texte Alternatif
+
+brut: Nom
+ URI
+ Texte Alternatif
+
+abbr: NOM (en majuscule)
+ Définition du NOM
+ nom (forme à afficher dans l'artile (optionnel))
+
+# Séparateur d'au moins 5 "-" pour définir la fin
+# des métadonnées d'entête de l'article
+----------
+```
+
+## Comment écrire un article
+### Titre h1 à h6
+```
+#1 Titre 1
+((
+Un paragraphe
+))
+
+#2 Titre 2
+```
+
+### Paragraphes
+```
+(( CSS
+Un paragraphe
+))
+```
+
+### Code Brut
+```
+{{ CSS
+def hello_world():
+ print("Hello")
+}}
+```
+
+### Listes ul/ol
+```
+-( CSS
+= Liste ul
+== Sous-liste ul
+=== Sous-sous-liste ul
+++++ Sous-sous-sous-liste ol
++++ Sous-sous-liste ol
+-)
+```
+
+### Citations
+```
+[[ CSS
+_cite: autheur
+_lang: langue
+_link: lien
+_year: année ou date YYYY-MM-DD
+_book: référence
+((
+Citation placée dans un paragraphe
+))
+]]
+
+[[ CSS
+Citation simple sans référence
+]]
+```
+
+### Ancres
+```
+-> id
+((
+un long paragraphe
+))
+((
+>_id:Retourner au point d'ancre_<
+))
+```
+
+### Retour à la ligne HTML
+```
+| #
+```
+
+### Lien vers URL
+```
+Voir ce _Nom du lien # Ouverture même fenêtre
+Voir ce _Nom du lien+ # ouverture nouvelle fenêtre
+```
+
+### Lien vers fichier
+```
+Voir ce __Nom du lien # Ouverture même fenêtre
+Voir ce __Nom du lien+ # ouverture nouvelle fenêtre
+```
+
+Note:
+Vous pouvez avoir un NAME identique pour file: et link:
+
+### Gras, Strong, italique...
+```
+*_très gras_* #
++_gras léger_+ #
+/_en italique_/ #
+[_en italique_] #
+~_texte barré_~ #
+{_Code_} #
+:_Citation rapide_: #
+%_Classe personnalisée_% >>>
+._Souligné_. #
+
+```
+
+### Abréviations
+```
+# ! NOM sera remplacé par "nom" dans la page si défini en entête
+# sinon, NOM sera conservé
+# Toujours écrire en majuscule les ABBR dans l'article brut
+# nom
+Avec ce NOM.
+```
+
+### Images
+```
+# Chaque image doit être placée en début de ligne
+# Placer dans un paragraphe pour chaque ou après "|",
+# sinon, affichage les une à côté des autres
+# ! Si pas d'unité pour w= et h= : défaut "px"
+_image:Nom
+_image:Nom c=CSS
+_image:Nom c=css w=1080
+_image:Nom w=640em h=480em
+_image:Nom t=+ # Rend l'image interne cliquable
+_image:Nom t=https://...# Donne un lien à l'image
+_image:Nom c=CSS t=https://... w=320px h=240
+```
+
+### Code Brut depuis un fichier
+```
+_brut:NOM
+```
diff --git a/src/var/lib/tyto/help/styles.css b/src/var/lib/tyto/help/styles.css
new file mode 100644
index 0000000..31ccc5c
--- /dev/null
+++ b/src/var/lib/tyto/help/styles.css
@@ -0,0 +1,184 @@
+/*
+ * All class / ID used by Tyto - Littérateur in a page
+ * DOMAIN MUST be changed by domain css set in configuration
+*/
+
+/*
+ * Header
+*/
+header#header_page {
+}
+
+div#site_logo {
+}
+
+a#site_logo_link {
+}
+
+img#site_logo_image {
+}
+
+div#site_infos {
+}
+
+a#site_link {
+}
+
+h1#site_title {
+}
+
+p#site_about {
+}
+
+
+/*
+ * navbar
+*/
+nav#site_menu {
+}
+
+ul#site_menu_items {
+}
+
+li.site_menu_item {
+}
+
+a.site_menu_link {
+}
+
+/*
+ * article
+*/
+article#article_main {
+}
+
+h1#main_title {
+}
+h2.title_2 {
+}
+h3.title_3 {
+}
+h4.title_4 {
+}
+h5.title_5 {
+}
+h6.title_6 {
+}
+
+/* Between every IF CONTENTS */
+div.contents {
+}
+
+/* Default if not set in post */
+p.DOMAIN {
+}
+
+br.DOMAIN {
+}
+
+a.link {
+}
+
+a.anchor_link {
+}
+
+abbr.DOMAIN {
+}
+
+img.DOMAIN_image {
+}
+
+code.icode {
+}
+
+ul.DOMAIN {
+}
+li.DOMAIN
+
+strong.strong {
+}
+b.bold {
+}
+em.em{
+}
+i.italic {
+}
+del.del {
+}
+u.underline {
+}
+cite.cite {
+}
+span.custom {
+}
+
+/* Block_code */
+code.DOMAIN {
+}
+pre.bcode {
+}
+p.bcode {
+}
+
+/* section for author and date */
+section#article_infos {
+}
+span#article_author {
+}
+span#article_pub {
+}
+span#article_code {
+}
+a#article_code_link {
+}
+
+/*
+ * sidebar
+*/
+
+aside#sidebar {
+}
+h1#sidebar_title {
+}
+ul#sidebar_list {
+}
+li.sidebar_item {
+}
+a.sidebar_item_link {
+}
+h2.sidebar_item_title {
+}
+p.sidebar_item_about {
+}
+
+
+/*
+ * footer
+*/
+footer#footer_page {
+}
+/* Block*/
+div#footer_infos {
+}
+h1#footer_site_title {
+}
+p#footer_about {
+}
+/* Block */
+div#footer_references {
+}
+ul.footer_items {
+}
+li.footer_item {
+}
+a.footer_item_link {
+}
+/* Block */
+div#footer_credits {
+}
+p.footer_copyright {
+}
+p.footer_generator {
+}
+
+
diff --git a/src/var/lib/tyto/program/args.py b/src/var/lib/tyto/program/args.py
new file mode 100644
index 0000000..102b140
--- /dev/null
+++ b/src/var/lib/tyto/program/args.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Manage arguments from command line
+# File: /var/lib/tyto/program/args.py
+#----------------------------------------------------------------------
+
+#------------
+# funny stats
+#------------
+# lines:
+# functions:
+# comments:
+#----------------------------------------------------------------------
+
+#**********************************************************************
+
+import os, sys
+import infos
+
+
+# Arguments from command line
+# tyto [action] [target]
+#----------------------------
+actions = \
+(
+'check',
+'edit',
+'edit-about',
+'edit-db',
+'edit-wip',
+'edit-www',
+'help',
+'new',
+'show',
+'show-about',
+'show-db',
+'show-wip',
+'show-www',
+'status',
+'wip',
+'publish'
+)
+
+pass_actions = (
+ 'new'
+
+ )
+
+# Actions that needs to check for article's database
+pass_db = \
+(
+'check',
+'edit',
+'edit-db',
+'edit-wip',
+'edit-www',
+'publish',
+'show',
+'show-about',
+'show-db',
+'show-wip',
+'show-www',
+'status',
+'wip',
+)
+
+pass_targets = \
+(
+'added',
+'again',
+'updated',
+'domain',
+'footer',
+'metas',
+'navbar',
+'sidebar',
+'stats',
+'template'
+)
+
+pass_status = \
+(
+'domain',
+)
+
+multi_chk = ('added', 'again', 'updated')
+
+action = ''
+target = ''
+noaction = False
+
+# action
+#-------
+try: action = sys.argv[1]
+except: noaction = True
+
+# With no argument, show help
+if noaction:
+ infos.tyto(target)
+ sys.exit(0)
+
+# Unused argument [action]
+act_err = False
+if not action in actions:
+ act_err = True
+
+
+# target
+#-------
+try: target = sys.argv[2]
+except: target = ''
+
+
+# Set action and target for binary
+def set_action():
+ return(action)
+
+def set_target():
+ return(target)
diff --git a/src/var/lib/tyto/program/check.py b/src/var/lib/tyto/program/check.py
index 2bf064f..8ce9dac 100644
--- a/src/var/lib/tyto/program/check.py
+++ b/src/var/lib/tyto/program/check.py
@@ -1,11 +1,28 @@
#!/usr/bin/env python3
-# Name: Tyto - Littérateur
-# Type: Global functions for check
-# Description: Check article contents. Create Stats and Database
-# file: wip.py
-# Folder: /var/lib/tyto/scripts/
-# By echolib (XMPP: im@echolib.re)
-# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Manage 'check' argument.
+# Check article's validity in Tyto's format
+# File: /var/lib/tyto/program/check.py
+#----------------------------------------------------------------------
#------------
# funny stats
@@ -18,1007 +35,978 @@
#**********************************************************************
# Import needed libs
-import sys, os, re, datetime
-import domain, log, wip
+import time, importlib, sys, os, re, datetime
+from datetime import datetime
+from time import gmtime, strftime
+import args, dom, logs, status, db, form, tyto, langs, wip
-Post_Err = False
-post_uri = ''
-msg_log = ''
-
-# markers (regex)
-m_anchor = r'^>>' # No close marker
-
-# 'Open' , 'Close' , 'Name'
-markers_reg = [
- [ r'^\($|^\(\s' , r'^\)$' , 'paragraphs' ],
- [ r'^\(\($|^\(\(\s', r'^\)\)$', 'quotes' ],
- [ r'^\[\[$|^\[\[\s', r'^\]\]$', 'brut codes' ],
- [ r'^-\($|^-\(\s' , r'^-\)$' , 'lists' ]
- ]
-
-#=======#
-# Tools #
-#=======#--------------------------------------------------------------
-#===============================#
-# Define article IDs, if exists #
-# Used for check, wip... #
-#-------------------------------#
-def post_IDs(file_post):
- # Check if argument's file (.tyto) or exit
- if not file_post:
- print(':( Unused argument file')
- sys.exit(1)
-
- # Set HTML file from file_post
- file_html = file_post.replace('.tyto','.html')
-
- # Check if file exists or exit
- global post_uri
- post_uri = '%s%s'%(domain.domain_articles, file_post)
- if not os.path.exists(post_uri):
- print(':( Unused file: %s'%post_uri)
- sys.exit(1)
-
- # From argument file_post
- # Set WEB link prefix. Count / in uri
- global weburi
- slash = 0
- weburi = ''
-
- for s in file_post:
- if s == '/': slash += 1
-
- if slash == 0:
- weburi = './'
- else:
- for i in range(0,slash):
- weburi = '%s../'%weburi
-
- # Get Hash from article's content
- global hash_chk
- hash_chk = get_filesum(post_uri,True)
-
- # Get Hash ID from URI
- global curr_post_ID, curr_post_db, post_logs
- curr_post_ID = get_filesum(post_uri,False)
-
- # Set database file for this article
- curr_post_db = '%s%s.conf'%(domain.domain_db, curr_post_ID)
-
- # Set and check/create logs file for this article
- post_logs = '%s%s.log'%(domain.domain_logs, curr_post_ID)
-
- #------------------
- # Article Database
- #------------------
- # Check if Article has DB
- # Source its conf and compare
- global db_exist
- try:
- exec(open(curr_post_db).read(),globals())
- db_exist = True
- except:
- db_exist = False
-
- if db_exist:
- # Set HTML file
- global srv_post_wip, srv_post_www
- srv_post_wip = [False, domain.srv_wip + file_html]
- srv_post_www = [False, domain.srv_www + file_html]
-
- # Set to True, if file exists on servers
- if os.path.exists(srv_post_wip[1]): srv_post_wip[0] = True
- if os.path.exists(srv_post_www[1]): srv_post_www[0] = True
-
-#============================================#
-# Check and if not exist, create domain dirs #
-# Also Used to create when new domain set #
-#--------------------------------------------#
-def create_domain_dirs(path_type):
- # Needed direcories when creating a new domain
- if 'all' in path_type:
- all_dirs = [
- domain.srv_wip_template,
- domain.srv_wip_images,
- domain.srv_wip_files,
- domain.srv_www_template,
- domain.srv_www_images,
- domain.srv_www_files,
- domain.domain_articles,
- domain.domain_images,
- domain.domain_files,
- domain.domain_sdb_dir,
- domain.domain_db,
- domain.domain_logs
- ]
- for folder in all_dirs:
- os.makedirs(folder, exist_ok=True)
-
- # Check/Create needed directories (only for 'check' process)
- if 'db' in path_type:
- os.makedirs(domain.domain_db, exist_ok=True)
- os.makedirs(domain.domain_logs, exist_ok=True)
-
-#=======================#
-# Return sum of srcfile #
-# src: True = file #
-# False = string #
-#-----------------------#
-def get_filesum(path,src):
- from hashlib import blake2b
- file_sum = blake2b(digest_size=4)
- if src:
- file_sum.update(open(path, 'rb').read())
- else:
- file_sum.update(path.encode())
- return file_sum.hexdigest()
+domain_dir = post_err = multi_chk = False
#=========================#
# Manage Argument 'check' #
# Start checking article #
#-------------------------#--------------------------------------------
-def manage_check(file_post, Force):
- # Set needed IDs
- post_IDs(file_post)
+def manage(target):
+ dom.valid()
+
+ # target needed
+ if not target:
+ logs.out("5", '[target]', True)
+
+ # update, again, newer
+ elif target in args.multi_chk:
+ check_all(target)
- # Start checking Post, then exit if errors found in
- check_article(post_uri, Force)
+ # modules (sidebar...)
+ elif target in args.pass_targets:
+ check_module(target)
+ return
+
+ # Unused file
+ elif not db.post:
+ logs.out("1", db.uri_file, True)
+
+ # Already check
+ elif db.exists and not db.old_chk:
+ print(' ├ [%s] > %s'%(db.title, db.uri_file))
+ logs.out("20", '%s > %s'%(db.date_chk, db.uri_file), False)
+ answer = form.asking(' ├ %s%s '%(
+ langs.site.check_a, langs.site.q
+ ), True)
+ check_process(target)
+
+ else:
+ check_process(target)
+
+ # Article is not valid
+ if post_err and not target in args.multi_chk:
+ logs.out("7", db.uri_file, True)
+
+
+#===============================#
+# Argument is a module name #
+# Check if modules files exists #
+#-------------------------------#
+def check_module(target):
+ if target == "domain":
+ status.check(target)
+ return
+
+ try:
+ unused_f = False
+ logs.out("60", '', False)
+ for t in dom.modules[target]:
+ if not os.path.exists(t):
+ logs.out("24", t, False)
+ unused_f = True
+ if not unused_f:
+ logs.out("28", "%s"%langs.log.all_ok, False)
+ except:
+ logs.out("28", "%s + %s"%(args.action, target), True)
-#==========================#
-# Pre-processing post file #
-#--------------------------#
-def check_article(post_uri,Force):
- global article
- # Check needed directories. Create if not exist
- create_domain_dirs('db')
+#==============================================#
+# Argument all #
+# check all realdy checkded articles #
+# but check only if article source has changed #
+#----------------------------------------------#
+def check_all(option):
+ global multi_chk
+ multi_chk = True
+ found = False
- # Create logs
- #-------------
- if not os.path.exists(post_logs):
- file = open(post_logs, "w")
- file.write('')
- file.close()
- msg_log = 'Log > Create logs file for %s in %s\n'%(
- post_uri, post_logs)
- log.append_f(post_logs,msg_log,0)
+ # With [added], article must NOT have a DB
+ # Search every .tyto in domain from articles/
+ # Then Do a check, and return
+ #-----------------------------------------
+ if option == "added":
+ for root, dirs, files in os.walk(dom.articles_d[:-1]):
+ for file in files:
+ if (file.endswith('.tyto')):
+ uri_file = "%s/%s"%(root, file)
+ uri_id = tyto.get_filesum(uri_file, False)
+ args.target = uri_file.rsplit(dom.articles_d)[1]
+ db_config = "%s%s.config"%(dom.articles_db_d, uri_id)
+ if os.path.exists(db_config):
+ continue
+
+ importlib.reload(db)
+ check_process(args.target)
+ print()
+ return
- # Article Database
- #------------------
- # In check process: no values (kept from db, if exists)
- global hash_wip, hash_www, time_wip, time_www
- hash_wip = time_wip = hash_www = time_www = ''
- if db_exist:
- # backup hash_wip/www values
- hash_wip = post_wip[0];time_wip = post_wip[1]
- hash_www = post_www[0];time_www = post_www[1]
- # Compare chk Hashes.
- # Pass if Force, and not same
- if hash_chk == post_chk[0] and not Force:
- print(':) Check was already done, on',post_chk[1])
- sys.exit(0)
+ # With [updated], Article must have changed
+ # with [again], force check
+ # Article must have a DB
+ #------------------------------------------
+ for post_db in os.listdir(dom.articles_db_d):
+ if post_db.endswith('.config'):
+ # Load DB
+ post_db = '%s%s'%(dom.articles_db_d, post_db)
+ try:
+ exec(open(post_db).read(),globals())
+ args.target = short_src
+ importlib.reload(db)
+ except:
+ continue
+
+ if option == 'updated' and not db.chk_updated: continue
+ if option == 'again' and not db.chk_again: continue
+
+ found = True
+ check_process(args.target)
+
+ if post_err: logs.out("44", args.target, False)
+
+ if not found: logs.out("28", args.action, True)
+
- # Processing
- #-----------
- # Prepare: put post's contents file in headers and article strings
- post_to_strings(post_uri)
+#========================#
+# Check articles process #
+#------------------------#
+def check_process(target):
+ if not db.post:
+ logs.out("1", db.post, True)
+
+ global post_bottom, article_bottom
+ global date_wip, hash_wip, date_www, hash_www, post_bottom
+ global post_err
+
+ date_wip = hash_wip = date_www = hash_www = ''
+ post_err = False
+
+ # Set values for wip and www from DB
+ if db.exists:
+ date_wip = db.date_wip
+ hash_wip = db.hash_wip
+ date_www = db.date_www
+ hash_www = db.hash_www
+
+ # Get extension from target, set short uris
+ ext_src = os.path.splitext(target)
- # Send to log
- msg_log = 'Check > Article: %s. logs: %s'%(post_uri,post_logs)
- log.append_f(domain.tyto_logs,msg_log,0)
+ # Get uri after articles/ (no starting / in value)
+ global src_post_short_uri, srv_post_short_uri, direc_src
+ global srv_post_wip_uri, srv_post_www_uri
- # Check markers in headers
- check_post_header(headers.rsplit('\n'))
-
- # Protect bCodes, keep markers for stats, before checking other markers
- wip.convert_bcodes(article.rsplit('\n'),
- '[[', ']]',
- domain.domain_css)
- article = wip.article_temp
-
- # Protect quotes, keep markers for stats, before checking other markers
- wip.convert_bcodes(article.rsplit('\n'),
- '((', '))',
- domain.domain_css)
- article = wip.article_temp
-
- # Protect iCodes, keep markers for stats
- wip.convert_icodes(article.rsplit('\n'), domain.domain_css)
- article = wip.article_temp
-
- # Check titles/comments in article
- check_article_titles(article.rsplit('\n'))
-
- # Check links (anchors)
- check_links_anchors(article)
-
- # Check other markers
- check_article_markers(article)
-
- # Error in article
- #---------------------
- if Post_Err:
- print(':( Invalid article. Needs corrections')
- sys.exit(1)
-
- # No Domain registred yet
+ src_post_short_uri = db.uri_file.rsplit(dom.articles_d)[1]
+ srv_post_short_uri = src_post_short_uri.replace(ext_src[1], '.html')
+ srv_post_wip_uri = dom.srv_wip + srv_post_short_uri
+ srv_post_www_uri = dom.srv_www + srv_post_short_uri
+ direc_src = src_post_short_uri.split("/")[-1]
+ direc_src = src_post_short_uri.rsplit(direc_src)[0]
+
+ # Get sub_uri for HTML
+ global sub_uri
+ sub_uri = db.uri_file.rsplit('articles/')[1]
+ sub_nbr = sub_uri.count('/')
+ if sub_nbr == 0: sub_uri = './'
+ else: sub_uri = sub_nbr * '../'
+
+ # Set HTTP link for wip and www
+ global http_www, http_wip
+ if srv_post_short_uri.endswith('index.html'):
+ http_www = "%s/%s"%(dom.www_url, direc_src)
+ http_wip = '%s/%s'%(dom.wip_url, direc_src)
+ else:
+ http_www = "%s/%s"%(dom.www_url, srv_post_short_uri)
+ http_wip = '%s/%s'%(dom.wip_url, srv_post_short_uri)
+
+ # Start checking processes
#-------------------------
- # If in a domain Process to DB
- if not domain.domain_active:
- print(':/ Article is Ok, but will not be converted')
- sys.exit(0)
+ # Convert file to string
+ # Also check for separator and empty article
+ file_to_string()
- # No Error...
- #------------
- # Temp post file for this article to use with wip action
- # Set
- global post_tmp
- post_tmp = '%s%s.wip'%(domain.domain_db, curr_post_ID)
+ # Check for icodes, bcodes, quotes
+ # check icodes marks on same line
+ if_icodes_bcodes_quotes(post_bottom)
+
+ # Protect block-codes and quotes
+ if bcode or quote:
+ tyto.protect_bcodes_quotes('check', post_bottom)
+ post_bottom = tyto.protect_article
+
+ # Protect inline-codes
+ if icode:
+ tyto.protect_icodes(post_bottom)
+ post_bottom = tyto.protect_article
+
+ # Check header tags configurations
+ check_needed_tags(post_header.rsplit('\n'))
+ if post_err:
+ if db.exists and tyto.exists(db.config):
+ os.remove(db.config)
+ return
+ print(' │\n ├ [%s] > %s'%(title, db.uri_file))
+
+ check_opt_tags(post_header.rsplit('\n'))
+ if post_err:
+ if db.exists and tyto.exists(db.config):
+ os.remove(db.config)
+ return
+
+
+ # Check for valid contents
+ check_content(post_bottom)
+
+ # Remove db (if exists) on post error and return
+ if post_err:
+ if db.exists and tyto.exists(db.config):
+ os.remove(db.config)
+ return
+
+ # COunt words
+ count_words(post_bottom.rsplit("\n"))
+
+ # Create article's database
+ create_database()
+
+
+#=================================#
+# Create string article from file #
+# Check if separator or exit #
+#---------------------------------#
+def file_to_string():
+ global article, post_header, post_bottom
+ global ln_header, ln_bottom, ln_article
+
+ post_header = post_bottom = ''
+ sep = content = False
+ article = open(db.uri_file, 'r').read()
- # Create NEW file
- file = open(post_tmp, "w")
- file.write('')
for line in article.rsplit('\n'):
- if len(line) == 0 : continue
- elif line.startswith('# ') : continue
- elif line.startswith('##') : continue
- file.write(line + '\n')
- file.close()
+ if line.startswith('-----'):
+ if not sep:
+ sep = True
+ continue
+
+ if sep:
+ if line: content = True
+ if not post_bottom: post_bottom = line
+ else: post_bottom = '%s\n%s'%(post_bottom, line)
+ else:
+ if not post_header: post_header = line
+ else: post_header = '%s\n%s'%(post_header, line)
- # Create DB
- create_DB(curr_post_db)
- print(':) Article is Ok and ready to "tyto wip"')
+ # Check if separator or exit
+ if not sep:
+ logs.out("6", '"-----" > %s'%db.uri_file, True)
+
+ if not content:
+ if db.exists and tyto.exists(db.config):
+ os.remove(db.config)
+ logs.out("18", db.uri_file, True)
+
+ # +2 > (start at 0 + separator line)
+ ln_article = len(article.splitlines()) + 1
+ ln_header = len(post_header.splitlines()) + 2
+ ln_bottom = len(post_bottom.splitlines()) + 2
+
#=========================================#
-# Put file in strings => headers, article #
-# But, check if separator is defined... #
-#-----------------------------------------#
-def post_to_strings(post_uri):
- # Check if separator from header and article ; else: exit
- file_string = open(post_uri,'r').read()
- if not '-----' in file_string:
- msg_log = 'Unused separator "-----" (header,post)'
- log.append_f(post_logs,msg_log,1)
- sys.exit(0)
+# Count words in article. #
+# Quotes, block-codes, icode = 1 per each #
+# At db creation, remove title numbers #
+#-----------------------------------------"
+def count_words(post_bottom):
+ global post_words
+ post_words = 0
+ for line in post_bottom:
+ if not line \
+ or line.startswith(tyto.nolinewords) \
+ or line.startswith("#") and \
+ not line.startswith(tyto.titles_tags):
+ continue
+
+ post_words = post_words + len(line.strip().split(" "))
- # From Separator...
- # Split post in 2 strings for Headers and Article
- global headers, article, post_lines, headers_ln
- post_lines = file_string.rsplit('\n')
- headers = article = ''
- post = False
+#=============================================#
+# Check if bcodes and quotes #
+# Check inline code, for markers on same line #
+# Stats for titles, quotes, bcodes, uniq_ancs #
+#---------------------------------------------#
+def if_icodes_bcodes_quotes(post_bottom):
+ global icode, quote, bcode, post_err
+ global post_titles, nbr_quotes, nbr_bcodes, nbr_ancs, post_comments
+ global post_images, post_raws, fcodes
- for line in post_lines:
- if line.startswith('-----'):
- post = True
+ icode = quote = in_quote = bcode = in_bcode = in_bq = False
+ post_titles = nbr_quotes = nbr_bcodes = nbr_ancs = 0
+ post_images = post_comments = post_raws = fcodes = 0
+
+ for ln, line in enumerate(post_bottom.rsplit('\n'), 1):
+ # Stat Comments, Titles, Anchors
+ if not line:
+ continue
+
+ # quotes
+ elif line.startswith(tyto.words_tags[11][0]) and not in_bcode:
+ quote = in_quote = in_bq = True
+ continue
+ elif line.startswith(tyto.words_tags[11][1]):
+ in_quote = False
+ nbr_quotes += 1
continue
- # In post content
- if post:
- article = '%s\n%s'%(article,line)
+ # bcode
+ elif line.startswith(tyto.words_tags[12][0]) and not in_quote:
+ bcode = in_bcode = in_bq = True
+ continue
+ elif line.startswith(tyto.words_tags[12][1]):
+ in_bcode = False
+ nbr_bcodes += 1
+ continue
- # In post header
- else:
- headers = '%s\n%s'%(headers,line)
-
- # Get number lines in headers, min line separator
- headers_ln = len(headers.split("\n")) - 1
-
-#=================#
-# HEADERS CONTENT #
-#=================#
-#====================================#
-# Init stats, arrays... #
-# Loop into headers to check markers #
-#------------------------------------#
-def check_post_header(headers):
- global Post_Err
- global stats_links_uniq, stats_files_uniq, stats_images_uniq
- global stats_links_p, stats_files_p, stats_images_p
- global stats_links
- global title, about, author, tags, date
-
- # Set Stats
- stats_links_uniq = stats_files_uniq = stats_images_uniq = 0
- stats_links = stats_links_p = stats_files_p = stats_images_p = 0
-
- # Set Mandatory marker. 0:True/False 1:ln 2: Content
- title = about = author = tags = date = ('','','')
-
- # Set Optional markers. 0:ln 1:Name 2:URL 3:Alt
- link = file = image = ('','','','')
-
- #-----------------------
- # Loop in headers string
- #-----------------------
- for ln, line in enumerate(headers, 1):
+ if in_bq:
+ continue
- #-----------------------
- # Set mandatory markers
- #-----------------------
- if line.startswith('title:'):
- title = (True,ln,line.split('title:')[1].lstrip())
- if line.startswith('about:'):
- about = (True,ln,line.split('about:')[1].lstrip())
- if line.startswith('author:'):
- author = (True,ln,line.split('author:')[1].lstrip())
- if line.startswith('tags:'):
- tags = (True,ln,line.split('tags:')[1].lstrip())
- if line.startswith('date:'):
- date = (True,ln,line.split('date:')[1].lstrip())
+ elif line.startswith(tyto.titles_tags):
+ post_titles += 1
+ continue
+ elif line.startswith('#'):
+ post_comments += 1
+ continue
+ elif line.startswith(tyto.single_tags[1][0]):
+ nbr_ancs += 1
+ continue
+ elif line.startswith('_image:'):
+ post_images += 1
+ elif line.startswith('_raw:'):
+ post_raws += 1
+ elif line.startswith('_code:'):
+ fcodes += 1
- #----------------------
- # Set optional markers
- #----------------------
- # links:
- if line.startswith('link:'):
- # Create variable array
- stats_links_uniq += 1
- check_links(line, ln, stats_links_uniq)
- # files:
- if line.startswith('file:'):
- # Create variable array
- stats_files_uniq += 1
- check_files(line, ln, stats_files_uniq)
- # images:
- if line.startswith('image:'):
- # Create variable array
- stats_images_uniq += 1
- check_images(line, ln, stats_images_uniq)
+ # icodes
+ elif tyto.words_tags[9][0] or tyto.words_tags[9][1] in line:
+ icode_m1 = icode_m2 = 0
+ icode_m1 = line.count(tyto.words_tags[9][0])
+ icode_m2 = line.count(tyto.words_tags[9][1])
+ if icode_m1 != icode_m2:
+ logs.out("8", 'L=%s. icode "%s" + "%s" > %s'%(
+ ln + 1 + ln_header,
+ tyto.words_tags[9][0], tyto.words_tags[9][1],
+ db.uri_file
+ ), False
+ )
+ post_err = True
+ else:
+ icode = True
- #-------------------------------
- # Check valid Mandatory markers
- #-------------------------------
- if_mandat_marker('title:', title)
- if_mandat_marker('about:', about)
- if_mandat_marker('author:', author)
- if_mandat_marker('tags:', tags)
- if_mandat_marker('date:', date)
- if date:
+
+#=========================================#
+# Check needed tags from article's header #
+# Each one must be uniq. Only first taken #
+#-----------------------------------------#
+def check_needed_tags(post_header):
+ global post_err
+ global title, author, about, tags, date
+ global stat_tags
+
+ title = author = about = tags = ''
+ date = ()
+ stat_tags = 0
+
+ # Check post header for each needed tags
+ for tag in tyto.needed_header_tags:
+ for ln, line in enumerate(post_header, 1):
+ # Break if already set
+ if globals()[tag]: break
+
+ # Set data from tag
+ if line.startswith('%s:'%tag):
+ globals()[tag] = line.rsplit('%s:'%tag)[1].lstrip()
+ globals()[tag] = tyto.convert_altname(globals()[tag])
+ # Stat for "tags:"
+ if tag == 'tags':
+ stat_tags = len(globals()[tag].strip().split(","))
+
+ # Check if unused needed tags
+ for tag in tyto.needed_header_tags:
+ if not globals()[tag]:
+ logs.out("38", '%s:'%tag, False)
+ post_err = True
+
+
+
+ # Check date format
+ # Set french format in post DB
+ if not post_err:
check_date(date)
-
- #------------------------------
- # Check valid Opitonal markers
- #------------------------------
-#===================#
-# MANDATORY Markers #
-#===================#--------------------------------------------------
-#=====================================#
-# Check if marker is used and defined #
-#-------------------------------------#
-def if_mandat_marker(marker, m_in):
- global Post_Err
-
- if not m_in[0]:
- msg_log = 'Unused needed marker "%s"'%marker
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- elif not m_in[2]:
- msg_log = 'Line %s. Undefined marker "%s"'%(
- m_in[1], marker
- )
- log.append_f(post_logs,msg_log,1)
-
#================================#
# Check Date format and validity #
# Create False date_check #
#--------------------------------#
def check_date(date):
- from datetime import datetime
- from time import gmtime, strftime
- import time
- global Post_Err, date_check
+ global post_err, date_tr
# Check if article date is valid (True)
+ date_tr = date
fmt_article = "%Y-%m-%d"
try:
- bool(datetime.strptime(date[2], fmt_article))
+ bool(datetime.strptime(date, fmt_article))
except ValueError:
- Post_Err = True
- msg_log = 'Line %s. Invalid date: %s'%(date[1],date[2])
- log.append_f(post_logs,msg_log,1)
+ post_err = True
+ logs.out("3", 'date: "%s" (YYYY-MM-DD) > %s'%(
+ date, db.uri_file
+ ), False)
# Create date_check (epoch) from article's Date + now TIME
- if not Post_Err:
- fmt_check = '%Y-%m-%d %H:%M:%S'
+ if not post_err:
+ fmt_check = '%Y-%m-%d %H:%M:%S'
time_check = strftime("%H:%M:%S", gmtime())
- date_check = date[2] + ' ' + time_check
- date_check = time.mktime(time.strptime(date_check,fmt_check))
+ date_check = date + ' ' + time_check
+ date_check = time.mktime(time.strptime(date_check, fmt_check))
+
+ # Set franch format in post DB
+ if dom.lang_site == 'fr':
+ date = date.rsplit('-')
+ date_tr = date[2] + '/' + date[1] + '/' + date[0]
-#==================#
-# OPTIONAL Markers #
-#==================#---------------------------------------------------
-#===============================#
-# Statistics #
-# Return term number in article #
-#-------------------------------#
-def stats_counter(term):
- term = r"%s"%term
- return(sum(1 for match in re.finditer(r'\b%s\b'%term, article)))
+#===========================================#
+# Check optional tags from article's header #
+# Multiple settings for each on 3 lines #
+#-------------------------------------------#
+def check_opt_tags(post_header):
+ global stat_links, stat_images, stat_files, stat_raws, stat_codes
+ global post_links, stat_snpics, snpic_url, stat_abbrs, post_abbrs
+ global post_files, opt_tags_post_name
+ global files_post
-#============================
-# Check optional marker set #
-# Called from #
-# - check_links #
-# - check_files #
-# - check_images #
-#---------------------------#
-def if_option_marker(marker, m_in):
- global Post_Err
+ # Statistics
+ stat_links = stat_images = stat_files = stat_raws = stat_codes = 0
+ post_links = post_files = stat_snpics = post_abbrs = stat_abbrs = 0
+ files_post = (())
- if not m_in[1]:
- msg_log = 'Line %s. Unused NAME for marker "%s"'%(
- m_in[0], marker
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
+ # Set default post pic
+ snpic_url = '%s/template/%s'%(dom.www_url, dom.logo)
+
+ # Search for term in article
+ opt_tags_post_name = \
+ {
+ "link" : "_%s",
+ "image" : "_image:%s",
+ "file" : "__%s",
+ "raw" : "_raw:%s",
+ "code" : '_code:%s',
+ "abbr" : "(%s)"
+ }
+
+ # Check post header for each optional tags
+ for tag in tyto.opt_header_tags:
+ for ln, line in enumerate(post_header, 1):
+ if line.startswith('%s:'%tag):
+ check_3lines(tag, ln, line)
- if not m_in[2]:
- msg_log = 'Line %s. Unused URL for marker "%s"'%(
- m_in[0]+1, marker
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- if not m_in[3]:
- msg_log = 'Line %s. Unused Alt-Text for marker "%s"'%(
- m_in[0]+2, marker
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
+#===================================#
+# Check if registred value is valid #
+#-----------------------------------#
+def valid_data_name(tag, name):
+ global post_err
+ data_inv = ''
+ data_err = False
+
+ for c in name:
+ if c in tyto.chrs_invalid:
+ post_err = True
+ data_err = True
+ data_inv = '%s%s'%(data_inv, str(c))
-#=================================#
-# Check every marker "links:" #
-# For line, from loop header file #
-# Also, create Stats #
-#---------------------------------#
-def check_links(line, ln, stats_links_uniq):
- global Post_Err, stats_links_p
+ if data_err:
+ logs.out("3", '"%s: %s" > %s'%(tag, name, data_inv), False)
+ return False
+
+ return True
- # Create variable array
- link_nbr = 'link_%s'%stats_links_uniq
- link_name = line.split('link:')[1].lstrip()
- link_url = headers.rsplit('\n')[ln].lstrip()
- link_alt = headers.rsplit('\n')[ln+1].lstrip()
-
- link = (
- ln,
- link_name,
- link_url,
- link_alt
- )
-
- # Set/Check values to check in header
- globals()[link_nbr] = link
- if_option_marker('link:', globals()[link_nbr])
-
- # Check NAME in article
- link_page = '_%s'%link_name
- if not link_page in article:
- msg_log = 'Unused "%s" for marker "link:" in article"'%link_page
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- if Post_Err: return
- # Set final marker_N
- link = (
- '%s'%link_name,
- link_url,
- link_alt
- )
- globals()[link_nbr] = link
-
- # Stats: count occurence
- stats_links_p = stats_counter(link_page)
-#=================================#
-# Check every marker "files:" #
-# For line, from loop header file #
-# Also, create Stats #
-#---------------------------------#
-def check_files(line, ln, stats_files_uniq):
- global Post_Err
- global stats_files_p
+#============================================#
+# Do stats, check 3 lines tags from ln #
+# Set data for each 2 next lines #
+# Exception for snpic (Different conditions) #
+#--------------------------------------------#
+def check_3lines(tag, ln, line):
+ global post_err, db_tag, files_post, post_bottom
+ global post_abbrs, post_links, post_files
- # Create variable array
- file_nbr = 'file_%s'%stats_files_uniq
- file_name = line.split('file:')[1].lstrip()
- file_uri = headers.rsplit('\n')[ln].lstrip()
- file_alt = headers.rsplit('\n')[ln+1].lstrip()
-
- file = (
- ln,
- file_name,
- file_uri,
- file_alt
- )
-
- # Set/Check values to check in header
- globals()[file_nbr] = file
- if_option_marker('file:', globals()[file_nbr])
-
- # Check NAME in article
- file_page = '__%s'%file_name
- if not file_page in article.rsplit('\n'):
- msg_log = 'Unused "%s" for marker "file:" in article"'%file_page
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- # Check URI value (exists and where)
- # In Generic folder /files/
- if file_uri.startswith('@'):
- file_uri = file_uri.replace('@','')
- gen_file = '%s%s'%(domain.domain_files,file_uri)
- if not os.path.exists(gen_file):
- msg_log = 'Unused file for marker "file:" in %s'%gen_file
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- else:
- file_uri = '/files/%s'%file_uri
-
- # From Root articles (www/ in web)
- elif file_uri.startswith('/'):
- file_uri = file_uri[1:len(file_uri)] # No need first / to check
- usr_file = '%s%s'%(domain.domain_articles,file_uri)
- if not os.path.exists(usr_file):
- msg_log = 'Unused file for marker "file:" in %s'%usr_file
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- else:
- file_uri = '/%s'%(file_uri)
-
- # Current or curstom URI
+ stat_tag = "stat_%ss"%tag
+
+ # Create variable for post DB
+ globals()[stat_tag] += 1
+ db_tag = '%s_%s'%(tag, globals()[stat_tag])
+ globals()[db_tag] = (())
+
+ # NAME
+ #-----
+ # ln is legacy (from loop), so take line content
+ # Take only first word if not a long name tag
+ if tag in tyto.opt_tags_long_name:
+ name = line.rsplit('%s:'%tag)[1].lstrip()
else:
- usr_file = '%s%s'%(domain.domain_articles,file_uri)
- if not os.path.exists(usr_file):
- msg_log = 'Unused file for marker "file:" in %s'%usr_file
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
+ name = line.rsplit('%s:'%tag)[1].lstrip().rsplit(' ')[0]
+ if not valid_data_name(tag, name): return
+
+
+ if not name:
+ logs.out("2", 'L=%s. "%s: %s" > %s'%(
+ ln, tag, langs.site.name, db.uri_file
+ ), False)
+ post_err = True
+ if tag == "snpic":
+ return
+
+ # abbr:
+ elif tag == "abbr":
+ name_abbr = name.upper()
+ post_abbrs = post_bottom.count('(%s)'%name_abbr)
+
+ # snpic only needs a Name
+ elif tag == "snpic":
+ check_snpic(name)
+ return
+
+ elif not isin("%s"%(opt_tags_post_name[tag]%name), post_bottom):
+ logs.out("12", '"%s" > %s'%(
+ opt_tags_post_name[tag]%name, db.uri_file
+ ), False)
+ post_err = True
- if Post_Err: return
- #--------------------
- # Set final marker_N
- #--------------------
- file = (
- '%s'%file_name,
- file_uri,
- file_alt
- )
- globals()[file_nbr] = file
+ globals()[db_tag] = ((opt_tags_post_name[tag]%name),)
- # Stats: count occurence
- stats_files_p = stats_counter(file_page)
+ if tag == "file":
+ post_files = post_files + post_bottom.count('__%s'%name)
+ post_bottom = post_bottom.replace('__%s'%name, '[file-link]')
+ elif tag == "link":
+ post_links = post_links + post_bottom.count('_%s'%name)
+
+ # URI/URL and Alt-Text
+ #---------------------
+ # loop 1,2. Next real line from start tag
+ for l in range(1,3):
+ if l == 1: data_log = "URI/URL"
+ elif l == 2: data_log = "Alt-Text"
-#=================================#
-# Check every marker "images:" #
-# For line, from loop header file #
-# Also, create Stats #
-#---------------------------------#
-def check_images(line, ln, stats_images_uniq):
- global Post_Err
- global stats_images_p
-
- # Create variable array
- image_nbr = 'image_%s'%stats_images_uniq
- image_name = line.split('image:')[1].lstrip()
- image_uri = headers.rsplit('\n')[ln].lstrip()
- image_alt = headers.rsplit('\n')[ln+1].lstrip()
-
- image = (
- ln,
- image_name,
- image_uri,
- image_alt
- )
-
- # Set/Check values to check in header
- globals()[image_nbr] = image
- if_option_marker('image:', globals()[image_nbr])
-
- # Check value in article
- image_page = '_image:%s'%image_name
- if not image_page in article.rsplit('\n'):
- msg_log = 'Unused "%s" for marker "image:" in article"'%image_page
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- # Check URI value (exists and where)
- # Set HTML value in DB
- if image_uri.startswith('@'):
- image_uri = image_uri.replace('@','')
- gen_image = '%s%s'%(domain.domain_images,image_uri)
- if not os.path.exists(gen_image):
- msg_log = 'Unused file for marker "imagee:" in %s'%gen_image
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
+ ln += 1
+ data = ''
+
+ try:
+ # ln - 1 as string post_header starts with 0
+ data = post_header.rsplit('\n')[ln - 1].lstrip()
+ if data.startswith(tyto.headers): data = ''
+ except:
+ data = ''
+
+ if not data and not l == 2 and not tag == 'raw':
+ logs.out("2", 'L=%s. "%s" (%s:) > %s'%(
+ ln, data_log, tag, db.uri_file
+ ), False)
+ post_err = True
+
+ # Convert special characters for HTML
+ if not tag in tyto.opt_tags_check_uri:
+ data = tyto.convert_altname(data)
+
+ # For tags having URI, check if file exists
+ # Set data for post DB
+ if l == 1 and tag in tyto.opt_tags_check_uri:
+ check_file_uri(tag, data, ln)
+ if post_err:
+ return
+ globals()[db_tag] = globals()[db_tag] + ((web_uri),)
+ files_post = files_post + (('%s'%web_uri),)
+
else:
- image_uri = '/images/%s'%image_uri
+ if post_err:
+ return
+ globals()[db_tag] = globals()[db_tag] + ((data),)
+
+
+#=====================#
+# Find in post_bottom #
+#---------------------#
+def isin(term, post_file):
+ paterm = re.escape(term)
+
+ if re.search(paterm, post_file): return(True)
+ else: return(False)
+
- # From Root articles (www/ in web)
- elif image_uri.startswith('/'):
- image_uri = image_uri[1:len(image_uri)] # No need first / to check
- usr_file = '%s%s'%(domain.domain_articles,image_uri)
- if not os.path.exists(usr_file):
- msg_log = 'Unused file for marker "image:" in %s'%usr_file
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- else:
- image_uri = '/%s'%(image_uri)
+
+#===================================#
+# Check if file exists #
+# Get filetype, filename, i=var nbr #
+#-----------------------------------#
+def check_file_uri(filetype, filename, ln):
+ global post_err, err, web_uri
- # Current or curstom URI
+ filetypes = ('file', 'raw', 'code')
+
+ # Set file uri from filename
+ # (@ = images/ or files/
+ # / = articles/
+ # Else in post_dir
+ if filename.startswith('@'):
+ if filetype in filetypes:
+ fileuri = dom.files_d + filename[1: len(filename)]
+
+ elif filetype == 'image' or filetype == "snpic":
+ fileuri = dom.images_d + filename[1: len(filename)]
+
else:
- usr_file = '%s%s'%(domain.domain_articles,image_uri)
- if not os.path.exists(usr_file):
- msg_log = 'Unused file for marker "file:" in %s'%usr_file
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
+ post_dir = db.uri_file.split("/")[-1]
+ post_dir = db.uri_file.rsplit(post_dir)[0]
+ if filename.startswith('/'):
+ fileuri = post_dir + filename[1: len(filename)]
+ else:
+ fileuri = post_dir + filename
- if Post_Err: return
- #--------------------
- # Set final marker_N
- #--------------------
- image = (
- '%s'%image_name,
- image_uri,
- image_alt
- )
- globals()[image_nbr] = image
-
- # Stats: count occurence
- stats_images_p = stats_counter(image_page)
-
-
-#=================#
-# ARTICLE CONTENT #
-#=================#
-#=====================================#
-# Main for all markers to check #
-# Loop into header post, till '-----' #
-#-------------------------------------#
-def check_article_markers(article):
- global Post_Err
- global stats_p, stats_bcodes, stats_anchors, stats_quotes
- global stats_lists, stats_lists_u, stats_lists_o
- global stats_titles, stats_comments
-
- stats_p = stats_pe = stats_qe = stats_le = 0
- stats_lists = stats_lists_u = stats_lists_o = 0
- stats_anchors = stats_quotes = 0
- precode = False # Do not treat line if in precode: ((
+ # Check if file exists
+ if not filetype == "snpic" and not tyto.exists(fileuri):
+ logs.out("1", "L=%s. %s > %s"%(ln, fileuri, db.uri_file), False)
+ post_err = True
+ return
- #------------------------
- # Loop lines from article
- #------------------------
- for ln, line in enumerate(article.rsplit('\n'), 1):
-
- # Do not check if in precode: [[ and ]]
- if re.match(markers_reg[2][0], line): precode = True
- elif re.match(markers_reg[2][1], line): precode = False
- if precode: continue
-
- #-------------------------
- # Markers at begining line
- #-------------------------
- # Paragraphs: ( and )
- if re.match(markers_reg[0][0], line): stats_p += 1
- elif re.match(markers_reg[0][1], line): stats_pe += 1
-
- # Lists: (- and -) ; count items with = and + at begining
- elif re.match(markers_reg[3][0], line): stats_lists += 1
- elif re.match(markers_reg[3][1], line): stats_le += 1
- elif line.startswith('='): stats_lists_u +=1
- elif line.startswith('+'): stats_lists_o +=1
-
- # Anchors: <:
- elif re.match(m_anchor, line):
- try : css = line.split(' ')[1]
- except: css = ''
- if not css:
- msg_log = 'Line %s. Unused anchor ID: ">> ID"'%(ln+headers_ln)
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- else:
- stats_anchors += 1
-
- # Quotes
- elif re.match(markers_reg[1][0], line): stats_quotes += 1
- elif re.match(markers_reg[1][1], line): stats_qe += 1
-
- # Check if referenced in header for markers
- for marker_p in '_image:', '_code:', '_brut:':
- if re.match(r'^%s'%marker_p, line):
- m_name = line.split(':')[1][0]
- marker_h = marker_p[1:len(marker_p)]
- if not re.findall(r'%s\s+%s'%(marker_h, m_name), headers):
- msg_log = 'Line %s. Unused marker "%s %s" in header'%(
- ln+headers_ln, marker_h, m_name
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- #------------------------------
- # Check valid contents markers
- #------------------------------
- # Paragraphs
- if stats_p != stats_pe:
- msg_log = 'Unpaired paragraph markers: %s "(" and %s ")"'%(
- stats_p, stats_pe
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- # Precodes
- if stats_bcodes != stats_ce:
- msg_log = 'Unpaired precode markers: %s "[[" and %s "]]"'%(
- stats_bcodes, stats_ce
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- # lists
- if stats_lists != stats_le:
- msg_log = 'Unpaired list markers: %s "-(" and %s "-)"'%(
- stats_lists, stats_le
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- # Quotes
- if stats_quotes != stats_qe:
- msg_log = 'Unpaired quote markers: %s "((" and %s "))"'%(
- stats_quotes, stats_qe
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
- #------------------------------------
- # Markers in text (strongs, bolds...)
- #------------------------------------
- global m_stats
- # Markers around words
- m_words = ('*_', '_*',
- '+_', '_+',
- '/_', '_/',
- '~_', '_~',
- '×_', '_×',
- '-_', '_-',
- '(_', '_)',
- '<_', '_>',
- '\\_', '_\\',
- '=_', '_=',
- '>_', '_<'
- )
- # Init words markers statistics
- m_stats = ['0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0',
- '0', '0'
- ]
- # markers Names (Only used for logs)
- m_names = ('strong',
- 'bold',
- 'emphasis',
- 'deletion',
- 'custom',
- 'underline',
- 'iCode',
- 'iCode',
- 'italics',
- 'cite',
- 'link anchor'
- )
-
- # Count markers, get, check and set stats.
- pos = pos_name = 0
- for marker in m_words:
- m_stats[pos] = article.count(marker)
- #print(marker, m_stats)
- if pos % 2 != 0:
- if m_stats[pos-1] != m_stats[pos]:
- msg_log = 'Unpaired %s markers %s "%s" and %s "%s"'%(
- m_names[pos_name],
- m_stats[pos-1], m_words[pos-1],
- m_stats[pos], m_words[pos]
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- pos_name += 1
- pos += 1
- # Add specific alternative iCode '<_','_>' to legacy iCode stats
- m_stats[12] = m_stats[12] + m_stats[14]
-
-#======================================================#
-# Thses markers needs ref line, in case invalid #
-# As bCodes needs convertion, they must be check first #
-# Done after post_to_strings #
-# Also count stats: bCodes, titles, comments #
-#------------------------------------------------------#
-def check_article_titles(article):
- global Post_Err
- global stats_bcodes, stats_ce
- global stats_comments, stats_titles
-
- stats_bcodes = stats_ce = 0
- stats_comments = stats_titles = 0
- precode = False
-
- #------------------------
- # Loop lines from article
- #------------------------
- for ln, line in enumerate(article, 1):
-
- # Do not check if in precode: [[ and ]]
- if re.match(markers_reg[2][0], line):
- precode = True
- stats_bcodes += 1
- elif re.match(markers_reg[2][1], line):
- precode = False
- stats_ce += 1
- if precode: continue
-
- # Titles
- if line.startswith(r'#'):
- Title_Err = False
- title = line.split(' ', 1)
- ht = line[1]
-
- if ht == ' ' or ht == '#':
- stats_comments += 1
- continue
-
- if ht.isnumeric() and not int(ht) in range(1,7):
- msg_log = 'Line %s. Mismatch title number "%s" (1-6)'%(
- ln + headers_ln, ht)
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- continue
- try:
- title[1]
- if title[1] == ' ' or title[1] == '':
- Title_Err = True
- else:
- stats_titles += 1
- except:
- Title_Err = True
-
- if Title_Err:
- msg_log = 'Line %s. Unused title description "%s ??"'%(
- ln + headers_ln, title[0]
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
-
-
-#===========================#
-# Check links anchors #
-# (if '>> ID' is registred) #
-#---------------------------#
-def check_links_anchors(article):
- global Post_Err
-
- #for ln, line in enumerate(article.rsplit('\n'), 1):
- anchors_link = re.findall(r'\>_(.*?)_\<', article)
- for anchor in anchors_link:
- print('> Anchor found: %s'%(anchor))
- anchor_id = anchor.rsplit(':',1)[0]
- if not re.search(r'\>\> %s'%anchor_id, article):
- msg_log = 'Unused anchor ID ">> %s" from link "%s"'%(
- anchor_id, anchor
- )
- log.append_f(post_logs,msg_log,1)
- Post_Err = True
- Post_Err = True
+ web_uri = '/' + fileuri.replace(dom.articles_d, "")
#====================================#
-# Create Database file for this Post #
-#------------------------------------#
-def create_DB(post_db):
- # Create NEW database for article
- file = open(post_db, "w")
- file.write('')
- file.close()
+# Check for snpic #
+# (Used pic for social network) #
+# Needs: #
+# - a Name #
+# - an image tag set with same name #
+#-----------------------------------#
+def check_snpic(name):
+ global snpic_url, snpic_post, post_err
+
+ try:
+ snpic_post
+ return
+ except:
+ pass
+
+ # Search post_header for image: %name
+ found = False
+ tag = tyto.opt_header_tags[1]
+ for ln, line in enumerate(post_header.rsplit('\n'), 1):
+ if line.startswith('%s:'%tag):
+ try:
+ check_name = line.rsplit('%s:'%tag)[1].lstrip().rsplit(' ')[0]
+ except:
+ check_name = ''
+
+ if check_name == name:
+ found = True
+ snpic_post = post_header.rsplit('\n')[ln].lstrip()
+ check_file_uri("snpic", snpic_post, ln)
+ break
- # time of check at creating DB
- global time_chk
- time_chk = log.nowdate()
+ if not found:
+ logs.out("27", '"%s: %s" > %s'%(tag, name, db.uri_file), False)
+ post_err = True
+ return
+
+ snpic_post = True
+ snpic_url = dom.www_url + web_uri
+
+
+#==========================================#
+# Check anchors target and links #
+# Check for duplicate anchor name target #
+# Cannot have anchors links without target #
+#------------------------------------------#
+def check_anchors():
+ global post_err
+
+ # Anchor target
+ if nbr_ancs > 0:
+ # Create anchors names targets
+ anchors_names = (())
+ for line in post_bottom.rsplit('\n'):
+ if line.startswith(tyto.single_tags[1][0]):
+ name = tyto.get_css(line)
+ anchors_names = anchors_names + ((name),)
+
+ # Check for uniq anchor_name
+ for name in list(anchors_names):
+ if anchors_names.count(name) > 1:
+ post_err = True
+ logs.out("15", '"%s %s" > %s'%(
+ tyto.single_tags[1][0], name, db.uri_file
+ ), False)
+ break
+
+ # Anchor link
+ if post_anchors == 0:
+ return
- # Specific statistics for links (add all)
- stats_links = stats_links_p + stats_files_p + stats_images_p
-
- # log
- msg_log = 'Create Database: %s'%post_db
- log.append_f(post_logs,msg_log,0)
-
- # Main Post Conf
- lines_conf = (
- '# Domain',
- 'post_domain = "%s"'%domain.domain_name,
- '\n# Metas & URIs',
- 'post_file = "%s"'%post_uri,
- 'post_ID = "%s"'%curr_post_ID,
- 'post_db = "%s"'%post_db,
- 'post_tmp = "%s"'%post_tmp,
- '\n# Article Status',
- 'post_chk = ("%s","%s")'%(hash_chk,time_chk),
- 'post_wip = ("%s","%s")'%(hash_wip,time_wip),
- 'post_www = ("%s","%s")'%(hash_www,time_www),
- '\n# Mandatory Headers',
- 'post_title = "%s"'%title[2],
- 'post_about = "%s"'%about[2],
- 'post_tags = "%s"'%tags[2],
- 'post_date = "%s"'%date[2],
- 'post_check = "%s" # └+CheckTime'%date_check,
- 'post_author = "%s"'%author[2],
- '\n# Optional Headers'
- )
- for line_conf in lines_conf:
- domain.append_f(post_db, line_conf)
-
- # Optional headers to Post conf
- # Add every links: array found to DB, one per line
- if stats_links_uniq > 0:
- for n in range(1,stats_links_uniq+1):
- m = 'link_%s'%n
- domain.append_f(post_db,'%s = %s'%(m,globals()[m]))
+ # Article has anchors links but no anchor target
+ elif nbr_ancs == 0:
+ logs.out("12", "%s [NAME] > %s"%(
+ tyto.single_tags[1][0], db.uri_file
+ ), False)
+ post_err = True
+ return
- # Add every files: array found to DB, one per line
- if stats_files_uniq > 0:
- for n in range(1,stats_files_uniq+1):
- m = 'file_%s'%n
- domain.append_f(post_db,'%s = %s'%(m,globals()[m]))
+ # Check in anchor link has target one
+ else:
+ for ln, line in enumerate(post_bottom.rsplit('\n'), 1):
+ # Anchor link
+ if tyto.words_tags[0][0] and tyto.words_tags[0][1] in line:
+ anchors = re.findall(r">_(.*?)_<", line)
+ for anchor in anchors:
+ anchor_name = anchor.rsplit(':')[0].lstrip()
+ if not anchor_name in anchors_names:
+ logs.out("12", 'L=%s. anchor "%s" > %s'%(
+ ln + 1 + ln_header, anchor_name, db.uri_file
+ ), False)
+ post_err = True
+
- # Add every images: array found to DB, one per line
- if stats_images_uniq > 0:
- for n in range(1,stats_images_uniq+1):
- m = 'image_%s'%n
- domain.append_f(post_db,'%s = %s'%(m,globals()[m]))
+#===========================#
+# Check tags in post_bottom #
+#---------------------------#
+def check_content(post_bottom):
+ global post_err
+
+ # Check tags for words (strongs, italics...)
+ # Set stats for each one
+ #-------------------------------------------
+ for tag in tyto.words_tags:
+ c_opened = c_closed = 0
+
+ if tag[5] == 'w':
+ c_opened = post_bottom.count(tag[0])
+ c_closed = post_bottom.count(tag[1])
+ # Useless tag now, replace
+ post_bottom = post_bottom.replace(tag[0], '')
+ post_bottom = post_bottom.replace(tag[1], '')
+ elif tag[5] == 't':
+ for line in post_bottom.rsplit('\n'):
+ if line.startswith(tag[0]): c_opened += 1
+ if line.startswith(tag[1]): c_closed += 1
+
+ if c_opened != c_closed:
+ logs.out("8", '%s "%s" + "%s" > %s'%(
+ tag[4], tag[0], tag[1], db.uri_file
+ ), False)
+ post_err = True
+ return # Useless and could code errors to check nexts
+ else:
+ globals()['post_%s'%tag[4]] = int(c_opened)
+
+
+ # Check if anchor has target
+ # Count anchors target
+ #---------------------------
+ check_anchors()
+
+
+ # Lists: check if contents are valid
+ #-----------------------------------
+ if post_lists > 0:
+ inlist = False
+ for ln, line in enumerate(post_bottom.rsplit('\n'), 1):
+ if line.startswith(tyto.words_tags[13][0]):
+ inlist = True
+ continue
+ elif line.startswith(tyto.words_tags[13][1]):
+ inlist = False
+
+ if not inlist:
+ continue
+
+ if inlist and not line or not line[0] in tyto.markers_lists:
+ logs.out("3", 'L=%s. %s %s > %s'%(
+ ln + ln_header,
+ tyto.words_tags[13][4],
+ tyto.markers_lists,
+ db.uri_file
+ ), False)
+ post_err = True
+
+
+ # Check for all match _TAGS:NAME from content in header
+ #------------------------------------------------------
+ set_tags = ()
+ for ln, line in enumerate(post_bottom):
+ for htag in tyto.head_tags:
+ match = False
+ ptag = '_%s'%htag
+
+ if line.startswith(ptag):
+ ptag_set = line.rsplit(':', 1)[1].lstrip().rsplit(' ')[0]
+ if ptag_set in set_tags: continue
+ for hline in post_header:
+ if re.search(r"^%s\s+%s$"%(htag, ptag_set), hline):
+ match = True
+ set_tags = (ptag_set)
+
+ if match: continue
+ else:
+ logs.out("12", "%s %s"%(htag, ptag_set), False)
+ post_err = True
+
+
+ # Legacy HTML not paired error
+ for tag in tyto.leg_html_tags:
+ leg1 = post_bottom.count(tag[0])
+ leg2 = post_bottom.count(tag[1])
+
+ if leg1 != leg2:
+ logs.out("8", '"%s" + "%s" > %s'%(
+ tag[0], tag[1], db.uri_file
+ ), False)
+ post_err = True
+
+
+ # Template Tags (warning for not paired symbols)
+ #-----------------------------------------------
+ for tag in tyto.tpl_tags:
+ tpl1 = post_bottom.count(tag[0])
+ tpl2 = post_bottom.count(tag[1])
+
+ if tpl1 != tpl2:
+ logs.out("22", '"%s" + "%s" > %s'%(
+ tag[0], tag[1], db.uri_file
+ ), False)
+
+
+#===============================#
+# Create new article's database #
+#------------------------------&co-#
+def create_database():
+ # Post Configurations
+ #--------------------
+ database = \
+ '# Post Configurations\n' + \
+ 'post_id = "%s"\n'%db.uri_id + \
+ 'post_src = "%s"\n'%db.uri_file + \
+ 'post_wip = "%s"\n'%srv_post_wip_uri + \
+ 'post_www = "%s"\n'%srv_post_www_uri + \
+ '\n' + \
+ 'direc_src = "%s"\n'%direc_src + \
+ 'short_src = "%s"\n'%src_post_short_uri + \
+ 'short_srv = "%s"\n'%srv_post_short_uri + \
+ 'sub_uri = "%s"\n'%sub_uri + \
+ 'http_wip = "%s"\n'%http_wip + \
+ 'http_www = "%s"\n'%http_www + \
+ '\n' + \
+ 'date_chk = "%s"\n'%tyto.nowdate() + \
+ 'hash_chk = "%s"\n'%db.hash_post + \
+ 'date_wip = "%s"\n'%date_wip + \
+ 'hash_wip = "%s"\n'%hash_wip + \
+ 'date_www = "%s"\n'%date_www + \
+ 'hash_www = "%s"\n'%hash_www + \
+ '\n# Needed tags configurations\n' + \
+ 'title = "%s"\n'%title + \
+ 'about = "%s"\n'%about + \
+ 'author = "%s"\n'%author + \
+ 'meta_tags = "%s"\n'%tags + \
+ 'date = "%s"\n'%date_tr + \
+ 'snpic = "%s"\n'%snpic_url + \
+ '\n# Used files\n' + \
+ 'uris = %s'%str(files_post)
+
+
+ # Tags configuration
+ #-------------------
+ datas_tag = ''
+ if stat_abbrs > 0:
+ for i in range(1, stat_abbrs + 1):
+ datas_tag = '%s\nabbr_%s = %s'%(
+ datas_tag, i, globals()['abbr_%s'%i]
+ )
+
+ if stat_links > 0:
+ for i in range(1, stat_links + 1):
+ datas_tag = '%s\nlink_%s = %s'%(
+ datas_tag, i, globals()['link_%s'%i]
+ )
+
+ if stat_images > 0:
+ for i in range(1, stat_images + 1):
+ datas_tag = '%s\nimage_%s = %s'%(
+ datas_tag, i, globals()['image_%s'%i]
+ )
+
+ if stat_files > 0:
+ for i in range(1, stat_files + 1):
+ datas_tag = '%s\nfile_%s = %s'%(
+ datas_tag, i, globals()['file_%s'%i]
+ )
+
+ if stat_codes > 0:
+ for i in range(1, stat_codes + 1):
+ datas_tag = '%s\ncode_%s = %s'%(
+ datas_tag, i, globals()['code_%s'%i]
+ )
+
+ if stat_raws > 0:
+ for i in range(1, stat_raws + 1):
+ datas_tag = '%s\nraw_%s = %s'%(
+ datas_tag, i, globals()['raw_%s'%i]
+ )
+
+
+ opt_tags = ''
+ if datas_tag:
+ opt_tags = \
+ '\n# Optional tags configurations' + \
+ '%s\n'%datas_tag
+
+ # Statistics configurations
+ #--------------------------
+ stat_words = post_words - post_titles # Count real words
+
+ db_stats = \
+ '# Statistics from header\'s tags\n' + \
+ 'uniq_anchors = %d\n'%nbr_ancs + \
+ 'uniq_abbrs = %d\n'%stat_abbrs + \
+ 'uniq_links = %d\n'%stat_links + \
+ 'uniq_images = %d\n'%stat_images + \
+ 'uniq_files = %d\n'%stat_files + \
+ 'uniq_codes = %d\n'%stat_codes + \
+ 'uniq_raws = %d\n'%stat_raws + \
+ '\n# Statistics from post contents\n' + \
+ 'comments = %d\n'%post_comments + \
+ 'tags = %d\n'%stat_tags + \
+ 'lines = %d\n'%ln_article + \
+ 'words = %d\n'%stat_words + \
+ 'titles = %d\n'%post_titles + \
+ 'paragraphs = %d\n'%post_paragraphs + \
+ 'links = %d\n'%post_links + \
+ 'images = %d\n'%post_images + \
+ 'anchors = %d\n'%post_anchors + \
+ 'abbrs = %d\n'%post_abbrs + \
+ 'strongs = %d\n'%post_strongs + \
+ 'bolds = %d\n'%post_bolds + \
+ 'emphasis = %d\n'%post_emphasis + \
+ 'italics = %d\n'%post_italics + \
+ 'dels = %d\n'%post_dels + \
+ 'underlines = %d\n'%post_underlines + \
+ 'cites = %d\n'%post_cites + \
+ 'customs = %d\n'%post_customs + \
+ 'icodes = %d\n'%tyto.nbr_icodes + \
+ 'bcodes = %d\n'%nbr_bcodes + \
+ 'quotes = %d\n'%nbr_quotes + \
+ 'lists = %d\n'%post_lists + \
+ '\n# Included files in post\'s contents\n' + \
+ 'files = %d\n'%post_files + \
+ 'raws = %d\n'%post_raws + \
+ 'codes = %d\n'%fcodes
+
+ # Create Post DB
+ #---------------
+ database = '%s\n%s\n%s'%(database, opt_tags, db_stats)
+ tyto.set_file(db.config, 'New', database)
+
+ if not multi_chk:
+ logs.out("21", db.uri_file, False)
- # Statistics Post conf
- lines_conf = ''
- lines_conf = (
- '\n# Statistics (Uniq)',
- 'links_u = %d'%stats_links_uniq,
- 'files_u = %d'%stats_files_uniq,
- 'images_u = %d'%stats_images_uniq,
- '\n# Statistics (Wordings)',
- 'strongs = %d'%m_stats[0],
- 'bolds = %d'%m_stats[2],
- 'emphasis = %d'%m_stats[4],
- 'italics = %d'%m_stats[16],
- 'cites = %d'%m_stats[18],
- 'deletions = %d'%m_stats[6],
- 'customs = %d'%m_stats[8],
- 'underlines = %d'%m_stats[10],
- 'icodes = %d'%m_stats[12],
- '\n# Statistics (Links)',
- 'links = %d'%stats_links,
- 'links_p = %d'%stats_links_p,
- 'files_p = %d'%stats_files_p,
- 'images_p = %d'%stats_images_p,
- '\n# Statistics (Templates)',
- 'titles = %d'%stats_titles,
- 'anchors = %d'%stats_anchors,
- 'paragraphs = %d'%stats_p,
- 'quotes = %d'%stats_quotes,
- 'lists = %d'%stats_lists,
- 'lists_u = %d'%stats_lists_u,
- 'lists_o = %d'%stats_lists_o,
- 'precodes = %d'%stats_bcodes,
- 'comments = %d'%stats_comments
- )
- for line_conf in lines_conf:
- domain.append_f(post_db, line_conf)
diff --git a/src/var/lib/tyto/program/db.py b/src/var/lib/tyto/program/db.py
new file mode 100644
index 0000000..5cac1b1
--- /dev/null
+++ b/src/var/lib/tyto/program/db.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Load article's database and check validity
+# File: /var/lib/tyto/program/db.py
+#----------------------------------------------------------------------
+
+#------------
+# funny stats
+#------------
+# lines:
+# functions:
+# comments:
+#----------------------------------------------------------------------
+
+#**********************************************************************
+
+import os
+import args, logs, dom, form, tyto, check, publish
+
+remove = exists = post = corrupt = file_wip = file_www = False
+chk_again = chk_updated = False
+wip_again = wip_added = wip_updated = False
+www_again = www_added = www_updated = False
+
+if dom.hole:
+ logs.out("13", '', True)
+
+
+# target needs db (file).
+# action like show, wip, edit-db
+# not domain, all, again...
+if args.target \
+ and args.action in args.pass_db \
+ and not args.target in args.pass_targets:
+
+ # Domain must be valid
+ if not dom.exists: logs.out("10", '', True)
+
+ uri_file = '%s%s'%(dom.articles_d, args.target)
+ uri_id = tyto.get_filesum(uri_file, False)
+
+ # Set DB file
+ config = '%s%s.config'%(dom.articles_db_d, uri_id)
+ if tyto.exists(config):
+ exists = True
+ try:
+ exec(open(config).read())
+ except:
+ exists = False
+ else:
+ exists = False
+
+ # Article file exists
+ if tyto.exists(uri_file):
+ post = True
+ hash_post = tyto.get_filesum(uri_file, True)
+ else:
+ remove = True
+
+ # Check if database config is valid (contains values)
+ if exists:
+ values = \
+ (
+ 'post_id',
+ 'post_src',
+ 'post_wip',
+ 'post_www',
+ 'direc_src',
+ 'short_src',
+ 'short_srv',
+ 'sub_uri',
+ 'http_wip',
+ 'http_www',
+ 'date_chk',
+ 'hash_chk',
+ 'date_wip',
+ 'hash_wip',
+ 'date_www',
+ 'hash_www',
+ 'title',
+ 'about',
+ 'author',
+ 'meta_tags',
+ 'date',
+ 'snpic',
+ 'uris',
+ 'uniq_anchors',
+ 'uniq_abbrs',
+ 'uniq_links',
+ 'uniq_images',
+ 'uniq_files',
+ 'uniq_raws',
+ 'comments',
+ 'tags',
+ 'words',
+ 'titles',
+ 'paragraphs',
+ 'links',
+ 'images',
+ 'anchors',
+ 'abbrs',
+ 'strongs',
+ 'bolds',
+ 'emphasis',
+ 'italics',
+ 'dels',
+ 'underlines',
+ 'cites',
+ 'customs',
+ 'icodes',
+ 'bcodes',
+ 'quotes',
+ 'lists',
+ 'files',
+ 'raws',
+ 'codes',
+ )
+
+ # Set exist for wip and www files
+ for value in values:
+ try:
+ eval(str(value))
+ except:
+ remove = True
+ corrupt = True
+ break
+
+ # Remove DB if unused source article or corrupted DB
+ if remove and exists:
+ os.remove(config)
+ exists = False
+ logs.out("23", config, False)
+
+ old_chk = old_wip = old_www = False
+ no_chk = no_wip = no_www = False
+ sync_srvs = False
+
+ # Set Statuses for chk, wip, www
+ if exists:
+ # File exists on servers
+ if tyto.exists(post_wip): file_wip = True
+ if tyto.exists(post_www): file_www = True
+
+ # Statuses not set in Db
+ if not hash_chk: no_chk = True
+ if not hash_wip: no_wip = True
+ if not hash_www: no_www = True
+
+ # Source article has changed
+ if hash_post != hash_chk: old_chk = chk_updated = True
+ if hash_chk: chk_again = True
+
+ # WIP article is old
+ if not old_chk:
+ if hash_wip and hash_chk != hash_wip: old_wip = wip_updated = True
+ if no_wip: wip_added = True
+ if hash_wip: wip_again = True
+ if not file_wip:
+ old_wip = wip_updated = True
+ wip_added = True
+ wip_again = True
+
+ # WWW article is old
+ if not old_wip:
+ if hash_www and hash_www != hash_wip: old_www = www_updated = True
+ if hash_www: www_again = True
+ if no_www: www_added = True
+ if not file_www:
+ old_www = www_updated = True
+ www_added = True
+ www_again = True
+
+ # Article is updated on both servers
+ if hash_chk == hash_wip == hash_www: sync_srvs = True
diff --git a/src/var/lib/tyto/program/dom.py b/src/var/lib/tyto/program/dom.py
new file mode 100644
index 0000000..21b8eec
--- /dev/null
+++ b/src/var/lib/tyto/program/dom.py
@@ -0,0 +1,276 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Load domain database and check validity
+# File: /var/lib/tyto/program/dom.py
+#----------------------------------------------------------------------
+
+#------------
+# funny stats
+#------------
+# lines:
+# functions:
+# comments:
+#----------------------------------------------------------------------
+
+#**********************************************************************
+
+import os, sys, importlib, langs
+import args
+
+lib = 'tyto_domain'
+exists = incomplete = active = ready = shortname = corrupt = False
+local_user = articles_db_d = False
+hole = False
+
+# Set current directory
+try:
+ user_dir
+except:
+ try:
+ user_dir = os.getcwd() + "/"
+ except:
+ hole = True
+
+# Settings for domain, check if db is not corrupted
+dom_values = \
+(
+'directory',
+'database',
+'local_user',
+'lang_sys',
+'lang_logs',
+'articles_db_d',
+'articles_d',
+'files_d',
+'images_d',
+'modules_d',
+'navbar_f',
+'sidebar_f',
+'metas_f',
+'footer_f',
+'footer_about_f',
+'shortname',
+'www_url',
+'wip_url',
+'srv_root',
+'srv_domain',
+'srv_wip',
+'srv_wip_tpl_d',
+'srv_wip_images_d',
+'srv_wip_files_d',
+'srv_www',
+'srv_www_tpl_d',
+'srv_www_images_d',
+'srv_www_files_d',
+'wip_css_f',
+'wip_logo_f',
+'wip_navbar_f',
+'wip_sidebar_f',
+'wip_metas_f',
+'wip_footer_f',
+'wip_stats_f',
+'www_css_f',
+'www_navbar_f',
+'www_sidebar_f',
+'www_metas_f',
+'www_footer_f',
+'www_stats_f',
+'www_logo_f',
+'www_rss_f',
+'logo',
+'styles',
+'rss',
+'rss_items',
+'title',
+'date',
+'about',
+'lang_site',
+'mail',
+'tags',
+'license',
+'license_url',
+'legal_url',
+'terms_url',
+'css',
+'sep',
+'article_code',
+'relme',
+'sidebar_title',
+'sidebar_items',
+'activated'
+)
+
+create_files = \
+(
+'navbar_f',
+'sidebar_f',
+'metas_f',
+'footer_f',
+'footer_about_f'
+)
+
+wip_html_mods = ()
+err_val = (()) # Make a list from values error
+file_unu = (()) # Make a list for files to check
+file_mod = (()) # male a list for modules files to create
+dir_new = (()) # Make a list for directories created
+dir_unu = (()) # Not created, unused
+
+
+if not hole:
+ home_dir = os.path.expanduser('~')
+
+ # Set configuration domain directory
+ root_dir = user_dir
+ if '/articles' in user_dir:
+ root_dir = user_dir.rsplit('/articles')[0] + "/"
+
+ # Set configuration domain file
+ config = '%styto_domain.py'%root_dir
+ shortname = config
+
+ # Set exists if configuration file
+ if os.path.exists(config):
+ exists = True
+ try:
+ exec(open(config).read())
+ try:
+ os.path.exists(articles_d)
+ if '/articles' in user_dir:
+ user_uri_dir = user_dir.rsplit(articles_d)[1]
+ else:
+ user_uri_dir = ''
+ os.chdir(articles_d)
+ except: corrupt = True
+ except:
+ corrupt = True
+
+ if not args.target in args.pass_targets:
+ if args.target.startswith("articles/"):
+ args.target = args.target.rsplit("articles/")[1]
+ args.target = user_uri_dir + args.target
+
+ # For logs: show uri if not shortname known
+ try: shortname
+ except: pass
+
+ try: active = activated
+ except: pass
+
+ if not corrupt:
+ for value in dom_values:
+ try:
+ eval(str(value))
+ value_set = True
+ except:
+ err_val = err_val + ((value),)
+ value_set = False
+ incomplete = True
+ active = False
+
+
+ if value.endswith('_d'):
+ if value_set:
+ if not os.path.exists(eval(str(value))):
+ try:
+ os.makedirs(eval(str(value)), exist_ok=True)
+ dir_new = dir_new + ((eval(str(value))),)
+ except:
+ dir_unu = dir_unu + ((eval(str(value))),)
+
+ elif value.endswith('_f'):
+ if value_set:
+ if not os.path.exists(eval(str(value))):
+ if value in create_files:
+ file_mod = file_mod + ((value),)
+ else:
+ file_unu = file_unu + ((eval(str(value))),)
+
+
+ #==============================================#
+ # When an active and complete domain is needed #
+ #----------------------------------------------#
+ if exists and not incomplete and not corrupt:
+ wip_html_mods = \
+ (
+ eval(str('wip_navbar_f')),
+ eval(str('wip_sidebar_f')),
+ eval(str('wip_metas_f')),
+ eval(str('wip_footer_f'))
+ )
+ if active:
+ ready = True
+
+ metas = (
+ eval(str('metas_f')),
+ eval(str('wip_metas_f')),
+ eval(str('www_metas_f'))
+ )
+ navbars = (
+ eval(str('navbar_f')),
+ eval(str('wip_navbar_f')),
+ eval(str('www_navbar_f'))
+ )
+ sidebars = (
+ eval(str('sidebar_f')),
+ eval(str('wip_sidebar_f')),
+ eval(str('www_sidebar_f'))
+ )
+ footers = (
+ eval(str('footer_f')),
+ eval(str('wip_footer_f')),
+ eval(str('www_footer_f')),
+ eval(str('footer_about_f'))
+ )
+
+ templates = (
+ eval(str('wip_logo_f')),
+ eval(str('wip_css_f')),
+ eval(str('wip_navbar_f')),
+ eval(str('wip_sidebar_f')),
+ eval(str('wip_metas_f')),
+ eval(str('wip_footer_f')),
+ eval(str('wip_stats_f')),
+ eval(str('www_logo_f')),
+ eval(str('www_css_f')),
+ eval(str('www_navbar_f')),
+ eval(str('www_sidebar_f')),
+ eval(str('www_metas_f')),
+ eval(str('www_footer_f')),
+ eval(str('www_stats_f')),
+ eval(str('www_rss_f')),
+ )
+
+ modules = {
+ "metas" : metas,
+ "navbar" : navbars,
+ "sidebar" : sidebars,
+ "footer" : footers,
+ "template": templates,
+ }
+
+#====================================#
+# Check if domain is ready and ready #
+#------------------------------------#
+def valid():
+ if incomplete: sys.exit(41)
+ elif not active: sys.exit(42)
diff --git a/src/var/lib/tyto/program/domain.py b/src/var/lib/tyto/program/domain.py
deleted file mode 100644
index b5a0aed..0000000
--- a/src/var/lib/tyto/program/domain.py
+++ /dev/null
@@ -1,540 +0,0 @@
-#!/usr/bin/env python3
-# Name: Tyto - Littérateur
-# Type: Global functions for domain
-# Description: Add new domain, check domain dir...
-# file: domain.py
-# Folder: /var/lib/tyto/scripts/
-# By echolib (XMPP: im@echolib.re)
-# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007
-
-#------------
-# funny stats
-#------------
-# lines:
-# functions:
-# comments:
-#----------------------------------------------------------------------
-
-#**********************************************************************
-
-#======#
-# MAIN #
-#======#
-import sys, os, subprocess, datetime
-import check, log
-
-# In Tyto DB dir
-tyto_db = '/var/lib/tyto/db/'
-tyto_domains = '%sdomains.conf'%tyto_db
-
-# In Tyto log dir
-tyto_logs_dir = '/var/log/tyto/'
-tyto_logs = '%styto.log'%tyto_logs_dir
-
-# Current dir
-curr_dir = domain_articles = domain_logs = os.getcwd()
-
-# set domain configuration file from current directory
-conf_domain = '%s/tyto_domain.conf'%curr_dir
-
-# Activation domain
-domain_active = domain_conf = False
-
-# Set needed directories
-
-# Create Tyto logs
-if not os.path.exists(tyto_logs):
- file = open(tyto_logs, "w")
- file.write('')
- file.close()
- msg_log = 'Log > Create logs file for Tyto in %s\n'%tyto_logs
- log.append_f(tyto_logs,msg_log,0)
-
-# Get database domains
-# If not exists, create the file conf
-# Will receive all domain registred
-try:
- exec(open(tyto_domains).read())
-except:
- domains_file = open(tyto_domains, 'w')
- domains_file.write("# Tyto's file with all activated domains\n")
- domains_file.close()
-
-domains_db = open(tyto_domains,'r').read()
-
-# Get user domain configuration file #
-# If exists, set to True, and exec
-try: # os.path.exists(conf_domain):
- exec(open(conf_domain).read())
- datas_domain = open(conf_domain, "r").read()
- if domain_active:
- print(':) Activated domain: "%s"'%domain_name)
- else:
- print(':/ Not activated domain in',conf_domain)
-except:
- print(':( Unused domain file:', conf_domain)
-
-#=======#
-# Tools #
-#=======#--------------------------------------------------------------
-#============================#
-# Append new value to a file #
-#----------------------------#
-def append_f(f,line_val):
- file = open(f, "a")
- file.write('%s\n'%line_val)
-
-#======================================#
-# Just a generic exit #
-# out defines message, not exit status #
-# for process form: "
-# - rename temp domain conf to legacy #
-#--------------------------------------#
-def exiting(process,out,msg):
- msgs = [
- '\n:) All done !',
- '\n:/ Maybe next time...',
- '%s'%msg
- ]
-
- if process == 'form':
- os.rename(temp_domain, conf_domain)
-
- print(msgs[out])
- sys.exit(0)
-
-
-#==========================#
-# Manage Argument 'domain' #
-#==========================#-------------------------------------------
-def manage_domain(Domain, Opt):
- if not Opt:
- try:
- # No option: get domain and print it
- print('\n',datas_domain)
- except:
- sys.exit(0)
-
- elif Opt == 'New':
- try:
- # Domain NAME is defined in CLI
- set_domain = Domain
- except:
- set_domain = ''
-
- add_domain(set_domain)
-
- elif Opt == 'Edit' or '-E':
- if domain_conf:
- print(':> Editing',domain_conf)
- old_conf_ID = check.get_filesum(domain_conf, True)
- edit_domain = subprocess.run(['/usr/bin/nano',
- '--linenumbers',
- domain_conf])
-
- # Compare before and after domain conf file
- new_conf_ID = check.get_filesum(domain_conf,True)
- if not old_conf_ID == new_conf_ID:
- exiting('root',2,':) Updated domain configuration file.')
- else:
- exiting('root',2,':) Unchanged domain configuration file.')
- else:
- sys.exit(0)
-
-#===================================#
-# Main fonction to add a new domain #
-# Check first, if it already exists #
-# Domain not activated: #
-# - Show registred values in form #
-#-----------------------------------#----------------------------------
-def add_domain(set_domain):
- # Exit if a domain already exists
- if domain_active:
- sys.exit(0)
-
- global temp_domain
- temp_domain = '%s/tyto_domain.tmp'%curr_dir
-
- print('\n'
- ' ┌──────────────────────────────────────────────┐\n'
- ' │ Configure a new domain for current directory │\n'
- ' │ Answer Y = yes. Default value = (default) │\n'
- ' │ Empty Answer cancel process, except for │\n'
- ' │ - "Optional" │\n'
- ' │ - "(default) value │\n'
- ' ├──────────────────────────────────────────────┘'
- )
-
- # Create new temp conf file
- # Used to prepare values, leaving legacy conf file
- file = open(temp_domain, "w")
- file.write('# Tyto Configuration Domain\n')
- file.close()
-
- # Domain is or not given in CLI.
- # Start form
- if set_domain:
- domain_input_confirn(set_domain)
- domain_form()
- else:
- domain_input()
- domain_form()
-
- # End of form.
- # Show resume's domain configuration from temp conf file
- print('\n'
- ' ┌─────────────────────────────┐\n'
- ' │ Please check domain datas...│\n'
- ' ├─────────────────────────────┘'
- )
- with open(temp_domain, "r") as file:
- post_temp = file.read()
- for line in post_temp.split('\n'):
- print(' │ %s'%line)
-
- # Ask to confirm to write activation domain
- print(' ├─────────────────────────────')
- confirm = input(' └ Activate domain configuration ? ')
- if confirm in ['y', 'Y']:
- create_domain()
- else:
- exiting('form',1,'')
-
-#==============================#
-# If domain name is set in CLI #
-# Confirm process #
-#------------------------------#
-def domain_input_confirn(set_domain):
- global domain_name
- confirm = input(' ├ Add Domain (%s) here ? '%set_domain)
- if confirm in ['y', 'Y']:
- # Check if domain already registred
- isdomain = set_domain.replace('.','_')
- if isdomain in domains_db:
- dom_folder = globals().get(isdomain,False)
- if dom_folder and not dom_folder == curr_dir:
- exiting('root',2,'\n:/ Domain exists in %s'%dom_folder)
- domain_name = set_domain
- else:
- exiting('root',1,'')
-
-#=====================#
-# Add new domain_name #
-#---------------------#
-def domain_input():
- global domain_name
- set_domain = input(' ├ Enter Domain Name: ')
- if not set_domain:
- exiting('root',1,'')
- else:
- domain_name = set_domain
-
-#====================#
-# Domain FORM #
-# domain_name is set #
-# Configure domain #
-#--------------------#
-def domain_form():
-
- # First settings to put in temp_domain config file
- domain_db = '%s/%s/articles/'%(tyto_db,domain_name)
- domain_logs = '%s%s/'%(tyto_logs_dir,domain_name)
- domain_articles = '%s/articles/'%curr_dir
- domain_images = '%simages/'%domain_articles
- domain_files = '%sfiles/'%domain_articles
-
- # Add settings from domain name before starting form
- append_f(temp_domain,'domain_name = "%s"'%domain_name)
- append_f(temp_domain,'domain_conf = "%s"'%conf_domain)
- append_f(temp_domain,'domain_db = "%s"'%domain_db)
- append_f(temp_domain,'domain_logs = "%s"'%domain_logs)
- append_f(temp_domain,'\n# Article directories')
- append_f(temp_domain,'domain_dir = "%s"'%curr_dir)
- append_f(temp_domain,'domain_articles = "%s"'%domain_articles)
- append_f(temp_domain,'domain_files = "%s"'%domain_files)
- append_f(temp_domain,'domain_images = "%s"'%domain_images)
-
- # ----------------------- #
- # Starting Form #
- # Some values are defaut #
- # Values can be registred #
- # from legacy conf file #
- # ----------------------- #
-
- # Local server Directory
- # ----------------------
- global srv
- try:
- srv
- except:
- srv = '/var/www'
-
- set_srv = input(' ├ Local server directory (%s) ? '%srv)
- if not set_srv and not srv:
- exiting('form',1,'')
- if set_srv and set_srv[-1] == '/':
- srv = set_srv[:-1]
-
- if not os.path.exists(srv):
- exiting('form',2,'\n:( Unsed directory "%s"'%srv)
-
- # Settings for server
- srv_domain = '%s/%s/'%(srv,domain_name)
- srv_wip = '%s/%s/wip/'%(srv,domain_name)
- srv_wip_files = '%s/%s/wip/files/'%(srv,domain_name)
- srv_wip_images = '%s/%s/wip/images/'%(srv,domain_name)
- srv_wip_template = '%s/%s/wip/template/'%(srv,domain_name)
- srv_www = '%s/%s/www/'%(srv,domain_name)
- srv_www_files = '%s/%s/www/files/'%(srv,domain_name)
- srv_www_images = '%s/%s/www/images/'%(srv,domain_name)
- srv_www_template = '%s/%s/www/template/'%(srv,domain_name)
- # Write settings to temp_omain
- append_f(temp_domain,'\n# Server directories')
- append_f(temp_domain,'srv = "%s"'%srv)
- append_f(temp_domain,'srv_domain = "%s"'%srv_domain)
- append_f(temp_domain,'srv_wip = "%s"'%srv_wip)
- append_f(temp_domain,'srv_wip_files = "%s"'%srv_wip_files)
- append_f(temp_domain,'srv_wip_images = "%s"'%srv_wip_images)
- append_f(temp_domain,'srv_wip_template = "%s"'%srv_wip_template)
- append_f(temp_domain,'srv_www = "%s"'%srv_www)
- append_f(temp_domain,'srv_www_files = "%s"'%srv_www_files)
- append_f(temp_domain,'srv_www_images = "%s"'%srv_www_images)
- append_f(temp_domain,'srv_www_template = "%s"'%srv_www_template)
-
- # Domain Title for website
- # ------------------------
- global domain_title
- try:
- domain_title
- show_title = domain_title[:14] + '...'
- except:
- domain_title = show_title = ''
-
- set_title = input(' ├ Domain Title (%s) ? '%show_title)
- if not set_title and not domain_title:
- exiting('form',1,'')
- if set_title:
- domain_title = set_title
- if '"' in domain_title:
- domain_title = domain_title.replace('"','\\"')
-
- append_f(temp_domain,'\n# Domain datas for web pages')
- append_f(temp_domain,'domain_title = "%s"'%domain_title)
-
- # Separator Pages Titles (default '-')
- # ------------------------------------
- global sep_titles
- try:
- sep_titles
- except:
- sep_titles = '-'
-
- set_sep = input(' ├ Website pages separator (%s) ? '%sep_titles)
- if set_sep:
- if len(set_sep) > 2:
- exiting('form',2,'\n:( Seperator is 2 characters max')
- sep_titles = set_sep
-
- append_f(temp_domain,'sep_titles = "%s"'%sep_titles)
-
- # Domain description
- # ------------------
- global domain_about
- try:
- domain_about
- show_about = domain_about[:14] + '...'
- except:
- domain_about = show_about = ''
-
- set_about = input(' ├ Domain description (%s) ? '%show_about)
- if not set_about and not domain_about:
- exiting('form',1,'')
- if set_about:
- domain_about = set_about
- if '"' in domain_about:
- domain_about = domain_about.replace('"','\\"')
-
- append_f(temp_domain,'domain_about = "%s"'%domain_about)
-
- # Lang for HTML Pages
- # -------------------
- global domain_lang
- try:
- domain_lang
- except:
- # Get default system language (2/3 chars for HTML)
- import locale
- domain_lang = locale.getdefaultlocale()[0].split('_')[0]
-
- set_lang = input(' ├ Website HTML lang (%s) ? '%domain_lang)
- if set_lang:
- if len(set_lang) > 3:
- exiting('form',2,'\n:( HTML Lang is 3 characters max')
- domain_lang = set_lang
-
- append_f(temp_domain,'domain_lang = "%s"'%domain_lang)
-
- # Domain CSS (prefix class). alphanum only
- # ----------------------------------------
- global domain_css
- try:
- domain_css
- except:
- domain_css = 'tyto'
-
- set_css = input(' ├ Generic CSS class (%s) ? '%domain_css)
- if set_css:
- domain_css = set_css
- if not domain_css.isalnum():
- css_alnum = ''.join(c for c in domain_css if c.isalnum())
- domain_css = css_alnum
-
- append_f(temp_domain,'domain_css = "%s"'%domain_css)
-
- # Domain mail
- # -----------
- global domain_mail
- try:
- domain_mail
- show_mail = domain_mail[:14] + '...'
- except:
- domain_mail = show_mail = ''
-
- set_mail = input(' ├ Contact admin mail (%s) ? '%show_mail)
- if not set_mail and not domain_mail:
- exiting('form',1,'')
- if set_mail:
- if not '@' and not '.' in set_mail:
- exiting('form',2,'\n:( Invalid mail format (x@x.x)')
- domain_mail = set_mail
- elif not domain_mail:
- exiting('form',2,'\n:( Mail is required.')
-
- append_f(temp_domain,'domain_mail = "%s"'%domain_mail)
-
- # Domain Tags
- # -----------
- global domain_tags
- try:
- domain_tags
- show_tags = domain_tags[:14] + '...'
- except:
- domain_tags = show_tags = ''
-
- set_tags = input(' ├ Domain Tags [x,y] (%s) ? '%show_tags)
- if not set_tags and not domain_tags:
- exiting('form',1,'')
- if set_tags:
- domain_tags = set_tags
-
- append_f(temp_domain,'domain_tags = "%s"'%domain_tags)
-
- # Webpages Copyright
- # ------------------
- global domain_license
- try:
- domain_license
- show_license = domain_license[:14] + '...'
- except:
- domain_license = show_license = 'CC BY-NC-SA'
-
- set_license = input(' ├ Website copyright (%s) ? '%show_license)
- if set_license:
- domain_license = set_license
- if '"' in domain_license:
- domain_license = domain_license.replace('"','\\"')
-
- append_f(temp_domain,'domain_license = "%s"'%domain_license)
-
- # Sidebar Title
- # -------------
- global sidebar_title
- try:
- sidebar_title
- show_st = sidebar_title[:14] + '...'
- except:
- if 'fr' in domain_lang:
- sidebar_title = show_st = "À l'affiche !"
- else:
- sidebar_title = show_st = "Featured !"
-
- set_st = input(' ├ Sidebar title (%s) ? '%show_st)
- if set_st:
- sidebar_title = set_st
- if '"' in sidebar_title:
- sidebar_title = sidebar_title.replace('"','\\"')
-
- # Sidbar items number. Default: 12
- # -------------------
- global sidebar_items
- try:
- sidebar_items
- except:
- sidebar_items = 8
-
- set_si = input(' ├ Sidebar max items [1-16] (%s) ? '%sidebar_items)
- if set_si and set_si.isdigit():
- if set_si in range(1,16):
- exiting('form',2,'\n:( Items number: 1-16')
- sidebar_items = set_si
-
- # Domain LOGO (optional)
- # ----------------------
- global domain_logo
- try:
- domain_logo
- show_logo = domain_logo[:14] + '...'
- except:
- domain_logo = show_logo = ''
-
- set_logo = input(' ├ Optional. Logo filename (%s) ? '%show_logo)
- if set_logo:
- domain_logo = set_logo
- append_f(temp_domain,'domain_logo = "%s"'%domain_logo)
-
- # External URL profile (optional)
- # -------------------------------
- global domain_exturl
- try:
- domain_exturl
- show_e= domain_exturl[:14] + '...'
- except:
- domain_exturl = show_e = ''
-
- set_e = input(' └ Optionnal. URL to a social network (%s) ? '%show_e)
- if set_e:
- domain_exturl = set_e
-
- append_f(temp_domain,'domain_exturl = "%s"'%domain_exturl)
-
- # Adding sidebar conf
- sidebar_dir = '%s/articles/sidebar/'%curr_dir
- sidebar_load = '%styto.sidebar'%sidebar_dir
- append_f(temp_domain,'\n# Sidebar')
- append_f(temp_domain,'domain_sdb_dir = "%s"'%sidebar_dir)
- append_f(temp_domain,'domain_sdb_load = "%s"'%sidebar_load)
- append_f(temp_domain,'domain_sdb_title = "%s"'%sidebar_title)
- append_f(temp_domain,'domain_sdb_items = "%s"'%sidebar_items)
-
-#==========================#
-# If confirm activation #
-# Add value to domain conf #
-# Create all directories #
-#--------------------------#
-def create_domain():
- # Add activation var to temp domain conf
- append_f(temp_domain,'\n# Domain activation')
- append_f(temp_domain,'domain_active = True')
-
- # Add var domain_name to tyto_domains file, if not exists
- domain_var = domain_name.replace('.','_')
- if not domain_var in domains_db:
- append_f(tyto_domains,'%s = "%s"'%(domain_var,curr_dir))
-
- # Create all directories for this domain
- check.create_domain_dirs('all')
-
- # Will rename temp_domain conf to legacy domain conf file
- exiting('form',2,'\n:) Activated domain: "%s"'%domain_name)
-
-
diff --git a/src/var/lib/tyto/program/form.py b/src/var/lib/tyto/program/form.py
new file mode 100644
index 0000000..6316e1d
--- /dev/null
+++ b/src/var/lib/tyto/program/form.py
@@ -0,0 +1,1090 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+#
+# Copyright (C) 2023 Cyrille Louarn
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib (im@echolib.re)
+#
+# Description: Manage 'domain' argument.
+# Create domain config from form
+# Create config files for user to custom
+# File: /var/lib/tyto/program/form.py
+#----------------------------------------------------------------------
+
+#------------
+# funny stats
+#------------
+# lines:
+# functions:
+# comments:
+#----------------------------------------------------------------------
+
+#**********************************************************************
+
+from datetime import datetime
+import os, sys, shutil, re, locale, importlib
+import logs, dom, tyto, html, show, langs
+
+# locale translation directory
+trans_dir = '/var/lib/tyto/translations'
+
+# Get default System language
+try:
+ lang_sys = locale.getdefaultlocale()[0].split('_')[0]
+ os.path.exists('%s/site_%s.py'%(trans_dir, lang_sys))
+except:
+ lang_sys = 'en'
+
+lang_site = lang_sys
+
+# Generic answer
+answer_yes = ('y', 'Y', 'yes', 'Yes', 'YES',
+ 'o', 'O', 'oui', 'Oui', 'Oui'
+ )
+
+
+#=======================================#
+# Function called when asking something #
+# Catch interruption #
+# if logs.out True: sys exit #
+# yes_no False: return answer #
+# True: exit if not in yes #
+#---------------------------------------#
+def asking(question, yes_no):
+ try:
+ answer = input(question)
+ except KeyboardInterrupt:
+ print('')
+ logs.out("255", '', True)
+
+ if yes_no:
+ if not answer in answer_yes:
+ logs.out("255", '', True)
+
+ else:
+ return(answer)
+
+
+#==========================#
+# Manage Argument 'domain' #
+# target: 3rd argument #
+#--------------------------#
+def manage(target):
+ if not dom.exists:
+ logs.out("43", '', False)
+ create_domain(target)
+ elif dom.incomplete:
+ create_domain(target)
+ else:
+ asking(' ├ %s%s '%(langs.site.form_edit, langs.site.q), True)
+ create_domain(target)
+
+
+#=====================#
+# Create a new domain #
+#---------------------#
+def create_domain(target):
+ valid_url = ('http://', 'https://')
+ invalid = False
+
+ if target and not target.startswith(valid_url):
+ logs.out("51", '"%s" -> http(s)://...'%target, False)
+ target = ''
+
+ print(langs.site.form_start)
+
+ # Get complete URL from target or ask
+ #------------------------------------
+ try:
+ www_url = dom.www_url
+ except:
+ if target: www_url = target
+ else: www_url = ''
+
+ answer = asking(' ├ [http(s)://...] %s%s {%s} '%(
+ langs.site.form_url, langs.site.q, www_url
+ ), False)
+
+ if answer:
+ if answer.startswith(valid_url): www_url = answer
+ else: logs.out("2", '"http(s)://%s"%s'%(answer, langs.site.q), True)
+ elif not www_url:
+ logs.out("255", '', True)
+
+ if www_url[-1] == "/": www_url = www_url[:-1]
+
+ protocol = www_url.rsplit('://')[0]
+ shortname = www_url.rsplit('://')[1]
+
+
+ # Prefix wip ; guess it if unknown
+ #---------------------------------
+ try:
+ wip_url = dom.wip_url
+ except:
+ # Set domain with www-wip
+ try:
+ wip_url = shortname.rsplit('.', 2)
+ if len(wip_url) > 2:
+ wip_url = '%s://www-wip.%s.%s'%(
+ protocol, wip_url[1], wip_url[2]
+ )
+ else:
+ wip_url = '%s://www-wip.%s.%s'%(
+ protocol, wip_url[0], wip_url[1]
+ )
+ except:
+ wip_url = ''
+
+ answer = asking(' ├ [http(s)://...] %s%s {%s} '%(
+ langs.site.form_wip, langs.site.q, wip_url
+ ), False)
+ if answer:
+ if answer.startswith(valid_url): wip_url = answer
+ else: logs.out("2", '"http(s)://www-wip.%s"%s'%(
+ answer, langs.site.q
+ ), True)
+ elif not wip_url:
+ logs.out("255", '', True)
+
+ if wip_url[-1] == "/": wip_url = wip_url[:-1]
+
+
+ # Translations for logs
+ #----------------------
+ try: lang_logs = dom.lang_logs
+ except: lang_logs = lang_sys
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_trlog, langs.site.q, lang_logs
+ ), False)
+
+ if answer:
+ if len(answer) == 2: lang_logs = answer.lower()
+ else: logs.out("3", answer, False)
+
+ if not tyto.exists('%s/logs_%s.py'%(trans_dir, lang_logs)):
+ lang_logs = 'en'
+
+
+ # Start registering variables in domain database
+ #-----------------------------------------------
+ local_user = '%s/.local/tyto/%s/'%(dom.home_dir, shortname)
+ modules_dir = '%sarticles/_configs/'%dom.root_dir
+ footer_about_f = '%sfooter_about.html'%modules_dir
+ config_bkp = '%sdomain_config.bkp'%local_user
+
+ set_f = \
+ '# Home Domain\n' + \
+ 'directory = "%s"\n'%dom.root_dir + \
+ 'database = "%s"\n'%dom.config + \
+ '\n# Local user configuration\n' + \
+ 'lang_sys = "%s"\n'%lang_sys + \
+ 'local_user = "%s"\n'%local_user + \
+ 'lang_logs = "%s"\n'%lang_logs + \
+ 'articles_db_d = "%sarticles/"\n'%local_user + \
+ '\n# Working directories\n' + \
+ 'articles_d = "%sarticles/"\n'%dom.root_dir + \
+ 'files_d = "%sarticles/files/"\n'%dom.root_dir + \
+ 'images_d = "%sarticles/images/"\n'%dom.root_dir + \
+ 'modules_d = "%s"\n'%modules_dir + \
+ '\n# Modules files\n' + \
+ 'navbar_f = "%styto.navbar"\n'%modules_dir + \
+ 'sidebar_f = "%styto.sidebar"\n'%modules_dir + \
+ 'metas_f = "%styto.metas.html"\n'%modules_dir + \
+ 'footer_f = "%styto.footer.html"\n'%modules_dir + \
+ 'footer_about_f = "%s"\n'%footer_about_f + \
+ '\n# Domain\n' + \
+ 'shortname = "%s"\n'%shortname + \
+ 'www_url = "%s"\n'%www_url + \
+ 'wip_url = "%s"\n'%wip_url
+
+ tyto.set_file(dom.config, True, set_f)
+
+
+ # Get srv root
+ #-------------
+ try: srv = dom.srv
+ except: srv = '/var/www'
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_srv, langs.site.q, srv
+ ), False)
+
+ if answer: srv = answer
+ if not tyto.exists(srv):
+ logs.out("1", srv, False)
+ srv = ''
+ invalid = True
+ elif srv[-1] == "/":
+ srv = srv[:-1]
+
+
+ # Get logo's website
+ #-------------------
+ try: logo = dom.logo
+ except: logo = 'logo.png'
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_logo, langs.site.q, logo
+ ), False)
+
+ if answer: logo = answer
+ if not logo: logo='logo.png'
+
+
+ # Set config's directories for servers
+ #-------------------------------------
+ root_srv_dom = '%s/%s'%(srv, shortname)
+ srv_wip_tpl = "%s/wip/template/"%root_srv_dom
+ stats_wip_f = "%s/wip/tyto_wip_statoolinfos.properties"%root_srv_dom
+ stats_www_f = "%s/www/tyto_www_statoolinfos.properties"%root_srv_dom
+
+ srv_www_tpl = "%s/www/template/"%root_srv_dom
+ set_f = \
+ '# Servers directories\n' + \
+ 'srv_root = "%s/"\n'%srv + \
+ 'srv_domain = "%s/"\n'%root_srv_dom + \
+ 'srv_wip = "%s/wip/"\n'%root_srv_dom + \
+ 'srv_wip_tpl_d = "%s"\n'%srv_wip_tpl + \
+ 'srv_wip_images_d = "%s/wip/images/"\n'%root_srv_dom + \
+ 'srv_wip_files_d = "%s/wip/files/"\n'%root_srv_dom + \
+ 'srv_www = "%s/www/"\n'%root_srv_dom + \
+ 'srv_www_tpl_d = "%s"\n'%srv_www_tpl + \
+ 'srv_www_images_d = "%s/www/images/"\n'%root_srv_dom + \
+ 'srv_www_files_d = "%s/www/files/"\n'%root_srv_dom + \
+ '\n' + \
+ '# Servers files (wip)\n' + \
+ 'wip_logo_f = "%s%s"\n'%(srv_wip_tpl, logo) + \
+ 'wip_css_f = "%sstyles.css"\n'%srv_wip_tpl + \
+ 'wip_navbar_f = "%snavbar.html"\n'%srv_wip_tpl + \
+ 'wip_sidebar_f = "%ssidebar.html"\n'%srv_wip_tpl + \
+ 'wip_metas_f = "%smetas.html"\n'%srv_wip_tpl + \
+ 'wip_footer_f = "%sfooter.html"\n'%srv_wip_tpl + \
+ 'wip_stats_f = "%s"\n'%stats_wip_f + \
+ '\n' + \
+ '# Servers files (www)\n' + \
+ 'www_logo_f = "%s%s"\n'%(srv_www_tpl, logo) + \
+ 'www_css_f = "%sstyles.css"\n'%srv_www_tpl + \
+ 'www_navbar_f = "%snavbar.html"\n'%srv_www_tpl + \
+ 'www_sidebar_f = "%ssidebar.html"\n'%srv_www_tpl + \
+ 'www_metas_f = "%smetas.html"\n'%srv_www_tpl + \
+ 'www_footer_f = "%sfooter.html"\n'%srv_www_tpl + \
+ 'www_stats_f = "%s"'%stats_www_f
+
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # RSS/Atom filename
+ #------------------
+ try: rss = dom.rss
+ except: rss = 'rss.xml'
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_rss_f, langs.site.q, rss
+ ), False)
+
+ if answer: www_rss = answer
+
+ set_f = 'www_rss_f = "%s/www/%s"\n'%(root_srv_dom, rss) + \
+ '\n# Domain user\'s settings\n' + \
+ 'logo = "%s"\n'%logo + \
+ 'styles = "styles.css"\n' + \
+ 'rss = "%s"'%rss
+
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # RSS/Atom max items
+ #-------------------
+ try: rss_items = dom.rss_items
+ except: rss_items = 100
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_rss_i, langs.site.q, rss_items
+ ), False)
+
+ if answer: rss_items = answer
+ if not str(rss_items).isdigit() or not int(rss_items) > 1:
+ rss_items = 100
+
+ set_f = 'rss_items = %d'%int(rss_items)
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get title domain
+ #-----------------
+ try: title = dom.title
+ except: title = ''
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_title, langs.site.q, title
+ ), False)
+
+ if answer: title = answer
+ if not title: invalid = True
+ else: title = tyto.convert_altname(title)
+
+ set_f = 'title = "%s"'%title
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get creation date
+ #------------------
+ try: date
+ except: date = datetime.now().year
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_date, langs.site.q, date
+ ), False)
+
+ if answer: date = answer
+
+ set_f = 'date = "%s"'%date
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get Description domain
+ #-----------------------
+ try: about = dom.about
+ except: about = ''
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_about, langs.site.q, about
+ ), False)
+
+ if answer: about = answer
+ if not about: invalid = True
+ else: about = tyto.convert_altname(about)
+
+ set_f = 'about = "%s"'%about
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get Lang domain for web pages
+ #------------------------------
+ try: lang_site
+ except: lang_site = lang_sys
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_trsite, langs.site.q, lang_site
+ ), False)
+
+ if answer:
+ if len(answer) == 2: lang_site = answer.lower()
+ else: logs.out("3", answer, False)
+
+ if not tyto.exists('%s/site_%s.py'%(trans_dir, lang_site)):
+ lang_site = 'en'
+
+ set_f = 'lang_site = "%s"'%lang_site
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get mail domain
+ #----------------
+ try: mail = dom.mail
+ except: mail = ''
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_mail, langs.site.q, mail
+ ), False)
+
+ if answer: mail = answer
+ if not re.search('^\w+@\w+.\w+$', mail):
+ logs.out("3", mail, False)
+ invalid = True
+
+ set_f = 'mail = "%s"'%mail
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get Tags domain
+ #----------------
+ try: tags = dom.tags
+ except: tags = ''
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_tags, langs.site.q, tags
+ ), False)
+
+ if answer: tags = answer
+ if not tags: invalid = True
+ elif '"' in tags: tags = tags.replace('"', '')
+
+ set_f = 'tags = "%s"'%tags
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get License domain
+ #-------------------
+ try: domlicense = dom.license
+ except: domlicense = 'CC BY-NC-SA 3.0'
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_lic, langs.site.q, domlicense
+ ), False)
+
+ if answer: domlicense = answer
+ if '"' in domlicense: domlicense = domlicense.replace('"', '')
+
+ set_f = 'license = "%s"'%domlicense
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Get License URL (optional)
+ #---------------------------
+ try: licurl = dom.license_url
+ except: licurl = ''
+
+ answer = asking(' ├ %s [http(s)://...] %s%s {%s} '%(
+ langs.site.form_opt, langs.site.form_licurl,
+ langs.site.q, licurl
+ ), False)
+
+ if answer: licurl = answer
+ if not licurl.startswith(valid_url):
+ if answer:
+ logs.out("2", '"http(s)://%s"%s'%(licurl, langs.site.q), False)
+ licurl = ''
+
+ set_f = 'license_url = "%s"'%licurl
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Legal Notice URL
+ #-----------------
+ try: legalurl = dom.legal_url
+ except: legalurl = ''
+
+ answer = asking(' ├ %s [http(s)://...] %s%s {%s} '%(
+ langs.site.form_opt, langs.site.form_legal,
+ langs.site.q, legalurl
+ ), False)
+
+ if answer: legalurl = answer
+ if not legalurl.startswith(valid_url):
+ if answer:
+ logs.out("2", '"http(s)://%s"%s'%(legalurl, langs.site.q), False)
+ legalurl = ''
+
+ set_f = 'legal_url = "%s"'%legalurl
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Terms URL
+ #-----------------
+ try: termsurl = dom.terms_url
+ except: termsurl = ''
+
+ answer = asking(' ├ %s [http(s)://...] %s%s {%s} '%(
+ langs.site.form_opt, langs.site.form_terms,
+ langs.site.q, termsurl
+ ), False)
+
+ if answer: termsurl = answer
+ if not termsurl.startswith(valid_url):
+ if answer:
+ logs.out("2", '"http(s)://%s"%s'%(termsurl, langs.site.q), False)
+ termsurl = ''
+
+ set_f = 'terms_url = "%s"'%termsurl
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # CSS Prefix
+ #-----------
+ try: css = dom_css
+ except: css = 'tyto'
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_css, langs.site.q, css
+ ), False)
+
+ if answer: css = answer.lower()
+ if not css.isalnum():
+ logs.out("3", css, False)
+ css = 'tyto'
+
+ set_f = 'css = "%s"'%css
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Titles webpage separator
+ #-------------------------
+ try: sep = dom.sep
+ except: sep = "-"
+
+ answer = asking(' ├ %s%s {%s} '%(
+ langs.site.form_sep, langs.site.q, sep
+ ), False)
+
+ if answer: sep = answer
+ if not len(sep) == 1:
+ logs.out("3", answer, False)
+ sep = "-"
+
+ set_f = 'sep = "%s"'%sep
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Show article source ?
+ #----------------------
+ try:
+ dom.article_code
+ article_code = "True"
+ except:
+ article_code = "False"
+
+ answer = (' ├ %s%s {%s} '%(
+ langs.site.form_pscode, langs.site.q, article_code
+ ), False)
+
+ if answer:
+ if answer in answer_yes: article_code = "True"
+ else: article_code = "False"
+
+ set_f = 'article_code = %s'%article_code
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Profile for rel="me" (optionnal)
+ #---------------------------------
+ try: relme = dom.relme
+ except: relme = ''
+
+ answer = asking(' ├ %s [http(s)://...] %s%s {%s} '%(
+ langs.site.form_opt, langs.site.form_relme,
+ langs.site.q, relme
+ ), False)
+
+ if answer: relme = answer
+ if not relme.startswith(valid_url):
+ if answer:
+ logs.out("2", '"http(s)://%s"%s'%(relme, langs.site.q), False)
+ relmel = ''
+
+ set_f = 'relme = "%s"'%relme
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Sidebar Title
+ #--------------
+ try: sdb_title = dom.sidebar_title
+ except: sdb_title = langs.site.site_sdb_t
+
+ answer = asking(' ├ %s. %s%s {%s} '%(
+ langs.site.sidebar, langs.site.title,
+ langs.site.q, sdb_title
+ ), False)
+
+ if answer: sdb_title = answer
+ if not sdb_title:
+ sdb_title = ''
+ invalid = True
+ else:
+ sdb_title = tyto.convert_altname(sdb_title)
+
+ set_f = 'sidebar_title = "%s"'%sdb_title
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Sidebar Items
+ #--------------
+ try: sdb_items = dom.sidebar_items
+ except: sdb_items = 6
+
+ answer = asking(' ├ [max=16] %s. %s%s {%s} '%(
+ langs.site.sidebar, langs.site.form_sdb_i,
+ langs.site.q, sdb_items
+ ), False)
+
+ if answer: sdb_items = answer
+ if not str(sdb_items).isdigit() or not int(sdb_items) in range(1,17):
+ sdb_items = 6
+
+ set_f = 'sidebar_items = %d'%int(sdb_items)
+ tyto.set_file(dom.config, False, set_f)
+
+
+ # Domain config invalid, do not activate
+ if invalid:
+ tyto.set_file(dom.config, False, '\nactivated = False')
+ print(langs.site.form_inv)
+ logs.out("2", dom.config, True)
+
+ # Resumed configuration
+ #-----------------------
+ try: active = dom.activated
+ except: active = False
+
+ print(langs.site.form_warn)
+ show.read_lines(dom.config, False)
+
+
+ # Activate and prepare domain ?
+ #------------------------------
+ answer = asking(' ├ %s%s '%(
+ langs.site.form_activ, langs.site.q
+ ), False)
+
+ if not answer in answer_yes:
+ tyto.set_file(dom.config, False, '\nactivated = False')
+ logs.out("255", '', True)
+
+
+ # Activate Domain
+ #----------------
+ tyto.set_file(dom.config, False, '\nactivated = True')
+ shutil.copy2(dom.config, config_bkp)
+ logs.out("32", config_bkp, False)
+
+ # RELoad config
+ #--------------
+ importlib.reload(dom)
+
+
+ # Create folders from configuration file
+ #---------------------------------------
+ folders = \
+ (
+ dom.srv_wip_tpl_d, dom.srv_wip_images_d, dom.srv_wip_files_d,
+ dom.srv_www_tpl_d, dom.srv_www_images_d, dom.srv_www_files_d,
+ dom.files_d, dom.images_d, dom.modules_d,
+ dom.articles_db_d,
+ )
+
+ print(' │')
+ for folder in folders:
+ if tyto.exists(folder):
+ logs.out("37", folder, False)
+ else:
+ os.makedirs(folder, exist_ok=True)
+ logs.out("33", foloder, False)
+
+ print(' │')
+
+
+ # Create in _configs/ modules files
+ #----------------------------------
+ create_metas('form')
+ create_navbar('form')
+ create_sidebar('form')
+ create_footer('form')
+ create_footer_about('form')
+
+ print(langs.site.form_ready)
+
+
+#=============================================================#
+# #
+# CREATE MODULES' USER FILE AND CREATE WIP FILES FROM MODULES #
+# #
+#=============================================================#
+#========================#
+# metas_load source file #
+#------------------------#
+def create_metas(option):
+ if not dom.ready: dom.valid()
+
+ # Create new default config file, or ask if exists
+ if tyto.exists(dom.metas_f):
+ answer = asking(' ├ %s. %s%s '%(
+ langs.site.metas, langs.site.form_reset,
+ langs.site.q
+ ), False)
+
+ if not answer in answer_yes:
+ if option == "form": return
+ logs.out("255", '', True)
+
+ # Set default content for
+ # metas config file
+ # if no translation file
+ #-----------------------
+ metas_en = \
+ '# For %s\n' + \
+ '# Type text/HTML file\n' + \
+ '# Description Configuration file for HTML tags\n' + \
+ '# Content inserted in section\n' + \
+ '# File %s\n' + \
+ '# How Insert and \n' + \
+ '# Notes - Ony these tags are added :\n' + \
+ '# - \n' + \
+ '~ - \n' + \
+ '# - Do NOT copy this file to template directory\n' + \
+ '# - These tags are already set'
+
+ try: metas_lang = langs.site.metas_doc
+ except: metas_lang = metas_en
+
+ metas_tags = \
+ '%s'%metas_lang%(tyto.Tyto, dom.metas_f) + \
+ '\n' + \
+ '# \n' + \
+ '\n' + \
+ '\n' + \
+ ''
+
+ tyto.set_file(dom.metas_f, 'New', metas_tags)
+
+ # Create an empty html file in wip/www server if not exists
+ if not tyto.exists(dom.wip_metas_f):
+ html.create_user_metas('wip')
+
+
+#=============================#
+# navbar load file translated #
+#-----------------------------#
+def create_navbar(option):
+ if not dom.ready: dom.valid()
+
+ # Create new config file, or ask if exists
+ if tyto.exists(dom.navbar_f):
+ answer = asking(' ├ %s. %s%s '%(
+ langs.site.navbar, langs.site.form_reset,
+ langs.site.q
+ ), False)
+
+ if not answer in answer_yes:
+ if option == "form": return
+ logs.out("255", '', True)
+
+
+ # Set default content for
+ # navbar config file
+ # if no translation file
+ #-----------------------
+ navbar_en = \
+ '# For %s\n' + \
+ '# Type: Text file\n' + \
+ '# Description Configuration file for navbar"\n' + \
+ '# (directories\'s list)\n' + \
+ '# Commands tyto new navbar (reset)\n' + \
+ '# tyto wip/publish navbar (Create)\n' + \
+ '# tyto show navbar (show config)\n' + \
+ '# tyto show-wip/show-www navbar (Show HTML)\n' + \
+ '# tyto edit navbar (edit this file)\n' + \
+ '# File %s\n' + \
+ '# Comment 1 folder name per line *1\n' + \
+ '# (from articles/)\n' + \
+ '# not begining with "/"\n' + \
+ '# Order in sidebar position\n' + \
+ '# Remember To avoid 404 error, you must:\n' + \
+ '# - add article index.{tyto}\n' + \
+ '# in set folder\n' + \
+ '# - check and wip article\n' + \
+ '# *1 Option To define a title link:' + \
+ '# - add "# title link"\n' + \
+ '\n# %s\n'%(20 * "-") +\
+ '# Examples :\n' + \
+ '# documentation\n' + \
+ '# about # infos about this website\n' + \
+ '# %s\n\n'%(20 * "-")
+
+ try: navbar_lang = langs.site.navbar_doc
+ except: navbar_lang = navbar_en
+
+ navbar_lang = navbar_lang%(tyto.Tyto, dom.navbar_f)
+
+ tyto.set_file(dom.navbar_f, 'New', navbar_lang)
+
+ # Create wip navbar file
+ html.create_navbar('wip')
+
+
+#==============================#
+# sidebar load file translated #
+#------------------------------#
+def create_sidebar(option):
+ if not dom.ready: dom.valid()
+
+ # Create an empty html file in wip/www server if not exists
+ if not tyto.exists(dom.wip_sidebar_f):
+ tyto.set_file(dom.wip_sidebar_f, 'new', '')
+
+ # Create new config file, or ask if exists with option = 'reset'
+ if tyto.exists(dom.sidebar_f):
+ answer = asking(' ├ %s. %s%s '%(
+ langs.site.sidebar, langs.site.form_reset,
+ langs.site.q
+ ), False)
+
+ if not answer in answer_yes:
+ if option == "form": return
+ logs.out("255", '', True)
+
+
+ # Set default content for
+ # navbar config file
+ # if no translation file
+ #-----------------------
+ sidebar_en = \
+ '# For %s\n' + \
+ '# Type Text file\n' + \
+ '# Description Configuration file for sidebar\n' + \
+ '# (articles\'s list)\n' + \
+ '# File %s\n' + \
+ '# Commands tyto new sidebar (reset)\n' + \
+ '# tyto wip/publish sidebar (Create)\n' + \
+ '# tyto show sidebar (Show config)\n' + \
+ '# tyto show-wip/show-www sidebar (Show HTML)\n' + \
+ '# tyto edit sidebar (edit this file)\n' + \
+ '# How 1 article URI per line\n' + \
+ '# (from articles/)\n' + \
+ '# not begining with "/"\n' + \
+ '# Order in sidebar position\n' + \
+ '# Max articles = %d\n' + \
+ '# Option Tp add sidebar title:\n' + \
+ '# ": Sidebar Title"\n' + \
+ '\n# %s\n'%(20 * "-") + \
+ '# Examples :\n' + \
+ '# : My new articles list'
+ '# index.tyto\n' + \
+ '# dir1/index.tyto\n' + \
+ '# %s\n\n'%(20 * "-")
+
+ try: sidebar_lang = langs.site.sidebar_doc
+ except: sidebar_lang = sidebar_en
+
+ sidebar_lang = sidebar_lang%(tyto.Tyto,
+ dom.sidebar_f,
+ dom.sidebar_items
+ )
+
+ tyto.set_file(dom.sidebar_f, 'New', sidebar_lang)
+
+
+#=============================#
+# footer load file translated #
+#-----------------------------#
+def create_footer(option):
+ if not dom.ready: dom.valid()
+
+ # Default footer contents
+ #------------------------
+ Tytosrc = \
+ '\n%s(%s)'%(9 * ' ', langs.site.source_code)
+
+ tyto_show = \
+ '%s%s %s'%(9 * ' ', tyto.Tyto, Tytosrc)
+
+ # Show copyright date from creation to now year
+ if int(dom.date) == int(datetime.now().year):
+ footer_date = dom.date
+ else:
+ footer_date = '%s - %s'%(dom.date, datetime.now().year)
+
+ # Simple link to home
+ domain_home = \
+ '\n%s%s'%(9 * ' ', dom.title)
+
+ # Insert content of footer_about_f or default if not exists
+ footer_about = ''
+ if tyto.exists(dom.footer_about_f):
+ footer_custom = open(dom.footer_about_f).read()
+ for line in footer_custom.rsplit('\n'):
+ if not line or line.startswith('#'):
+ continue
+
+ line = '%s%s'%(6 * ' ', line)
+ if footer_about: footer_about = '%s\n%s'%(footer_about, line)
+ else: footer_about = line
+ else:
+ footer_about = "
%s
"%dom.about
+
+ # License URL. Set to homepage if unknown
+ if not dom.license_url: dom.license_url = '/'
+
+ # Links for laws (Terms and Legals)
+ if dom.legal_url:
+ legal_link = \
+ '%s'%(11 * ' ', langs.site.legal_t)
+
+ if dom.terms_url:
+ terms_link = \
+ '%s'%(11 * ' ', langs.site.terms_s)
+
+ # create laws links from terms and legal if exists
+ footer_laws = ''
+ if dom.terms_url and dom.legal_url:
+ footer_laws = '%s - \n%s%s'%(legal_link, 8 * ' ', terms_link)
+ elif dom.terms_url:
+ footer_laws = terms_link
+ elif dom.legal_url:
+ footer_laws = legal_link
+
+ footer_laws_links = ''
+ if footer_laws:
+ footer_laws_links = \
+ '