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 +
+
+ +
+
+ echolib - Reference (2022-12-28) +
+
+```` +``` +# 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 = \ + ' \n' + + # Set default content for + # navbar config file + # if no translation file + #----------------------- + footer_en = \ + '# For %s\n' + \ + '# Type text/HTML file\n' + \ + '# Description Configuration file for footer\n' + \ + '# File %s\n' + \ + '# Commands tyto new footer (reset)\n' + \ + '# tyto wip/publish footer (Create)\n' + \ + '# tyto show footer (Show config)\n' + \ + '# tyto show-wip/show-www footer (Show HTML)\n' + \ + '# tyto edit footer (edit this file)\n' + \ + '# How Put any HTML code\n' + \ + '# Notes - Lines are ignored if:\n' + \ + '# - empty\n' + \ + '# - begin with "#"\n' + \ + '# - Do NOT copy to template directory' + '# %s\n'%(20 * "-") + + try: footer_lang = langs.site.footer_doc + except: footer_lang = footer_en + + # Final HTML footer code + footer = \ + '%s\n'%footer_lang%(tyto.Tyto, dom.footer_f) + \ + '
\n' + \ + ' \n' + \ + '\n' + \ + ' \n' + \ + ' \n' + \ + '
' + + # Create new default file, or ask if exists + if tyto.exists(dom.footer_f): + answer = asking(' ├ %s. %s%s '%( + langs.site.footer, langs.site.form_reset, + langs.site.q + ), False) + + if not answer in answer_yes: + if option == "form": return + logs.out("255", '', True) + + tyto.set_file(dom.footer_f, 'New', footer) + + # Create footer file in wip server if not exists + if not tyto.exists(dom.wip_footer_f): + html.create_user_footer('wip') + + +# Generic HTML list in footer +""" + ' \n' +""" + + +#========================================# +# Create custom about section for footer # +#----------------------------------------# +def create_footer_about(option): + if not tyto.exists(dom.footer_about_f): + set_f = '%s\n'%langs.site.footer_about_doc%(tyto.Tyto, + dom.footer_about_f + ) + \ + ''%dom.about + + tyto.set_file(dom.footer_about_f, False, set_f) + diff --git a/src/var/lib/tyto/program/html.py b/src/var/lib/tyto/program/html.py new file mode 100644 index 0000000..5c5c46a --- /dev/null +++ b/src/var/lib/tyto/program/html.py @@ -0,0 +1,536 @@ +# 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: Create HTML page +# File: /var/lib/tyto/program/html.py +#---------------------------------------------------------------------- + +#------------ +# funny stats +#------------ +# lines: +# functions: +# comments: +#---------------------------------------------------------------------- + +#********************************************************************** + +import os, sys, importlib +import logs, db, dom, tyto, form, langs + +# Publish option can be +pub_opts = ('www', 'pub') + +# Not a line if it starts with...(for sidebar, navbar) +nolines = ('#', '/') + +#==========================# +# Load article DB # +# Start HTML page sections # +#--------------------------# +def set_page(target, article_bottom): + create_metas_page() # Include metas tags + create_main_page(target, article_bottom) # Create main page + + +#============================================# +# Set metas_page to be included in main page # +#--------------------------------------------# +def create_metas_page(): + global metas_page + + # Settings for metas + #------------------- + metas_page = '' + scale = 'width=device-width, initial-scale=1.0' + all_tags = dom.tags + ',' + db.meta_tags + css_ref = 'href="%stemplate/%s"'%(db.sub_uri, dom.styles) + rss_ref = 'type="application/rss+xml" ' + \ + 'href="%s%s" title="RSS 2.0. %s %s %s"'%( + db.sub_uri, dom.rss, + dom.title, dom.sep, dom.shortname + ) + icon_file = 'favicon.png' + icon_ref = 'type="image/png" href="%stemplate/%s"'%( + db.sub_uri, icon_file + ) + relme = '' # External URL in metas (if exists in config domain) + if dom.relme: + relme = '\n '%( + dom.relme + ) + + # Create author and date publish section infos + create_html_time_meta('wip') + + # Set all raw HTML metas + #----------------------- + global metas + metas = \ + '\n' + \ + ' \n'%scale + \ + ' \n'%dom.www_url + \ + ' \n'%dom.lang_site + \ + ' \n'%dom.mail + \ + ' \n'%dom.license + \ + ' \n'%tyto.Tyto + \ + ' \n'%db.title + \ + ' \n'%db.author + \ + ' \n'%db.about + \ + ' \n'%all_tags + \ + '%s'%meta_pub + \ + ' \n'%db.http_www + \ + ' \n'%(rss_ref) + \ + ' \n'%css_ref + \ + ' \n'%icon_ref + \ + ' \n' + \ + ' \n'%dom.title + \ + ' \n'%db.title + \ + ' \n' + \ + ' \n'%db.http_www + \ + ' \n'%db.about + \ + ' \n'%db.snpic + \ + '%s'%relme + \ + ' %s %s %s'%(db.title, dom.sep, dom.title) + + +#=======================================# +# Set main page, with all HTML sections # +#---------------------------------------# +def create_main_page(target, article_bottom): + global main_page, post_html_code + + # Create link for website's logo + #------------------------------- + logo_html = \ + '\n'%(11 * " ") + \ + '%s\n'%(15 * " ") + \ + '%s'%(8 * " ") + + post_html_code = '' + if dom.article_code: + post_html_code = \ + ' \n' + \ + ' {%s} \n'%( + langs.site.tyto_psrc, db.title, + langs.site.source_code + ) + + #-----------------------# + # Create main HTML Page # + #-----------------------# + main_page = \ + '\n' + \ + '\n'%dom.lang_site + \ + ' \n' + \ + '%s\n'%metas + \ + ' \n\n' + \ + ' \n' + \ + '
\n' + \ + ' \n' + \ + '
\n' + \ + ' \n' + \ + '

%s

\n'%dom.title + \ + '
\n' + \ + '

%s

\n'%dom.about + \ + '
\n' + \ + '
\n' + \ + '\n' + \ + '\n' + \ + '
\n' + \ + '

%s

\n'%( + db.title, langs.site.w_written, db.date, + langs.site.by, db.author, + db.title, + ) + \ + '%s\n'%article_bottom + \ + '
\n' + \ + '

\n' + \ + ' %s, %s\n'%( + db.author, langs.site.author_of, db.title, + db.author, langs.site.le + ) + \ + '%s\n'%time_html_pub + \ + '%s'%post_html_code + \ + '

\n' + \ + '
\n' + \ + '
\n' + \ + '\n' + \ + '\n' + \ + '\n' + \ + ' \n' + \ + '' + + +#============================================# +# Create HTML line for article infos section # +# when wip, and publish # +#--------------------------------------------# +def create_html_time_meta(process): + # Need to reload the DB to get last time updated + importlib.reload(db) + + global time_html_pub, meta_pub, date_raw + + if process == 'wip': + try: + date_raw = db.date_wip #