diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b247ba..2686b6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,213 +10,5 @@ Tyto - Littérateur # CURRENTLY IN DEV (in devel branch) ! -## [1.9.39] -- fix datepub in modules when "check domain" -- add Tyto - Littérateur generator links at HTML footer creation -- fix settings from command line options -- add "RSS" and "sitemap" links to default footer (not yet processed) -- add command "set 'MODULE_NAME'" to create default module (header, footer...) -- Create again all HTML modules with option -F, --force -- With option -E, --errors: only show warning and error logs -- Avoid creating a domain in a directory domain set yet -- better modules managed in "wip" process -- Added logs file (include ALL logs) in /var/log/tyto -- - current.log for current session only -- - archive.log for all sessions -- Sitemap (! In dev) -- - Create sitemaps in root and sub-directories - -## [1.9.38] -- Moved: HTML footer in div site_container -- fix: main title in HTML page h1 -- Fill Article HTML metas datas -- Working on files copies to wip server with "wip" command -- - some changes in post DB and check process - -## [1.9.37] -- Preparing full HTML Page -- Some fixes -- Added web target (without index.html) in section "FILE" in post DB -- Added article footer contents - -## [1.9.36] -- fix translations modules -- added metas module -- added command "show metas" to see those already set in page with post - -## [1.9.35] -- Working on creating modules -- - Todo last: metas -- check -- - target: domain, wip, www to check directories and files -- - - domain: create .raw modules and wip modules (.html) -- wip -- - html values are now in wip.py -- fixes and more... - -## [1.9.34] -- Working on creating modules -- - fixes typos codes -- - header, navbar, sidebar = ok -- - toto footer contents and testing things -- - Manage modules with an ini db file to avoid creating HTML updated modules - -## [1.9.33] -- Working on creating modules (header, navbar, sidebar, header) -- - Added working header module -- - Finishing navbar module... -- - Todo Next: sidebar and footer modules - -## [1.9.32] -- Working on creating modules (header, navbar, sidebar, header) - -## [1.9.31] -- Fix replacing markers starting LINE with HTML -- Added tpl_files directory -- - addes styles.css (empty with classes used) - -## [1.9.30] -- 'wip' process -- - About all markers done ! - -## [1.9.29] -- Translations -- - added for logs (english) -- - updated french logs -- wip (working on...) -- - added modules conversions (source to HTML) -- check -- - some fixes and updated code -- tools -- - check css content -- readme : updated article example with comments - -## [1.9.28] -- readme -- - updated tyto article, with comments to show how to -- multiple targets -- - fix CSS classe name error -- - reset stats, post.error, HEADERS datas -- check process: -- - Added hr and br tags (CSS class name check) -- - fix blockquote title HTML -- - New method to split separator in article -- - fix icode, bcode contents HTML signs (converted) - -## [1.9.27] -- fix when target article .tyto is missing -- Nearly all stats are added in DB from modules -- Only used tags and stats (or nearly) are added to DB -- cleaner code -- check: -- - added words_tags (strong, italics...) -- - "all" target now ready - -## [1.9.26] -- user can indent titles in text -- prepared words markers (strong, bolds) (some to add soon) - -## [1.9.25] -- fix typo when creating HTML list -- new anchors process (HTML prepared at 'check') - -## [1.9.24] -- new list process (HTML prepared at 'check') - -## [1.9.23] -- new quote process (HTML prepared at 'check') - -## [1.9.22] -- new bcode process (html prepared for wip) -- new post database management values -- new icode process (html prepared for wip) -- bcodes and icodes are FIRST processed in text article -- generic check fonction for bcodes, quotes, lists, paragraphs -- lots more - -## [1.9.21] -- new indentation (3 spaces) -- added 'raw:' marker -- (for wip process): -- - added html titles to post database -- - added html comments to post database (default: ';; a comment') -- - added val3 tag as html comment to content, and convert content to base64 -- - - added values to post database -- cleaner code - -## [1.9.20] -- working on 'check' process -- - updated 'logo:' process -- - added 'abbr:' process - -## [1.9.19] -- working on 'check' process -- - Added post 'logo: URI' (for social network share + opt show on page) - -## [1.9.18] -- working on 'check' process -- - added image: tag -- - added value 2 (tag line 2) uri check (generic, root, post) -- - some corrections for domain config file -- - prepared html values for wip - -## [1.9.17] -- working on 'check' process -- - dev on checking file used in some post header tags - -## [1.9.16] -- working on 'check' process -- - dev: post database values (lots are missing) -- - added links, files supports -- - testing some new markers - -## [1.9.15] -- Added 'check' process for bcodes, quotes and paragraphs + stats -- - Their contents must be indented -- - replace with empty lines for bcodes and quotes - -## [1.9.14] -- added 'check' process for block-codes - -## [1.9.13] -- Check: One-Line needed tags, titles -- Added first stats -- move ini template from tyto to domain for domain config file -- Check valid date in article and form domain date - -## [1.9.12] -- preparing check process : head tags in article - -## [1.9.11] -- Preparing for multi-targets with "all" -- check if article is a .tyto format -- preparing values for articles - -## [1.9.10] -- cleaner code -- start/stop domain by user -- User directories check/create when domain activated -- Check install configuration - -## [1.9.9] -- cleaner code with new check/update domain process - -## [1.9.8] -- Check/create/update a domain is ready - -## [1.9.7] -- new process to check and update configuration file (bug at write some values) - -## [1.9.6] -- Better management to create/update domain - -## [1.9.5] -- Preparing post database - -## [1.9.4] -- Added start/strop action to activate (or deactivate) domain -- Directories creation for user working domain -- better log management for user - - - +## [1.9.50] +- Complete new code diff --git a/LICENSE b/LICENSE index 2a1caa8..b83cd68 100644 --- a/LICENSE +++ b/LICENSE @@ -629,7 +629,7 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - GSL Statique Littérateur + Tyto - Littérateur Copyright (C) 2022 Libre en Communs / Commissions / Infrastructure This program is free software: you can redistribute it and/or modify diff --git a/README.md b/README.md index 1d83be2..e481b67 100644 --- a/README.md +++ b/README.md @@ -1,372 +1,195 @@ # Very early IN dev code -This program can ve tested but not at all usable. Lots of work to do... +This program can ve tested and should mainly work. Please report any problems + +Tyto - Littérateur is translated in french and english. # Commands ``` -# Get commands list help +# Get commands help tyto + +# Get all documentation +tyto help all + +# How to write words tags (strong...) and anchors +tyto help words anchor ``` + # Create new domain - create a domain directory, like www.domain.tld - Go to this directory - type `tyto new domain` - type `tyto check domain` -- create in new directory domain ".../articles/", an article (see tuto below) -- type `check myfile.tyto` (or use "wip" instead of check) +- create in new directory domain ".../articles/", an article (see help) +- type `wip myfile.tyto` -## Working on -- 'wip' action processes -- Create HTML full page from article -- Create/Manage modules (metas, header, navbar, sidebar, footer) - -## ToDo -- thinking about creating an auto top article menu from titles -- create full HTML page -- sitemaps -- RSS -- 'publish' process - ## Exemple d'article .tyto commenté ``` -#================================================# -# Entete de l'article # -# Fin de l'entête avec au moins 5 tirets "-----" # -# Toute ligne de commentaire "# ..." est ignorée # -# ! Recommandé de ne pas utiliser le signe "_" # -#================================================# +title: Tests +about: Tests divers +date: 2023-02-28 +tags: tests +authors: echolib -# Pour ne pas inclure cet article dans les sitemaps : -! NoSitemap +abbr: CSS + Cascading SteelSheet + en -# Données uniques sur UNE ligne -title: tests d'un article -about: À propos de cet article de test -tags: Tyto, tuto, -author: echolib -date: 2023-10-27 - -# Données unique sur UNE ligne optionnelle -# Si non définit, le logo du domaine est utilisé -# Ne sera affiché qu'avec _image:logo -logo: logo.png - -# Données multiples sur 3 lignes -# [TAG]: Nom -# LIEN -# Text alternatif - -# Reprendre dans l'article : __cliquer sur ce lien -link: cliquer sur ce Lien - https:// - Text alternatif - -link: Réservez ici - https:// - Billets - -# Reprendre dans l'article : --télécharger ce fichier -file: télécharger ce fichier - @/PDFs/hello.pdf - Un PDF ! - -# Reprendre dans l'article : _code:codetest -code: codetest - @RAWS/test.py - Exemple d'un code Python - -# Reprendre dans l'article : _image:mypic -image: mypic - @hello.png - Text Alt - -# Les abréviations : -# 2eme ligne: Texte alternatif -# 3ème ligne: valeur affichée dans l'article à la place du Nom -abbr: HTML - HuperText Markup Langage - HTML - -# Reprendre dans l'article : ;;css -abbr: css - Cascading Stylesheet - CSS +link: le site est prêt + https://forge.a-lec.org + La forge libre ----- -#=====================================================================# -# Contenu de l'article # -# Les classe optionnelles non renseignées deviennent celle du domaine # -# /!\ Tout code HTML sera interprêté par le navigateur sauf si placé # -# entre les marqueurs de block-code ou icode # -#=====================================================================# - -# La ligne suivante est un commentaire HTML "" -# ---------------------------------------------------------------- -;; Commentaire - -# Ceci est une ancre avec l'ID uniq1 (ID unique) -# ---------------------------------------------- --> uniq1 --> top - -# Créer un lien vers l'ancre "uniq1" -# ---------------------------------- ->_uniq1: Go to uniq1 anchor_< - -# Ceci est une ligne
ayant pour classe hrcss -# ----------------------------------------------- --- hrcss - -# Les Titres de l'article de #1 à #5 (

à

) -# ------------------------------------------------ -#1 Titre en h2 - -# Contenu dans un paragraphe entre (( ... )) ayant pour classe "mypar" -# -------------------------------------------------------------------- -(( mypar - Un long paragraphe... - - # Retour à la ligne avec un
de classe brcss - # (les retours à la ligne vides ne sont pas pris en compte) - # --------------------------------------------------------- - | brcss - - # Reprise du Nom pour les liens de link: et file: - # Reprise des abréviations - Il faut __cliquer sur ce Lien, __Réservez ici et --télécharger ce fichier - ou encore faire une ::css pour du beau rendu ::HTML - - # Un paragraphe dans le paragraphe ayant pour classe, celle du domaine - # -------------------------------------------------------------------- - (( - Un /_court_/ paragraphe de :_1984_: pour de ~_vrai_~ +_faux en gras_+ - Il faut le ._souligner_. et *_Très Gras_* +#1 Titre 1 +(( + Un peu de ::CSS et ::le site est prêt + (( note + Cet article est un test )) - - # Créer un icode (utilise la balise HTML) - # /!\ ! Doit être sur une même ligne - # ---------------------------------------------- - {_
    ,
      _} - {_{_Afficher un icode brut_}_} )) -# Un exemple de block code ayant pour classe python -# Tout contenu entre les marqueurs "{{" et "}}" est conservé -# ---------------------------------------------------------- -#2 Block Code -{{ python - # Un commentaire et du code - def hello(world): - world and print(world) or print("NoMore") +#2 Citation +(" + cite: Auteur + date: AAAA-MM-JJ + book: Nom du livre + lang: fr + link: https://... + + (( + Citation complète dans un paragraphe + )) +)" + +#2 Code (bloc) +{{ + # Écrire les marqueurs de mots + # Chaque marqueur à la classe CSS de la configuration + # Astuce : ** + ← + `` + ← + très gras + + *`très gras`* => + +`gras`+ => + /`italique`/ => + ;`italique`; => + _`souligné`_ => + ~`effacé`~ => + [`cité`] => # Contenu + :`cité`: => # auteur, nom + |`perso`| => + + # Code dans un texte + # ! Les marqueurs d'ouverture et de fermeture de code sont sur la MEME LIGNE + + {`
    • Une entée de liste
    • `} => + + # ! Dans certains cas, il faut ajouter un espace après le 1er marqueur + # et/ou avant le second. Ils seront automatiquement supprimés + + *`DOMAIN/articles/ `* # évite /` : marqueur italique ouvert }} +#2 Une liste +# Liste. Classe CSS possible (défaut : celle dans la configuration) +# Une entrée de liste peut être ordonnée avec le signe "+" ou non avec "=" +# Une liste peut contenir des entrées mixées ("+" et "=") +# mais au changement de signe, ajouter un signe ! +# Possible d'écrire une entrée sur plusieurs lignes -# Écrire en gras, italique... -# --------------------------- -#2 Marqueurs de mots -(( - Même si, il est possible d'écrire directement des balises (HTML), Tyto - propose de les simplifier, en entourant les mots avec des marqueurs. La - classe CSS du domaine est utilisée pour chaque marqueur. - - {{ - *_Très Gras_* > - +_En Gras_+ > - [_Citer un texte_] > - :_Citer une référence_: > - ~_Texte barré_~ > - ._Text souligné_. > - /_En italique_/ > - ;_En italique_; > - - # Marques multiples, ajouter "&" - *_&._Très gras et souligné_.&_* - }} -)) - - -# Dans un block div [[ ... ]] (classe CSS mydiv), -# La citation entre [" ... "] (classe CSS mycite) -# est placée dans un paragraphe (classe CSS mycite) -# ! Tout commentaire "# ..." dans la citation sera affiché... -# ----------------------------------------------------------- -#2 Citation -[[ mydiv - [" mycite - ;; A great quote here ! - cite: Someone - date: 2023-10-13 - book: A History - lang: en - link: https://... - - (( mycite - Here, i am - )) - "] -]] - - -# Créer une liste ol/ul entre <: ... :> (classe mylist) -# dans un paragraphe (classe du domaine) -# "+" pour ol, "=" pour ul -# Ajouter toujours un signe pour un sous-item ou /!\ au changement de signe -# ------------------------------------------------------------------------- -#2 Une liste mixée {_
        ,
          _} -(( - <: mylist - + numeric ol item 1 - ++ numeric ol sub-Item 1 - +++ numeric ol sub-sub-item 1 - ==== ul item >_top: Go to Top_< - ==== ul item >_top: Another anchor_< - :> -)) - -# Afficher un block code avec le contenu d'un fichier -# Il doit avoir été configuré dans l'entête -# ----------------------------------------- -#2 Un block code depuis un fichier -_code:codetest - -# Afficher une image (1 tag par ligne) -# ------------------------------------ -#2 Les images -# Placer le logo de l'article -_image:logo - -# les options du marqueur: -# - c=CLASS < Sinon la classe est celle du domaine -# - w=WIDTH < longueur (si pas d'unité : défaut "px") -# - h=HEIGHT < Comme w= -# - f=Ma légende sous l'image (ajoute
          ) -# - - Recommandé d'utiliser cette option en dernier - -# ! Les images dans cet exemple sont affichées horizontalement -# Placer "|", ou mettez chaque image dans un paragraphe "((...))" -# ou définir un style css de type display:block pour les afficher verticalement -(( - _image:mypic - _image:mypic c=MYCSS -)) - -# Une image avec légende (
          ) (jamais dans un paragraphe) -_image:mypic c=PIC w=60em h=30% f=echolib sur une chaise - +(= + = Première entrée non ordonnée (ul) + == Sous entrée non ordonnée + +++ Première sous-sous entrée ordonnée (ol) + +++ Seconde sous-sous entrée ordonnée + = Seconde entrée non ordonnée + = Troisième entrée ... + ... non ordonnée +)= ``` ## Output HTML ``` - - - - -Go to uniq1 anchor -
          -

          Titre en h2

          -

          - Un long paragraphe... - -
          - - Il faut cliquer sur ce Lien, Réservez ici et télécharger ce fichier - ou encore faire une CSS pour du beau rendu HTML - -

          - Un court paragraphe de 1984 pour de vrai faux en gras - Il faut le souligner et Très Gras -

          - <ol>, <ul> - {_Afficher un icode brut_} -

          -

          Block Code

          - -

          1# Un commentaire et du code

          -

          2def hello(world):

          -

          3 world and print(world) or print("NoMore")

          -
          -

          Marqueurs de mots

          -

          - Même si, il est possible d'écrire directement des balises (HTML), Tyto - propose de les simplifier, en entourant les mots avec des marqueurs. La - classe CSS du domaine est utilisée pour chaque marqueur. - - -

          1*_Très Gras_* > <strong>

          -

          2+_En Gras_+ > <b>

          -

          3[_Citer un texte_] > <q>

          -

          4:_Citer une référence_: > <cite>

          -

          5~_Texte barré_~ > <del>

          -

          6._Text souligné_. > <u>

          -

          7/_En italique_/ > <em>

          -

          8;_En italique_; > <i>

          -

          9

          -

          10# Marques multiples, ajouter "&"

          -

          11*_&._Très gras et souligné_.&_*

          -
          -

          -

          Citation

          -
          -
          -
          + + + +
          + +
          +
          -

          -

          Un block code depuis un fichier

          - - -

          1#================================#

          -

          2# Searching options in arguments #

          -

          3#--------------------------------#

          -

          4def get_options():

          -

          5 global dlogs, force, erron

          -

          6

          -

          7 dlogs = force = erron = False

          -

          8 for arg in range(1, len(sys.argv)):

          -

          9 dlogs = sys.argv[arg] in tyto.debug_options

          -

          10 force = sys.argv[arg] in tyto.force_options

          -

          11 erron = sys.argv[arg] in tyto.debug_errors

          -

          12

          -

          13 print("<li>my 'cafe !</li>")

          -

          14

          - -
          -

          Les images

          - -

          -Text Alt -Text Alt -

          -
          Text Alt
          echolib sur une chaise
          + + +
          + + + +
          + + ``` diff --git a/debian/control b/debian/control index ab58014..040bb33 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Package: tyto -Version: 1.9.39 +Version: 1.9.50 Section: custom Priority: optional Architecture: all diff --git a/src/usr/bin/tyto b/src/usr/bin/tyto index 583cb87..23e58bb 100755 --- a/src/usr/bin/tyto +++ b/src/usr/bin/tyto @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -# Version: 1.9.39 -# Updated: 2023-11-11 1699742831 +# version: 1.9.50 # Tyto - Littérateur # Copyright (C) 2023 Cyrille Louarn @@ -16,102 +15,136 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Main binary to execute. -# Import modules and start checking/using arguments +# Description: Main executable # File: /usr/bin/tyto #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# Project files : -# Project lines : -# Project comments : -# Project functions: -# Project program : -# -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - import os, sys -#===============# -# Error message # -#---------------# -def error_message(path): - print("! Installation error, unused:", path) - sys.exit(1) - - -#======================================================# -# A little checker to be sure, all files are installed # -#------------------------------------------------------# +#===================================# +# Check Tyto directoryies and files # +# Insert directories # +#-----------------------------------# def check_install(): - os.path.exists(libs) or error_message(libs) - os.path.exists(trfs) or error_message(trfs) + # Settings + error = False + lib_tyto = "/var/lib/tyto/%s" - for f in prog_files: - f = os.path.join(libs, f + ".py") - if not os.path.exists(f): - error_message(f) + log_m = "! Tyto installation. Unused" - # Only default lang files - for f in lang_files: - f = os.path.join(trfs, f + ".py") - if not os.path.exists(f): - error_message(f) - + # Files trees + files = { + "program" : ( + "args", + "check", + "debug", + "domain", + "feed", + "form", + "help", + "langs", + "modules", + "new", + "page", + "post", + "publish", + "sitemap", + "tools", + "wip", + ), + "translations" : ( + "logs_fr", + "logs_en", + "site_fr", + "site_en", + ), + } + + for d in files: + tyto_dir = lib_tyto%d + if not os.path.exists(tyto_dir): + print("%s directory:"%log_m, tyto_dir) + error = True + continue + + # Insert path + sys.path.insert(0, tyto_dir) + + for f in files[d]: + tyto_file = os.path.join(tyto_dir, f + ".py") + if not os.path.exists(tyto_file): + print("%s file:"%llog_m, tyto_file) + error = True + + error and sys.exit(1) -#======#======================================================================= -# MAIN # +#=============================================================================# +# Main # #======# if not __name__ == "__main__": - print("! Error: '%s' not '%s'"%(__name__, "__main__")) + print("! Process error: '%s' not '%s'"%(__name__, "__main__")) sys.exit(1) -# files list in /program/ -prog_files = { - "args", - "check", - "debug", - "domain", - "forms", - "help", - "langs", - "new", - "post", - "show", - "tools", - "tyto", - "userset", - "wip" - } -lang_files = { - "logs_en", - "logs_fr", - "website_en", - "website_fr" - } - -# Set librairies to import app files -libs = "/var/lib/tyto/program" -trfs = "/var/lib/tyto/translations" - +# Check installed files check_install() -sys.path.insert(0, libs) -sys.path.insert(0, trfs) -# Manage arguments from command line, start process -import args -args.start_process() +# Load some Tyto libs +import langs, debug, args, domain + +#==========# +# Language # +#==========# +langs.load_logs() # Load logs language + + +#========# +# Domain # +#========# +domain.check_home() # Mainly check current directory + + +#===========# +# Arguments # +#===========# +args.get_arguments() # Get arguments from command line + +# First starting logs +debug.out(200, os.getlogin(), domain.user_dir, False, 0, False) +debug.out(201, langs.logs_lang, langs.logs_uri, False, 0, False) +if domain.in_hole: + debug.out(4, "$PWD ?", domain.user_dir, True, 2, True) + +if args.err_action: # [action] error (only) + debug.out(2, "[action]", args.err_action, False, 2, False) + + +#===============# +# Start process # +#===============# +import check, help, new, wip, publish + +# From command line [ACTION], do +do = { + "check" : check.manage, + "help" : help.manage, + "new" : new.manage, + "publish" : publish.manage, + "wip" : wip.manage, + "start" : domain.manage, + "stop" : domain.manage, + + } + +do[args.action]() +""" +try: do[args.action]() +except KeyError: debug.out(90, args.action, "", True, 2, True) +""" diff --git a/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc deleted file mode 100644 index db3b35c..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc deleted file mode 100644 index 631844e..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc deleted file mode 100644 index 3bfa598..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc deleted file mode 100644 index d58e1d1..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/forms.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/forms.cpython-311.pyc deleted file mode 100644 index a581213..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/forms.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/help.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/help.cpython-311.pyc deleted file mode 100644 index ff52e8a..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/help.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/langs.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/langs.cpython-311.pyc deleted file mode 100644 index ee7fa5b..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/langs.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc deleted file mode 100644 index 4e4dd43..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc deleted file mode 100644 index f94cad6..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc deleted file mode 100644 index 4ce594f..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/show.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/show.cpython-311.pyc deleted file mode 100644 index 86983c1..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/show.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc deleted file mode 100644 index 6ea26d4..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc deleted file mode 100644 index cd6f46a..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc deleted file mode 100644 index dec9a6c..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc deleted file mode 100644 index 3bba342..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc deleted file mode 100644 index 8020c89..0000000 Binary files a/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc and /dev/null differ diff --git a/src/var/lib/tyto/program/args.py b/src/var/lib/tyto/program/args.py index 01f331b..6d2d6cb 100644 --- a/src/var/lib/tyto/program/args.py +++ b/src/var/lib/tyto/program/args.py @@ -14,101 +14,177 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # # Description: Manage arguments from command line # File: /var/lib/tyto/program/args.py #---------------------------------------------------------------------- - -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - import sys -import langs, tyto, debug, help, new, check, userset, show, wip -#==================# -# Action Arguments # -#------------------# -def get_action(): - global action +#======================================================# +# Get action at first position # +# Set options for other arguments (no position matter) # +#------------------------------------------------------# +def get_arguments(): + global commands, action, err_action, set_module + + commands = { + "actions" : { + "check" : False, + "wip" : False, + "publish" : False, + "new" : False, + "start" : False, + "stop" : False, + "help" : False, + }, + "targets" : { + "*.tyto" : (), + "all" : False, + "domain" : False, + "sitemap" : False, + }, + "modules" : { + "modules" : False, + "metas" : False, + "header" : False, + "navbar" : False, + "sidebar" : False, + "footer" : False, + }, + "options" : { + "-v" : False, + "-E" : False, + }, + } + + # Arguments from command line + # --------------------------- + set_module = False + for user_arg in range(2, len(sys.argv)): + # Register articles + if sys.argv[user_arg].endswith(".tyto"): + commands["targets"]["*.tyto"] = \ + commands["targets"]["*.tyto"] + (sys.argv[user_arg],) + + # Register targets + for arg in commands["targets"]: + if not commands["targets"][arg] \ + and sys.argv[user_arg] == arg: + commands["targets"][arg] = True + continue + + # Register modules + for arg in commands["modules"]: + if not commands["modules"][arg] \ + and sys.argv[user_arg] == arg: + commands["modules"][arg] = True + set_module = True + continue + + # Register options + for arg in commands["options"]: + if sys.argv[user_arg] == arg: + commands["options"][sys.argv[user_arg]] = True + continue + + # Action, the first argument + # -------------------------- + err_action = True + try: action = sys.argv[1] + except: action = "help" ; err_action = False ; return + for arg in commands["actions"]: + if action == arg: + commands["actions"][arg] = True + err_action = False + break + + if err_action: + err_action = action + action = "help" + return + + + """ + global err_act, action, actions, article, targets, logall, erron, force + + hlp_actions = ("help", "-h", "--help") + + actions = ( + "check", "wip", "publish", + "start", "stop", + "new", hlp_actions, + ) + debug_opts = ("--verbose", "-v") + erron_opts = ("--errors", "-E") + force_opts = ("--force", "-F") + + # Options + err_act = article = targets = logall = erron = force = False + + # First argument must be an action + # -------------------------------- try: action = sys.argv[1] except: action = "" - - -#==================# -# Target arguments # -#------------------# -def get_target(): - global target, targets - try: target = sys.argv[2] - except: target = "" - - targets = False - if target == "all": targets = True - - -#================================# -# Searching options in arguments # -# Done only once # -#--------------------------------# -def get_options(): - global dlogs, force, erron, set_options - - try: set_options ; return - except: pass - - dlogs = force = erron = False - for arg in range(1, len(sys.argv)): - if sys.argv[arg] in tyto.debug_options: dlogs = True - if sys.argv[arg] in tyto.force_options: force = True - if sys.argv[arg] in tyto.debug_errors: erron = True - - set_options = True - - -#===========# -# Show logs # -#-----------# -def valid_action(): - global action - - if not action in tyto.actions: - debug.out(1, "[action]", action, False, 2, False) + if not action in actions or action in hlp_actions: + err_act = action action = "help" + return + + # Set other arguments passed + # -------------------------- + global domain, modules, metas, header, navbar, sidebar, footer, set_modules + global sitemap + + sitemap = False + domain = modules = metas = header = navbar = sidebar = footer = False + set_modules = { + "modules" : "", + "metas" : "", + "header" : "", + "navbar" : "", + "sidebar" : "", + "footer" : "", + } + + for arg in range(2, len(sys.argv)): + # ------ + # Target + # ------ + # Argument is a .tyto article + if not article and sys.argv[arg].endswith(".tyto"): + article = sys.argv[arg] + continue + + # Other targets + # domain + if not domain: domain = bool(sys.argv[arg] == "domain") + if not sitemap: sitemap = bool(sys.argv[arg] == "sitemap") + + # Modules + if not modules: + modules = set_modules["modules"] = bool(sys.argv[arg] == "modules") + if not metas: + metas = set_modules["metas"] = bool(sys.argv[arg] == "metas") + if not header: + header = set_modules["header"] = bool(sys.argv[arg] == "header") + if not navbar: + navbar = set_modules["navbar"] = bool(sys.argv[arg] == "navbar") + if not sidebar: + sidebar = set_modules["sidebar"] = bool(sys.argv[arg] == "sidebar") + if not footer: + footer = set_modules["footer"] = bool(sys.argv[arg] == "footer") -#==============# -# Start action # -#--------------# -def start_process(): - # Set Lang logs - langs.load_logs_lang() - - get_options() - get_action() - get_target() - valid_action() - - do = { - "help" : help.show, - "check" : check.manage, - "new" : new.manage, - "set" : userset.manage, - "start" : userset.manage, - "stop" : userset.manage, - "show" : show.manage, - "wip" : wip.manage, - } - - do[action](action, target) - + # ------- + # Options + # ------- + if not targets: targets = bool(sys.argv[arg] == "all") + if not logall: logall = bool(sys.argv[arg] in debug_opts) + if not erron: erron = bool(sys.argv[arg] in erron_opts) + if not force: force = bool(sys.argv[arg] in force_opts) + """ diff --git a/src/var/lib/tyto/program/check.py b/src/var/lib/tyto/program/check.py index e8c2740..9ca75f0 100644 --- a/src/var/lib/tyto/program/check.py +++ b/src/var/lib/tyto/program/check.py @@ -14,1192 +14,652 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Mainly to check article, but also domain and more +# Description: Check article content. Check domain # File: /var/lib/tyto/program/check.py #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - -import sys, os, re -from dateutil.parser import parse - -import args, domain, langs, debug, post, tools, tyto, wip - - -#=====================================# -# Check domain # -# Load domain configuration file # -# Update it if needed, and set values # -# Load website lang # -# Domain must be activated # -#-------------------------------------# -def ready(): - domain.cf_update_values(False) - domain.ready() +import os, re, datetime +import args, post, debug, domain, tools #===========================================# -# Manage argument from command line "check" # -# Domain must be valid to proceed # -# Create user work domain directories # +# action "check" # +# not to check article, but domain, or more # #-------------------------------------------# -def manage(action, target): - # target is "all" - if args.targets: - multiple_targets() - return - - # target is not "all" - ready() - target.endswith(".tyto") and is_article(target) - target == "domain" and wrk_domain() - target in ("wip", "www") and srv_domain(target) +def manage(): + if args.commands["targets"]["domain"]: + domain.is_ready() + with open(domain.cf_uri) as f: + print(f.read()) + debug.out(250, domain.conf["title"], domain.cf_uri, True, 0, False) -#==========================================# -# Check some things used by current domain # -#------------------------------------------# -def wrk_domain(): - wrk_template_files() - wip.get_modules("wip") +#===============================# +# File is a Tyto format # +# 1st process, checking # +# Create strings from separator # +# Check article header contents # +#-------------------------------# +def tyto_format(): + global out, article, article_header, article_writer, stats - if post.error != 0: - sys.exit(post.error) + debug.out(210, post.uid, post.uri, False, 0, False) + + post.set_default_vars() + post.cf_load() + post.cf_read() + + # Multiple targets ("all"): out is False + # (out to exit on error at single target) + out = not(args.commands["targets"]["all"]) + + article_header = article_writer = "" + separator = False + + with open(post.uri, "r") as article: + article = article.read().rsplit("\n") + for lines, line in enumerate(article, 1): + # Find separator between header and contents writer + if not separator and line.lstrip().startswith(post.separator): + separator = True + + if separator: + if article_writer: article_writer = "%s\n%s"%(article_writer, line) + else: article_writer = line + else: + if article_header: article_header = "%s\n%s"%(article_header, line) + else: article_header = line + + # Separator must be set + if not separator: + debug.out(30, '"%s"'%post.separator, post.uri, True, 2, out) + return False + + # Contents in headers + if not article_header: + debug.out(31, "!?", post.uri, True, 2, out) + return False + + # Contents in article writer + if len(article_writer.rsplit("\n")) == 1: + debug.out(32, "!?", post.uri, True, 2, out) + return False + + + # Remove useless separator at first line + article_writer = \ + article_writer.replace(article_writer.rsplit("\n")[0], "").rsplit("\n") + + article_header = article_header.rsplit("\n") + + # Set statistics + stats = { + "file" : { + "lines" : lines, + "header_lines" : len(article_header), + "writer_lines" : len(article_writer), + }, + # format: "..." : {TagName : int} + "header" : {}, + "writer" : {}, + } + + # Check and set needed header contents (single line tags) + if not header_sl_tags(): return False + + # Check for block codes + global ln_w + ln_w = len(article_header) + 1 # Count article header lines for error logs + + # Check article contents + if not block_tags("bcodes"): return False # Protect bcodes contents + if not words_tags(): return False # Count/check paired words tags + if not icodes(): return False # Protect icodes contents + if not header_ml_tags(): return False # Set values for 3 lines tags + if not sl_ptags(): return False # Count/Chech paragraphs, divs + if not block_tags("bquotes"): return False + if not titles(): return False + if not block_tags("lists"): return False + indep_tags() # Count independant tags + if not anchors(): return False # Anchors source + if not anchors_links(): return False # Anchors links + + return True + + +#=============================================================================# +# ARTICLE HEADER # +#=============================================================================# +#==============================# +# Set from article header # +# - optional marks # +# - needed tags # +# Check if all needed tags set # +#------------------------------# +def header_sl_tags(): + global stats + + # Set needed tags + for ln, line in enumerate(article_header, 1): + # Options_marks (! NOMAP, ! NOTSS, ! TOC, ...) + for mark in post.options_marks: + if not post.options_marks[mark] \ + and line.lstrip().startswith(mark): + post.options_marks[mark] = True + # Needed tags + for tag in post.needed_tags: + if not post.needed_tags[tag] and \ + line.lstrip().startswith("%s:"%tag): + tag_set = line.rsplit("%s:"%tag)[1].lstrip() + if not tag_set: + debug.out(33, "%s. '%s: ?'"%(ln, tag), post.uri, True, 2, out) + return False + # For logo and date, get ln + if tag == "logo": + logo_ln = ln + elif tag == "date": + date_ln = ln + + post.needed_tags[tag] = tag_set + + # Article logo (Auto set if not set by user) + if not post.needed_tags["logo"]: + post.needed_tags["logo"] = \ + "%stemplate/%s"%( + domain.web["www"], domain.cf.get("TEMPLATE_FILENAMES", "logo") + ) + else: + global tag_set_l2 + tag_set_l2 = post.needed_tags["logo"] + tag_uri_file("logo", logo_ln) + post.needed_tags["logo"] = domain.web["www"][:-1] + tag_set_l2 -#==========================================================# -# Warn if unused registred file in wrk template/ directory # -# Done only once # -# - in case of "all" or direct "wip" # -#----------------------------------------------------------# -def wrk_template_files(): - global check_tpl_files + # Check if all needed tags are set + for tag in post.needed_tags: + if not post.needed_tags[tag]: + debug.out(33, '"%s: ?"'%tag, post.uri, True, 2, out) + return False + + # Date format must be YYY-MM-DD + try: + datetime.date.fromisoformat(post.needed_tags["date"]) + except ValueError: + debug.out(34, "%s. date: %s ? (YYYY-MM-DD)"%(date_ln, post.needed_tags["date"]), post.uri, True, 2, out) + return False + + # Count authors + stats["header"]["authors"] = len(post.needed_tags["authors"].rsplit(",")) + + return True + + +#==============================================# +# loop article header and set multi-lines tags # +#----------------------------------------------# +def header_ml_tags(): + global tags_l1 - try: check_tpl_files ; return - except: pass + tags_l1 = () + tag_found = False + pass_line = 2 # 3 lines to pass if a tag is found - for key, uri in domain.cf.items("USER_TEMPLATE_FILES"): - if not os.path.exists(uri): - debug.out(5, key, uri, True, 1, False) + for ln, line in enumerate(article_header, 1): + # When a tag was found, pass 2 configuration lines + if pass_line == 0: + tag_found = False + pass_line = 2 + elif tag_found: + pass_line -= 1 + continue + + for tag in post.option_tags: + if line.lstrip().startswith("%s:"%tag): + if not get_ml_tag_l1(tag, ln, line): return False + if not get_ml_tag_l2(tag, ln, line): return False + if not get_ml_tag_l3(tag, ln, line): return False + dict_ml_tag(tag) + tag_found = True + + return True + + +#=======================================# +# Check set line 1 from multi-lines tag # +#---------------------------------------# +def get_ml_tag_l1(tag, ln, line): + global tag_set_l1, tags_l1, tag_mark - check_tpl_files = True + tag_set_l1 = line.rsplit("%s:"%tag)[1].lstrip() + # tag not set or contains tag marker + if not tag_set_l1: + debug.out(33, '%s. %s: ?'%(ln, tag), post.uri, True, 2, out) + return False + elif post.marker_tags%"" in tag_set_l1: + debug.out(34, "%s. %s: '%s' > '%s'"%( + ln, tag, tag_set_l1, post.marker_tags%"" + ), post.uri, True, 2, out) + return False + + # Check if tag_set_l1 is uniq + if tags_l1.count(tag_set_l1): + debug.out(35, "%s. '%s'"%(ln, tag_set_l1), post.uri, True, 2, out) + return False + # Is new : add tag_set_l1 to others in tuple + tags_l1 = tags_l1 + (tag_set_l1,) + + # Set writer mark from tag_set_l1 (default "::tag_set_l1") + tag_mark = post.marker_tags%tag_set_l1 + # Count if tag mar is found in article writer + has_mark = sum(tag_mark in s for s in article_writer) + if has_mark == 0: + debug.out(36, '%s: "%s"'%(tag, tag_mark), post.uri, True, 2, out) + return False + + # Add stat + try: stats["writer"]["%ss"%tag] += has_mark + except: stats["writer"]["%ss"%tag] = has_mark + + return True + + +#=======================================# +# Check set line 2 from multi-lines tag # +#---------------------------------------# +def get_ml_tag_l2(tag, ln, line): + global tag_set_l2 + + tag_set_l2 = article_header[ln].lstrip() + + # Tag not set + if not tag_set_l2: + debug.out(33, '%s. %s: ? (L2)'%(ln + 1, tag), post.uri, True, 2, out) + return False + + # Line starts with a tag name + for item in post.option_tags: + if tag_set_l2.startswith("%s:"%item): + debug.out(34, "%s. %s: '%s'"%( + ln + 1, tag, tag_set_l2 + ), post.uri, True, 2, out) + return False + + # Check uri file + if tag in post.nofile_tags: + return True + + # Check uri file set + if not tag_uri_file(tag, ln + 1): + return False + + return True + + +#=============================================# +# Get full file uri # +# check if file exists # +# Set srv_uri and web_uri file # +# Put in dicts (post.option_tags, post.datas) # +#---------------------------------------------# +def tag_uri_file(tag, ln): + global tag_set_l2 # will replace current value with web_uri + + mark_uri = tag_set_l2[:1] + + # URI file targets generic directories + if mark_uri == "@": + real_uri = tag_set_l2[1:] + srv_uri = "files/" + real_uri + web_uri = "/" + srv_uri + src_dir = domain.wrk_dirs["files"] + if tag in ("image", "logo"): + src_dir = domain.wrk_dirs["images"] + srv_uri = "images/" + real_uri + web_uri = "/" + srv_uri + + # URI file targets root articles/ directory + elif mark_uri == "/": + real_uri = tag_set_l2[1:] + srv_uri = real_uri + web_uri = tag_set_l2 + src_dir = domain.wrk_dirs["articles"] + + # URI file targets from current article directory + else: + real_uri = tag_set_l2 + src_dir = domain.wrk_dirs["articles"] + post.datas["dir_target"] + srv_uri = post.datas["dir_target"] + real_uri + web_uri = "/" + srv_uri + + # CHeck if file set for this tag exists + src_file = src_dir + real_uri + if not os.path.exists(src_file): + debug.out(11, "%s. %s: '%s'"%( + ln, tag, tag_set_l2 + ), src_file, True, 2, out) + return False + + global f_nbr + try: f_nbr += 1 + except: f_nbr = 1 + post.datas["files"][f_nbr] = (src_file, srv_uri) + tag_set_l2 = web_uri + + return True + + +#=======================================# +# Check set line 3 from multi-lines tag # +#---------------------------------------# +def get_ml_tag_l3(tag, ln, line): + global tag_set_l3 + + tag_set_l3 = article_header[ln + 1].lstrip() + if not tag_set_l3: + debug.out(33, '%s. %s: ? (L3)'%(ln + 2, tag), post.uri, True, 2, out) + return False + + for item in post.option_tags: + if tag_set_l3.startswith("%s:"%item): + debug.out(34, "%s. %s: '%s'"%( + ln + 1, tag, tag_set_l2 + ), post.uri, True, 2, out) + return False + + return True + #====================================# -# Check wip and www dirs and files # -# Done only once # -# - in case of "all" or direct "wip" # +# Add multi-lines tag values to dict # #------------------------------------# -def srv_domain(target): - global check_srv_domain - - try: check_srv_domain ; return - except: pass - - for section in ("%s_DIRS"%target.upper(), "%s_FILES"%target.upper()): - # Set err number for directories or files - if section.endswith("DIRS"): err = 6 - else: err = 5 - - for key, uri in domain.cf.items(section): - if not os.path.exists(uri): - debug.out(err, key, uri, True, 1, False) - - check_srv_domain = True - - -#================================# -# Check article(S) # -# Also used with multiple (loop) # -#--------------------------------# -def is_article(target): - if not valid(target) or post.error != 0: - if targets: return - else: sys.exit(post.error) - - # When all is OK - # Will create post database, but now, show some values - """ - print("> check: Final texts string...") - print('\n'.join(texts)) - """ - - # Write to post database - cf_update_values("after") - - -#===========================================# -# Check full article contents (head + text) # -# In error case, exit or return if targetS # -#-------------------------------------------# -def valid(target): - global targets - targets = args.targets - - # Target is a tyto article format - if not post.is_article(target): - return False - - if not post.do_chk: - post.error = debug.out(210, post.chk_date, post.uri, True, 0, False) - return False - - # Reset: must be done only when multiple targets - if targets: - post.title = ("title:", False) - post.about = ("about:", False) - post.date = ("date:", False) - post.tags = ("tags:", False) - post.author = ("author:", False) - post.logo = ("logo:", False) # optional - - post.stats_bcodes = 0 - post.stats_quotes = 0 - post.stats_parags = 0 - post.stats_lists = 0 - post.stats_divs = 0 - - post.stats_links = 0 - post.stats_images = 0 - post.stats_files = 0 - post.stats_raws = 0 - post.stats_codes = 0 - post.stats_abbrs = 0 - - post.stats_text_links = 0 - post.stats_text_files = 0 - post.stats_text_images = 0 - post.stats_text_abbrs = 0 - post.stats_text_codes = 0 - post.stats_text_raws = 0 - post.error = 0 - - # Reset post database (Old values are prepared) - tools.create_file(post.cf_uri, post.ini_template) - post.cf_load() - - # Add statistics to post database - cf_update_values("before") - - global css - css = domain.css - - global headers, texts - headers = post.head_contents.rsplit("\n") - texts = post.text_contents.rsplit("\n") - - # ============ - # Text article - # ============ - # Process bcodes and icodes first to protect their contents - post.error == 0 and sl_ptags(post.ptags[0]) \ - or tools.exit(targets, post.error) - post.error == 0 and icodes() \ - or tools.exit(targets, post.error) - - # ============= - # Head contents - # ============= - # One Line targs in head_contents - debug.out(215, "check. Headers", post.cf_uri, False, 0, False) - post.error == 0 and ol_tags() \ - or tools.exit(targets, post.error) - # Multiple and optional Tags on 3 linges - post.error == 0 and ml_tags() \ - or tools.exit(targets, post.error) - - # ============ - # Text article - # ============ - # Single tags - post.error == 0 and sl_stags() \ - or tools.exit(targets, post.error) - - # Anchors links - post.error == 0 and anchors_links() \ - or tools.exit(targets, post.error) - - # Words Tags (Strong, bold...) - post.error == 0 and words_tags() \ - or tools.exit(targets, post.error) - - # Quotes - post.error == 0 and sl_ptags(post.ptags[1]) \ - or tools.exit(targets, post.error) - - # Lists - post.error == 0 and sl_ptags(post.ptags[3]) \ - or tools.exit(targets, post.error) - - # Paragraphs - post.error == 0 and sl_ptags(post.ptags[2]) \ - or tools.exit(targets, post.error) - - # divs - post.error == 0 and sl_ptags(post.ptags[4]) \ - or tools.exit(targets, post.error) - - post.error > 0 and tools.exit(targets, post.error) - - return True - - -#===========================================# -# Create a loop to get all .tyto articles # -#-------------------------------------------# -def multiple_targets(): - ready() - post.find_tyto_article() - - -#=====================# -# check head contents #======================================================== -#=====================# -#======================# -# One Line needed tags # -#----------------------# -def ol_tags(): - global sitemap, src_uri, stats_tyto_head_coms - global stats_total_files - - stats_total_files = 0 - post.cf_set("HEADERS", "sitemap", "True") - stats_tyto_head_coms = 0 - - for ln, line in enumerate(headers, 1): - - # Optional one line markers - if line.startswith("#"): - stats_tyto_head_coms += 1 - post.cf_set("STATS_HEADERS", "tyto_coms", str(stats_tyto_head_coms)) - - elif line.startswith(post.nositemap): - post.cf_set("HEADERS", "sitemap", "False") - - elif line.startswith(post.logo[0]): - if not post.logo[1]: - post.logo = (post.logo[0], ol_tag_value(line, False)) - logo_ln = ln - if is_value2_file_exists(logo_ln, "logo:", post.logo[1]): - src_uri = "%s%s"%(domain.www_url, src_uri) - else: - return False - - # One Line tags (Must be set) - # =========================== - elif not post.title[1] and line.startswith(post.title[0]): - post.title = (post.title[0], ol_tag_value(line, False)) - if not is_ol_tag(post.title[0], post.title[1]): - return False - post.cf_set("HEADERS", "title", post.title[1]) - - elif not post.about[1] and line.startswith(post.about[0]): - post.about = (post.about[0], ol_tag_value(line, False)) - if not is_ol_tag(post.about[0], post.about[1]): - return False - post.cf_set("HEADERS", "about", post.about[1]) - - elif not post.date[1] and line.startswith(post.date[0]): - post.date = (post.date[0], ol_tag_value(line, False)) - if not is_ol_tag(post.date[0], post.date[1]): - return False - elif not is_valid_date(post.date[1]): - return False - post.date = ("date:", tools.local_date(post.date[1])) - post.cf_set("HEADERS", "date", post.date[1]) - - elif not post.author[1] and line.startswith(post.author[0]): - post.author = (post.author[0], ol_tag_value(line, True)) - if not is_ol_tag(post.author[0], post.author[1]): - return False - post.cf_set("HEADERS", "authors", post.author[1]) - - elif not post.tags[1] and line.startswith(post.tags[0]): - post.tags = (post.tags[0], ol_tag_value(line, True)) - if not is_ol_tag(post.tags[0], post.tags[1]): - return False - post.cf_set("HEADERS", "tags", post.tags[1]) - - # Default domain logo for this post - if not post.logo[1]: - src_uri = "%stemplate/%s"%(domain.www_url, domain.logo) - post.logo = (post.logo[0], src_uri) - - post.cf_set("HEADERS", "logo", src_uri) - - return True - - -#===========================================# -# Return value from one line after "[tag]:" # -# tags, author are comma separated # -# set new value, removing spaces (strip) # -#-------------------------------------------# -def ol_tag_value(line, commas): - value = line.rsplit(":")[1].lstrip() - - # reformat comma separated items, removing first spaces - if commas: - tuple_values = value.rsplit(",") - value = "" - for i, item in enumerate(tuple_values): - value = value + item.strip() - if i != len(tuple_values) - 1: - value = value + "," - - return value - - -#===========================# -# Check if tag value is set # -# Return True/False # -#---------------------------# -def is_ol_tag(tag, value): - if not value: - post.error = debug.out(51, "%s ?"%tag, post.uri, True, 2, False) - return False - - return True - - -#======================================# -# Check if date id valid # -# Set date of check (YYYY-MM-DD H:M:S) # -# Return True/False # -#--------------------------------------# -def is_valid_date(date): - global chk_date - +def dict_ml_tag(tag): try: - parse(date) - chk_date = tools.nowdate() - post.cf_set("CHECK", "date", chk_date) - return True + stats["header"]["%ss"%tag] += 1 + nbr = stats["header"]["%ss"%tag] except: - post.error = debug.out(50, "%s"%date, post.uri, True, 2, False) + nbr = stats["header"]["%ss"%tag] = 1 + + + post.option_tags[tag][nbr] = \ + (len(tag_set_l1), tag_mark, tag_set_l1, tag_set_l2, tag_set_l3) + + +#=============================================================================# +# ARTICLE WRITER # +#=============================================================================# +#======================================================# +# Check indented first marks for bcode, quotes, lists # +# Put block contents in post.block_tags[tag]["source"] # +# Replace bcodes in article writer to protect contents # +#------------------------------------------------------# +def block_tags(tag): + global article_writer + + mark_o = post.block_tags[tag]["marks"][0] + mark_c = post.block_tags[tag]["marks"][1] + tag_i = -1 + tag_c = False + block_source = "" + + for ln, line in enumerate(article_writer, ln_w): + # bcode not yet opened, but begin here + if tag_i == -1 and \ + line.lstrip().startswith(mark_o): + tag_i = line.index(mark_o[0]) + mark_ln = ln + + # bcode was opened and mark closed here + elif tag_i != -1 and \ + line.lstrip().startswith(mark_c) and \ + line.index(mark_c[0]) == tag_i: + tag_c = True + + # tag is open + if tag_i != -1: + # replace line in article for bcodes only + if tag == "bcodes": + article_writer[ln - stats["file"]["header_lines"] - 1] = " " + + if not block_source: block_source = line + else: block_source = "%s\n%s"%(block_source, line) + + # bcode is closed here + if tag_c: + # Create or add to stats + try: stats["writer"][tag] += 1 + except: stats["writer"][tag] = 1 + nbr = stats["writer"][tag] + + # convert html signe, and convert bcode to base64 + bcode_H = tools.get_HID(block_source, False) + + # Put bcode in dict with nbr bcode + post.block_tags[tag]["sources"][nbr] = (block_source, bcode_H) + + # Replace last line with close mark + if tag == "bcodes": + article_writer[ln - stats["file"]["header_lines"] - 1] = bcode_H + + # Reset all for next block + tag_i = -1 + tag_c = False + block_source = "" + + # bcode wan NOT closed + if tag_i != -1 and not tag_c: + debug.out(37, "%s. %s: '%s' ?"%( + mark_ln, tag, mark_c + ), post.uri, True, 2, out) return False + return True + #============================# -# multiple and optional Tags # -# Written using 3 lines # -# Tags are stric start line # +# Check and count words tags # +# incliding icodes # #----------------------------# -def ml_tags(): - c = 0 # Continue for next x lines, as tags are 3 lines values - - for ln, line in enumerate(headers): - if c != 0: - c -= 1 - continue +def words_tags(): + for tag in post.words_tags: + opened = closed = 0 + for line in article_writer: + if post.words_tags[tag][0] in line: + tag_o = line.count(post.words_tags[tag][0]) + try: stats["writer"][tag] += tag_o + except: stats["writer"][tag] = tag_o + opened = stats["writer"][tag] + if post.words_tags[tag][1] in line: + closed += line.count(post.words_tags[tag][1]) - if line.startswith(post.ml_tags): - tag_name = line.rsplit(":")[0] - tag = tag_name + ":" - - post.ml_tags_stats[tag] = post.ml_tags_stats[tag] + 1 - - if not ml_tag_values(ln, tag, post.ml_tags_stats[tag]): - return False - - c = 2 - post.cf_set("STATS_HEADERS", - "%ss"%tag_name, - str(post.ml_tags_stats[tag]) - ) - continue + if opened != closed: + debug.out(33, "%s. %s '%s', %s '%s'"%( + tag, + opened, post.words_tags[tag][0], + closed, post.words_tags[tag][1] + ), post.uri, True, 2, out) + return False return True -#========================================# -# Get tag 3 lines values # -# Check if 2nd, 3rd lines starts with: # -# - tag, comment or are empty # -# Create var and val for post database # -# Return True/False (if no value) # -#----------------------------------------# -def ml_tag_values(ln, tag, stats): - global value1, value2 +#===========================================================# +# ICODES (Secure raw contents) # +# Replace in article writer each inline-code with their HID # +# Put stats and datas in dicts # +#-----------------------------------------------------------# +def icodes(): + global article_writer + + try: stats["writer"]["icodes"] + except: return True - # Get 3 lines values - value1 = headers[ln].rsplit(":")[1].lstrip() - value2 = headers[ln+1].lstrip() - value3 = headers[ln+2].lstrip() + total = 0 + for ln, line in enumerate(article_writer, ln_w): + if post.words_tags["icodes"][0] in str(line) and \ + post.words_tags["icodes"][1] in str(line): + icodes = re.findall(r'%s(.*?)%s'%( + post.words_tags["icodes"][0], + post.words_tags["icodes"][1] + ), line) + + for nbr, icode in enumerate(icodes, 1): + total += 1 + # Put in dict + icode_H = tools.get_HID(icode, False) + post.icodes["sources"][total] = (icode, icode_H) + + # Replace line in wrticle_writer + line = line.replace(icode, icode_H) + article_writer[ln - stats["file"]["header_lines"] - 1] = line - # Check values (not empty or begins with a tag) - # value1 - # ------ - if not value1: - post.error = \ - debug.out(51, "%s) %s 1/3"%(ln+1, tag), post.uri, True, 2, False) - return False - - # value1 must not starts or contains:o - elif "_" in value1: - post.error = \ - debug.out(56, '%s) "_" : "%s"'%(ln+1, value1), post.uri, True, 2, False) - return False - - # Specific for image: logo name is reserved - elif tag == post.ml_tags[1] and value1 == "logo": - post.error = debug.out(56, "%s) 'logo'"%(ln+1), post.uri, True, 2, False) - return False - - # value2 - # ------ - if not value2 or value2.startswith(post.ml_tags): - post.error = \ - debug.out(51, "%s) %s 2/3"%(ln+2, tag), post.uri, True, 2, False) - return False - - # value3 - # ------ - if not value3 or value3.startswith(post.ml_tags): - post.error = \ - debug.out(51, "%s) %s 3/3"%(ln+3, tag), post.uri, True, 2, False) - return False - - # No error with values - # Convert value1 in header with tyto_value1 in text - tyto_value = post.ml_tags_marks[tag] + value1 - - # CHeck if value is in text and to stats or return error - if post.text_contents.find(tyto_value) == -1: - post_error = debug.out(51, tyto_value, post.uri, True, 2, False) - return False - - # Check value2 link for some tags (file, image...) - if tag in post.value2s_uri \ - and not value2.startswith(post.value2s_ext_uris) \ - and not is_value2_file_exists(ln+2, tag, value2): - return False + return True - #-----------------------------------------------# - # Convert values to HTML (put in post database) # - #-----------------------------------------------# - # link: - if tag == post.ml_tags[0]: - section = "LINKS" - post.stats_text_links += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "links", str(post.stats_text_links)) - html_value = tyto.a_link%( - value2, "%s link"%css, value3, value1 - ) - - # file: - elif tag == post.ml_tags[2]: - section = "FILES" - post.stats_text_files += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "files", str(post.stats_text_files)) - html_value = tyto.a_link%( - value2, "%s file"%css, value3, value1 - ) - - # image: - elif tag == post.ml_tags[1]: - section = "IMAGES" - post.stats_text_images += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "images", str(post.stats_text_images)) - html_value = tyto.image_link%( - value2, "%%s image", value3, - "%%s", value2, "%%s", value3, value3, "%%s", "%%s" - ) - - # raw: (content file converted to base64) - elif tag == post.ml_tags[3]: - section = "RAWS" - post.stats_text_raws += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "raws", str(post.stats_text_raws)) - html_value = ""%value3 - with open(value2_uri, "r") as f: - html_value = "%s\n%s"%(html_value, f.read()) - - html_value = tools.b64_convert("encode", html_value) - - # code: (content file converted to HTML + base64) - elif tag == post.ml_tags[4]: - section = "CODES" - post.stats_text_codes += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "codes", str(post.stats_text_codes)) - htmlbcode = ""%value3 - with open(value2_uri, "r") as f: - for ln, line in enumerate(f.read().rsplit("\n"), 1): - line = tools.convert_html_signs(line) - line = tyto.code_line%(ln, line) - htmlbcode = "%s\n%s"%(htmlbcode, line) +#===============================# +# Check start-lines paired tags # +#-------------------------------# +def sl_ptags(): + # Do divs, paragraphs... tags count + for tag in post.sl_ptags: + tot_o = tot_c = 0 + for ln, line in enumerate(article_writer, ln_w): + # Simple count of opened and closed tags + if line.lstrip().startswith(post.sl_ptags[tag][0]): tot_o += 1 + elif line.lstrip().startswith(post.sl_ptags[tag][1]): tot_c += 1 + + if tot_o != tot_c: + debug.out(38, '%s: "%s"=%s, "%s"=%s'%( + tag, + post.sl_ptags[tag][0], tot_o, + post.sl_ptags[tag][1], tot_c, + ), post.uri, True, 2, out) + return False + + stats["writer"][tag] = tot_o - footer_code = '\n
          \n' + \ - '

          \n' + \ - '%s\n'%( - value2, value3, "Source" - ) + \ - '

          \n' + \ - '
          ' - html_value = tyto.code_bcode%(css, htmlbcode, footer_code) - html_value = tools.b64_convert("encode", html_value) - - # abbr: - elif tag == post.ml_tags[5]: - section = "ABBRS" - post.stats_text_abbrs += post.text_contents.count(tyto_value) - post.cf_set("STATS_TEXTS", "abbrs", str(post.stats_text_abbrs)) - html_value = '%s'%( - css, value2, value3 - ) - - # Set in post database, source and HTML content - post.cf_set(section, "%s_%s"%(tag.replace(":", ""), stats), tyto_value) - post.cf_set(section, "html_%s"%stats, html_value) - return True - - -#================================# -# Check value2 uri for some tags # -# uri starts with: # -# - @... for generic articles/ # -# - /... for root work directory # -# - ... for post directory # -#--------------------------------# -def is_value2_file_exists(ln, tag, val2): - global value2, src_uri, value2_uri, stats_total_files - - # uri "@..." means generic folders - if val2[0].startswith("@"): - if val2[1] == "/": val2 = val2[2:] - else: val2 = val2[1:] - - #Set directory for files - value2_uri = os.path.join(domain.wrk_files + val2) - value2 = src_uri = os.path.join("/files", val2) - - # Set directory for images in /images - if tag == post.ml_tags[1] or tag == post.logo[0]: # image: - value2_uri = os.path.join(domain.wrk_images, val2) - value2 = src_uri = os.path.join("/images", val2) - - # uri "/..." means from wrk root folder - elif val2[0].startswith("/"): - val2 = val2[1:] - value2_uri = os.path.join(domain.wrk_articles, val2) - src_uri = val2 - - # uri "..." means from legacy post folder - else: - value2_uri = os.path.dirname(post.uri) + "/" + val2 - value2 = "./" + val2 - src_uri = value2_uri.rsplit("articles/")[1] - - # Check if file exists - if not os.path.exists(value2_uri): - post.error = \ - debug.out(5, "%s) %s"%(ln, tag), value2_uri, True, 2, False) - return False - - # Add file to [SOURCE_FILES] post database - stats_total_files += 1 - post.cf_set("SOURCE_FILES", "file_%s"%stats_total_files, value2_uri) - post.cf_set("STATS_FILE", "files", str(stats_total_files)) - - return True - - -#=====================# -# check text contents #======================================================== -#=====================# -#=========================================# -# start line paired tags # -# Generic check for all paired markers # -# Check if opened and closed tags match # -# Count markers for stats # -# bcodes: remove lines from texts # -# Convert content between markers to html # -# (except for paragraphs) # -# convert html content to base64 # -# create sections, keys values to post DB # -# Return True/False # -#-----------------------------------------# -def sl_ptags(markers): - global texts - - debug.out(215, "check. %s"%markers[2], post.cf_uri, False, 0, False) - - convert = { - "bcodes" : wip.bcode, - "quotes" : wip.quote, - "lists" : wip.list, - } - - index0 = index1 = -1 - - for ln, line in enumerate(texts, post.head_lines + 1): - # Marker is opened - if index0 >= 0 : - content = "%s\n%s"%(content, line) - if markers[2] == "bcodes": - if index1 != index0: - texts[ln - 1 - post.head_lines] = "" - - # Opened mark - if line.lstrip().startswith(markers[0]): - # Mark was opened yet - if index0 >= 0: - # at the same position - if line.index(markers[0][0]) == index0: - post.error = \ - debug.out(53, '%s: %s...%s) %s'%( - markers[2], tag_ln, ln, markers[1] - ), post.uri, True, 2, False) - return False - continue - csstest = tools.get_css(line, markers[0], ln) - if post.error > 0: - return - - index0 = line.index(markers[0][0]) - tag_ln = ln - content = line - if markers[2] == "bcodes": - texts[ln - 1 - post.head_lines] = "" - - # Closed mark - if line.lstrip().startswith(markers[1]): - # But not opened - if index0 < 0: - post.error = \ - debug.out(53, '%s) %s: %s...'%( - ln, markers[2], markers[0] - ), post.uri, True, 2, False) - return False - - index1 = line.index(markers[1][0]) - # at same position as opened - if index1 == index0: - index0 = index1 = -1 - tag_ln = "" - post.ptags_stats[markers[2]] += 1 - post.cf_set("STATS_TEXTS", - markers[2], - str(post.ptags_stats[markers[2]]) - ) - - # paragraphs don't need html wip yet - if markers[2] in ("parags", "divs"): - continue - - elif markers[2] == "bcodes": - texts[ln - 1 - post.head_lines] = \ - "%s_%s"%(markers[2][:-1], post.ptags_stats[markers[2]]) - - # Set keys, values to post database - post.cf_set( - markers[2].upper(), - "%s_%s"%(markers[2][:-1], post.ptags_stats[markers[2]]), - tools.b64_convert("encode", content) - ) - post.cf_set( - markers[2].upper(), - "html_%s"%post.ptags_stats[markers[2]], - tools.b64_convert("encode", - convert[markers[2]](content, ln) - ) - ) - - # bcode not opened, but closed tag found - elif index0 < 0: - tag_ln = ln - - # Check if markers match - # ---------------------- - if index1 >= 0 and index0 < 0: - post.error = \ - debug.out(53, '%s) %s: %s...'%( - tag_ln, markers[2], markers[0] - ), post.uri, True,2, False) - return False - - if index0 >= 0 and index1 < 0: - post.error = \ - debug.out(53, '%s) %s: ...%s'%( - tag_ln, markers[2], markers[1] - ), post.uri, True,2, False) - return False - - if index0 != index1: - post.error = \ - debug.out(53, '%s) %s: %s...%s'%( - tag_ln, markers[2], markers[0], markers[1] - ), post.uri, True,2, False) - return False - - return True - #==========================================# -# icodes (SAME LINE) # -# Check if opened and closed markers match # -# Convert double marks, then single markes # -# replace icodes with "" in texts article # -# Add source text and html converted to DB # +# For stats: count hr, br, anchors (sourc) # #------------------------------------------# -def icodes(): - global texts - - debug.out(215, "check. icodes", post.cf_uri, False, 0, False) - - stats_text_icodes = 0 - markers = post.words_markers - - for ln, line in enumerate(texts, post.head_lines + 1): - # Search for opened, closed markers - # markers cannot be at same position on line - m0 = line.find(markers[1][0]) - m1 = line.find(markers[1][1]) - if m0 == m1: - continue - - # Simple 3 cases errors - # --------------------- - # First marker is a closed one - if m1 < m0: - post.error = \ - debug.out(53, '%s) %s..."%s"'%( - ln, markers[1][1], markers[1][0] - ), post.uri, True,2, False) - return False - - # First marker but unused closed one - if m0 >= 0 and m1 == -1: - post.error = \ - debug.out(53, '%s) ...%s'%( - ln, markers[1][1] - ), post.uri, True,2, False) - return False - - # Last marker but unused opened one - elif m1 >= 0 and m0 == -1: - post.error = \ - debug.out(53, '%s) %s...'%( - ln, markers[1][0] - ), post.uri, True,2, False) - return False - - if markers[0][0] in line: - # Double mark first - line = line.replace(markers[0][0], markers[0][4]) - line = line.replace(markers[0][1], markers[0][5]) - icodes = re.findall('%s(.*?)%s'%(markers[0][4], markers[0][5]), line) - - for icode in icodes: - stats_text_icodes += 1 - icleg = markers[0][0] + icode + markers[0][1] - icnew = markers[0][4] + \ - tools.convert_html_signs(icode) + \ - markers[0][5] - icrep = markers[0][4] + \ - icode + \ - markers[0][5] - line = line.replace(icrep, "icode_%s"%stats_text_icodes) - - # Set HTML [ICODES] to post database - html_val = icnew.replace(markers[0][4], markers[0][2]%css) - html_val = html_val.replace(markers[0][5], markers[0][3]) - html_val = tools.b64_convert("encode", html_val) - post.cf_set( - "ICODES", - "icode_%s"%stats_text_icodes, - icleg - ) - post.cf_set( - "ICODES", - "html_%s"%stats_text_icodes, - html_val - ) - - # Single mark - line = line.replace(markers[1][0], markers[1][4]) - line = line.replace(markers[1][1], markers[1][5]) - icodes = re.findall('%s(.*?)%s'%(markers[1][4], markers[1][5]), line) - - for icode in icodes: - stats_text_icodes += 1 - icleg = markers[1][0] + icode + markers[1][1] - icnew = markers[1][4] + \ - tools.convert_html_signs(icode) + \ - markers[1][5] - icrep = markers[1][4] + \ - icode + \ - markers[1][5] - line = line.replace(icrep, "icode_%s"%stats_text_icodes) - - # Set HTML [ICODES] to post database - html_val = icnew.replace(markers[1][4], markers[1][2]%css) - html_val = html_val.replace(markers[1][5], markers[1][3]) - html_val = tools.b64_convert("encode", html_val) - post.cf_set( - "ICODES", - "icode_%s"%stats_text_icodes, - icleg - ) - post.cf_set( - "ICODES", - "html_%s"%stats_text_icodes, - html_val - ) - - texts[ln - 1 - post.head_lines] = line - - post.cf_set("STATS_TEXTS", "icodes", str(stats_text_icodes)) - - return True +def indep_tags(): + for tag in post.indep_tags: + for ln, line in enumerate(article_writer, ln_w): + if line.lstrip().startswith(post.indep_tags[tag]): + try: stats["writer"][tag] += 1 + except: stats["writer"][tag] = 1 -#============================# -# start line single tags # -# Check optional title tags # -# Count tyto + html comments # -# Add stat for _image:logo # -# Return True/False # -#----------------------------# -def sl_stags(): - global anchors_ids, stats_tyto_text_coms - - debug.out(215, "check. Contents", post.cf_uri, False, 0, False) + +#=================================# +# Check titles # +# - put them in tuple post.titles # +# - Count them # +#---------------------------------# +def titles(): + global article_writer, anchors_ids anchors_ids = () # Uniq anchors IDs - stats_tyto_text_coms = stats_html_coms = 0 - stats_text_anc_ids = 0 - stats_titles = 0 - for ln, line in enumerate(texts, post.head_lines + 1): - linels = line.lstrip() + for ln, line in enumerate(article_writer, ln_w): + line = line.lstrip() + + # User needs Table of content + if not post.options_marks["! TOC"] \ + and line.startswith("! TOC"): + post.options_marks["! TOC"] = True - # Tyto Titles - if linels.startswith(post.tyto_titles): - if not linels[3:]: - post.error = \ - debug.out(52, "%s) %s ?"%(ln, line), post.uri, True, 2, False) + # Add title + elif line.startswith(post.title_marks): + mark = str(line[0] + line[1]) + if line.rsplit(mark)[1]: + post.titles = post.titles + (line,) + try: stats["writer"]["titles"] += 1 + except: stats["writer"]["titles"] = 1 + anchors_ids = anchors_ids + ("toc_%s"%stats["writer"]["titles"],) + continue + else: + debug.out(33, "%s. '%s ?'"%(ln, mark), post.uri, True, 2, out) return False - - # Avoid wanting #6 - #9 (but accept #1x.. #5x.. as comments...) - elif linels[1].isdigit() and int(linels[1]) >= 6: - post.error = \ - debug.out(52, "%s) %s..."%( - ln, linels[0:10] - ), post.uri, True, 1, False) - return False - - stats_titles += 1 - post.cf_set("STATS_TEXTS", "titles", str(stats_titles)) - - # Create html value for this title in database - post.cf_set( - "TITLES", - "title_%s"%stats_titles, - line - ) - post.cf_set( - "TITLES", - "html_%s"%stats_titles, - post.html_titles[linels[0:2]]%(css, line[3:]) - ) - - # Count Tyto Comments - elif line.lstrip().startswith("#"): - stats_tyto_text_coms += 1 - post.cf_set("STATS_TEXTS", "tyto_coms", str(stats_tyto_text_coms)) - - - # Count HTML comments - elif line.lstrip().startswith(post.text_comments): - stats_html_coms += 1 - post.cf_set("STATS_TEXTS", "html_coms", str(stats_html_coms)) - - # Convert tyto commented marker to HTML - if line.lstrip().startswith(post.text_comments[0]): - real_com = line.lstrip()[3:] - post.cf_set( - "COMMENTS", - "comment_%s"%stats_html_coms, - line.lstrip() - ) - post.cf_set( - "COMMENTS", - "html_%s"%stats_html_coms, - ''%real_com - ) - - - # Add stat + html for [IMAGES] when user wants to show logo in post - elif line.lstrip().startswith("_image:logo"): - post.stats_text_images += 1 - post.cf_set( - "IMAGES", - "image_%s"%(post.ml_tags_stats["image:"] + 1), - "_image:logo" - ) - post.cf_set( - "IMAGES", - "html_%s"%(post.ml_tags_stats["image:"] + 1), - tyto.image_link%( - post.logo[1], "%%s image", post.title[1], - "%%s", post.logo[1], "%%s", post.title[1], post.title[1], - "%%s", "%%s" - ) - ) - # BR line - elif line.lstrip().startswith(post.html_brline[0]): - brcss = tools.get_css(line, post.html_brline[0], ln) - - # HR line - elif line.lstrip().startswith(post.html_hrline[0]): - hrcss = tools.get_css(line, post.html_hrline[0], ln) - - - # Anchor source - elif line.lstrip().startswith(post.anchor_target[0]): - anchor_id = tools.get_css(line, post.anchor_target[0], ln) - if anchor_id in anchors_ids: - post_error = \ - debug.out(54, '%s) "%s"'%(ln, anchor_id), post.uri, True, 2, False) - return False - anchors_ids = (*anchors_ids, anchor_id) - - stats_text_anc_ids += 1 - post.cf_set("STATS_TEXTS", "anc_targets", str(stats_text_anc_ids)) + elif line.startswith(("#0", "#6")): + debug.out(34, "%s. %s..."%(ln, line[:3]), post.uri, True, 1, out) + return False + + # Not valid Tyto title + elif line.startswith("#"): + try: stats["writer"]["tyto_comments"] += 1 + except: stats["writer"]["tyto_comments"] = 1 return True -#===========================# -# Find and anchors links # -# Check if anchor_id exists # -#---------------------------# -def anchors_links(): - markers = post.anchor_link - stats_text_anc_links = 0 +#=======================================# +# Check if anchors have uniq identities # +#---------------------------------------# +def anchors(): + try: stats["writer"]["anchors"] + except: return True - for ln, line in enumerate(texts, post.head_lines + 1): - anc_links = re.findall('%s(.*?)%s'%(markers[0], markers[1]), line) - if not anc_links: - continue - - for anc_link in anc_links: - if not ":" in anc_link: - debug.out(51, '%s) ">_id:%s_<"'%( - ln, langs.logs.anchor_title, - ), post.uri, True, 1, False) - continue - - anc_id = anc_link.rsplit(":")[0] + global anchors_ids + + for ln, line in enumerate(article_writer, ln_w): + if line.lstrip().startswith(post.indep_tags["anchors"]): + anc_id = tools.get_css(line, post.indep_tags["anchors"]) if anc_id in anchors_ids: - anc_title = anc_link.rsplit(":")[1].lstrip() - if not anc_title: - post.error = \ - debug.out(51, '%s) ">_%s:?_<"'%( - ln, anc_id - ), post.uri, True, 2, False) + debug.out(35, "%s. Anchor ID '%s'"%( + ln, anc_id + ), post.uri, True, 2, out) + return False + anchors_ids = anchors_ids + (anc_id,) + + return True + + +#==============================================# +# Check Anchors links ID # +# Create HTML for anchors # +# Put in dict post.anchors for wip # +#----------------------------------------------# +def anchors_links(): + try: stat = stats["writer"]["anc_links"] + except: return True + + total = 0 + for ln, line in enumerate(article_writer, ln_w): + if post.words_tags["anc_links"][0] in str(line) and \ + post.words_tags["anc_links"][1] in str(line): + anchors = re.findall(r'%s(.*?)%s'%( + post.words_tags["anc_links"][0], + post.words_tags["anc_links"][1] + ), line) + + for nbr, anchor in enumerate(anchors, 1): + # CHeck ID in anchors_ids + anc_id = anchor.rsplit(":")[0] + if not anc_id in anchors_ids: + debug.out(33, "%s. Anchor '-> %s'"%( + ln, anc_id + ), post.uri, True, 2, out) return False - # Set to post Database - stats_text_anc_links += 1 - post.cf_set("ANCHORS", - "anchor_%s"%stats_text_anc_links, - post.anchor_set[0]%anc_link - ) - post.cf_set("ANCHORS", - "html_%s"%stats_text_anc_links, - post.anchor_set[1]%(css, anc_id, anc_title) - ) - else: - post.error = \ - debug.out(51, '%s) "-> %s"'%(ln, anc_id), post.uri, True, 2, False) - return False - - post.cf_set("STATS_TEXTS", "anc_links", str(stats_text_anc_links)) - return True - - -#======================================# -# Count and return sub indices in text # -#--------------------------------------# -def find_sub_indices(full, sub): - return [index for index in range(len(full)) if full.startswith(sub, index)] - -#================================================# -# From position of sub tag: return article line # -# When error, to let user know real line article # -# char is indice tag position # -#------------------------------------------------# -def find_line_sub(char): - for fc, lc, ln in lines_chars: - if fc <= char and char <= lc: - return ln - -#======================================= -# CHeck paired words tagss # -# Count for tags stats + words # -# ! No neeed to create HTML in post db # -#--------------------------------------# -def words_tags(): - global lines_chars - - # Create List lines number and number of characters in line - # (In error case, show line number) - old_len = 0 # len(line) - lines_chars = (()) - stats_text_chars = 0 - for ln, line in enumerate(texts, post.head_lines + 1): - stats_text_chars = stats_text_chars + len(line) - lines_chars = lines_chars + ((old_len, stats_text_chars, ln),) - old_len = stats_text_chars - - post.cf_set("STATS_TEXTS", "chars", str(stats_text_chars)) - - # Create article in block text format - # Also count words written - block_texts = '' - for line in texts: - block_texts = block_texts + line - - post.cf_set("STATS_TEXTS", - "words", - str(sum(1 for w in block_texts.split())) - ) - - # For each word tag, get position indicies - for i, tags in enumerate(post.words_tags): - indices_o = find_sub_indices(block_texts, tags[0]) - indices_c = find_sub_indices(block_texts, tags[1]) - if not indices_o and not indices_c: - continue - - # Count and compare opened and closes tags numbers - len_o = len(indices_o) - len_c = len(indices_c) - - # Not paired tags - if len_o != len_c: - post.error = \ - debug.out(53, "%s %s, %s %s"%( - len_o, tags[0], - len_c, tags[1] - ), post.uri, True, 2, False) - return False - - # Test if tags are opened > closed - for n in range(len_o): - # Current opened must be smaller than current closed - if indices_o[n] > indices_c[n]: - post.error = \ - debug.out(53, '%s) "%s...%s"'%( - find_line_sub(indices_o[n]), tags[1], tags[0] - ), post.uri, True, 2, False) - return False + total += 1 + anc_H = '%s'%( + domain.web["css"], + anchor.rsplit(":")[0], + anchor.rsplit(":")[1].lstrip() + ) + post.anchors[total] = ("%s%s%s"%( + post.words_tags["anc_links"][0], + anchor, + post.words_tags["anc_links"][1] + ), anc_H) - # Other mismatches opens/closed tags - else: - # Current closed must be higher than opened + 1 - try: - if indices_c[n] > indices_o[n+1]: - post.error = \ - debug.out(53, '%s) "%s...%s"'%( - find_line_sub(indices_c[n]), tags[0], tags[0] - ), post.uri, True, 2, False) - return False - except: - continue - - # Add tag statistic to post database - post.cf_set("STATS_TEXTS", tags[2], str(len_o)) - return True - - -#==================================# -# Update post configuration file # -# part: # -# before > first generic datas # -# after > after all check modules # -#----------------------------------# -def cf_update_values(part): - # Generic known first datas, before check modules - if part == "before": - post.cf_set("DOMAIN", "name", domain.name) - post.cf_set("CHECK", "static", str(domain.static)) - post.cf_set("CHECK", "hash", post.wrk_id) - - # [FILE] - web_target = post.wrk_target[:-4] + "html" - if web_target.endswith("index.html"): - web_target = web_target[:-10] - if not web_target.startswith("/"): - web_target = "/" + web_target - - # Create HTML local sub uri - post.cf_set("FILE", "id", post.uri_id) - post.cf_set("FILE", "uri", post.uri) - post.cf_set("FILE", "db", post.cf_uri) - post.cf_set("FILE", "target", post.wrk_target) - post.cf_set("FILE", "web", web_target) - post.cf.set("FILE", "sub_uri", post.wrk_target.count('/') * "../" or "./") - - # [WIP] - # ----- - post.cf_set("WIP", "hash", post.wip_hash) - post.cf_set("WIP", "date", post.wip_date) - post.cf_set("WIP", "web", "%s%s"%(domain.wip_url, post.web_target)) - post.cf_set("WIP", "uri", "%s%s"%(domain.wip, post.web_target)) - - # [WWW] - # ----- - post.cf_set("WWW", "hash", post.www_hash) - post.cf_set("WWW", "date", post.www_date) - post.cf_set("WWW", "web", "%s%s"%(domain.www_url, post.web_target)) - post.cf_set("WWW", "uri", "%s%s"%(domain.www, post.web_target)) - - # Stats (these, cause i want them first) - post.cf_set("STATS_FILE", "lines", str(post.lines)) - post.cf_set("STATS_HEADERS", "lines", str(post.head_lines)) - post.cf_set("STATS_TEXTS", "lines", str(post.text_lines)) - - return - - # Datas after check modules - # ------------------------- - # [CHECK] - # ------- - post.cf_set("CHECK", "errors", "False") - - # [STATS_FILE] - # ------------ - stats_tyto_all_coms = stats_tyto_text_coms + stats_tyto_head_coms - stats_tyto_all_coms > 0 and \ - post.cf_set("STATS_FILE", "tyto_coms", str(stats_tyto_all_coms)) - - # Add base64 new article TEXTS - post.cf_set("TEXTS", "post", tools.b64_convert("encode", '\n'.join(texts))) - - # ============================ - # Write new values in database - # ============================ - post.cf_write() - - # Show done message - debug.out(254, "[%s]"%post.title[1], post.uri, True, 0, False) diff --git a/src/var/lib/tyto/program/debug.py b/src/var/lib/tyto/program/debug.py index 2bd8f2c..4160488 100644 --- a/src/var/lib/tyto/program/debug.py +++ b/src/var/lib/tyto/program/debug.py @@ -14,95 +14,75 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Add more logs when debug is set in command line +# Description: Show logs in console. Manage verbose mode (-v) # File: /var/lib/tyto/program/debug.py #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - import sys -import langs, args, logs - +import args, langs, domain #===================# # Messages for logs # #-------------------# def set_messages(): - global messages, got_messages - - try: got_messages ; return + global messages, loaded_messages + + try: loaded_messages ; return except: pass messages = \ { # ERRORS (1-100) - 1 : langs.logs.err_arg, - 2 : langs.logs.err_hole, - 3 : langs.logs.err_dir, - 4 : langs.logs.err_cd, - 5 : langs.logs.err_no_file, - 6 : langs.logs.err_no_dir, - 7 : langs.logs.err_cr_file, - 8 : langs.logs.err_lang, - 9 : langs.logs.err_ini_file, - 10 : langs.logs.err_post_global, - 11 : langs.logs.err_post_not_chk, - 13 : langs.logs.err_post_not_www, - 20 : langs.logs.err_bad_uri, - 21 : langs.logs.err_post_sep, - 22 : langs.logs.err_post_head, - 23 : langs.logs.err_post_empty, - 50 : langs.logs.err_date, - 51 : langs.logs.err_post_data, - 52 : langs.logs.err_post_title, - 53 : langs.logs.err_post_paired, - 54 : langs.logs.err_post_id_yet, - 55 : langs.logs.err_post_in_tag, - 56 : langs.logs.err_post_datatag, - # WARNINGS (100-200) - 100 : langs.logs.warn_no_dom, - 101 : langs.logs.domain_created, - 102 : langs.logs.reset_dom, - 103 : langs.logs.website_lang, - 104 : langs.logs.domains_no, - 105 : langs.logs.domain_off, - 106 : langs.logs.warn_post_chk, - # Great (200-255) - 200 : langs.logs.load_file, - 201 : langs.logs.lang_logs_sys, - 202 : langs.logs.domain_found, - 203 : langs.logs.created_dir, - 204 : langs.logs.domain_updated, - 205 : langs.logs.domain_new, - 206 : langs.logs.created_file, - 207 : langs.logs.updated_file, - 208 : langs.logs.website_lang, - 209 : langs.logs.domain_on, - 210 : langs.logs.post_chk_yet, - 211 : langs.logs.checking_post, - 212 : langs.logs.wipping_post, - 214 : langs.logs.reading_domain, - 215 : langs.logs.processing, - 216 : langs.logs.create_sitemaps, - 250 : langs.logs.completed, - 254 : langs.logs.post_chk_ready, - 255 : langs.logs.later, + 2 : langs.logs.err_argument, + 3 : langs.logs.err_domain_name, + 4 : langs.logs.err_in_hole, + 5 : langs.logs.err_file_create, + 6 : langs.logs.err_dir_unused, + 7 : langs.logs.err_dir_create, + 8 : langs.logs.err_lang_no, + 9 : langs.logs.err_domain_dir, + 10 : langs.logs.err_dir_in, + 11 : langs.logs.err_file_no, + 12 : langs.logs.err_post_db, + 13 : langs.logs.err_post_srv_no, + 30 : langs.logs.err_post_sep, + 31 : langs.logs.err_post_header_no, + 32 : langs.logs.err_post_writer_no, + 33 : langs.logs.err_post_tag_set, + 34 : langs.logs.err_post_tag_val, + 35 : langs.logs.err_post_tag_id, + 36 : langs.logs.err_post_mark_no, + 37 : langs.logs.err_post_mark_stop, + 38 : langs.logs.err_post_mark_miss, + 39 : langs.logs.warn_post_tupic, + 90 : langs.logs.err_process, + 99 : langs.logs.err_kbd_stop, + # WARNINGS + 100 : langs.logs.warn_domain_no, + 101 : langs.logs.warn_date_format, + 102 : langs.logs.warn_domain_conf, + 199 : langs.logs.warn_maybe_later, + # INFOS (200-255) + 200 : langs.logs.inf_dir_user, + 201 : langs.logs.inf_lang_logs, + 202 : langs.logs.inf_domain_load, + 203 : langs.logs.inf_domain_conf, + 205 : langs.logs.inf_file_create, + 206 : langs.logs.inf_file_upd, + 207 : langs.logs.inf_dir_create, + 210 : langs.logs.inf_check_post, + 211 : langs.logs.inf_wip_process, + 212 : langs.logs.inf_mod_process, + 250 : langs.logs.inf_end_process, } - got_messages = True + loaded_messages = True #===================================# @@ -118,18 +98,13 @@ def set_messages(): def out(nbr, var, val, show, color, stop): set_messages() - logs.add_line(nbr, messages[nbr], var, val) - - args.get_options() - logit = show or args.erron and color > 0 or args.dlogs - - if args.erron: - if color == 0: - logit = False - - if not logit: - return nbr + # File log printing + # ----------------- + #logit(nbr, var, val) + + # Front User Console + # ------------------ # COlors CS = '\033[0;0m' # Unset CL = '\033[0;2m' # Gray @@ -144,18 +119,23 @@ def out(nbr, var, val, show, color, stop): SC = CL # Default gray if color == 0: SC = CG elif color == 1: SC = CY - elif color == 2: SC = CR - - # Print, acoording to parameters - print("%s*%s %s%s%s > %s%s%s < %s%s%s"%( - SC, CS, - CL, messages[nbr], CS, - CB, var, CS, - CC, val, CS - ) - ) + elif color == 2: SC = CR + + if not show: + if args.commands["options"]["-v"]: show = True + force_show = bool(color > 0 and args.commands["options"]["-E"]) + if show or force_show: + # Print, acoording to parameters + print("%s*%s %s%s%s > %s%s%s < %s%s%s"%( + SC, CS, + CL, messages[nbr], CS, + CB, var, CS, + CC, val, CS + ) + ) + # Exit if stop = True if stop: if nbr >= 200: nbr = 0 @@ -163,3 +143,11 @@ def out(nbr, var, val, show, color, stop): return nbr + +#============# +# Files logs #================================================================ +#------------# +def logit(nbr, var, val): + try: domain.name + except: domain.name = "" + print(": debug. domain name", domain.name) diff --git a/src/var/lib/tyto/program/domain.py b/src/var/lib/tyto/program/domain.py index 4fe74f7..da1b7cb 100644 --- a/src/var/lib/tyto/program/domain.py +++ b/src/var/lib/tyto/program/domain.py @@ -14,704 +14,586 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Manage configuration domain(s) +# Description: Manage domain (config files). # File: /var/lib/tyto/program/domain.py #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - import os, sys, configparser -import debug, tyto, tools, forms, langs +import debug, tools, form, langs -#=========================================================# -# Exit if directory name is compatible with a domain name # -#---------------------------------------------------------# -def compatible_name(): - if len(name.rsplit(".")) <= 1: - debug.out(3, "abc.tld", name, True, 2, True) +# +# Manage domain +# Mainly start and stop action +# +def manage(): + import args + is_ready() -#================================# -# Load Domain Configuration file # -# As needed, exit if not exists # -#--------------------------------# -def cf_load(): - global cf + if args.commands["actions"]["start"]: + if not conf["activated"]: + cf.set("DOMAIN", "activated", "True") + with open(cf_uri, "w") as f: + cf.write(f) + + debug.out(250, "%s [%s]"%( + conf["title"], "True" + ), cf_uri, True, 0, True) - cf_exists() or sys.exit(100) - - cf = False - cf = configparser.ConfigParser() - cf.read(cf_uri) - debug.out(200, langs.logs.domain, cf_uri, False, 0, False) + elif args.commands["actions"]["stop"]: + if conf["activated"]: + cf.set("DOMAIN", "activated", "False") + with open(cf_uri, "w") as f: + cf.write(f) + + debug.out(250, "%s [%s]"%( + conf["title"], "False" + ), cf_uri, True, 0, True) -#=====================================# -# Load User Domain Configuration file # -# As needed, exit if not exists # -#-------------------------------------# -def ult_cf_load(): - global ult_cf - - ult_cf = False - if not os.path.exists(ult_cf_uri): - tools.create_file(ult_cf_uri, tyto.ini_domain_user) - - ult_cf = configparser.ConfigParser() - ult_cf.read(ult_cf_uri) +#=======================# +# Set current directory # +#-----------------------# +def check_home(): + global user_dir, home_dir, in_hole + + try: + in_hole = False + user_dir = os.getcwd() + "/" + home_dir = os.path.expanduser('~') + if ".local/share/Trash/" in user_dir: + in_hole = True + except: + user_dir = "" + in_hole = True -#===================================# -# Load User local Domains List File # -#-----------------------------------# -def ult_dlf_load(): - global ult_dlf +#===================================================# +# Set domain name # +# check if name is valid (compatible with a domain) # +#---------------------------------------------------# +def set_name(): + global wrk_root, name, cf_uri, cf_id, ult_dir, ult_cf_uri, ult_domains_uri - # User Domains list file - ult_dlf = False - if not os.path.exists(ult_dlf_uri): - tools.create_file(ult_dlf_uri, tyto.ini_domains_list) - - ult_dlf = configparser.ConfigParser() - ult_dlf.read(ult_dlf_uri) + # Set current working root directory before articles/ folder (if exists) + wrk_root = user_dir.rsplit("articles/")[0] - -#===========================================# -# Check if Domain COnfiguration file exists # -# Show status message only once # -# return True or False -#-------------------------------------------# -def cf_exists(): - global shown_ok, shown_no + # Set current directory name + name = os.path.basename(os.path.dirname(wrk_root)) - if os.path.exists(cf_uri): - try: shown_ok - except: debug.out(202, name, cf_uri, False, 0, False) - shown_ok = True - return True - else: - try: shown_no - except: debug.out(104, "False", cf_uri, True, 1, False) - shown_no = True - compatible_name() - return False + # valid name is "a.b" format + if not len(name.rsplit(".")) > 1: + debug.out(3, name, user_dir, True, 2, True) + + # Set Configuration file uri, and ID + cf_uri = wrk_root + "tyto_domain.ini" + cf_id = tools.get_HID(cf_uri, False) + + # Set .loacl/Tyto/ configuration files uri + ult_dir = "%s/.local/Tyto/"%home_dir + ult_cf_uri = "%s%s.ini"%(ult_dir, cf_id) + ult_domains_uri = "%sdomains.ini"%ult_dir #=========================================# # Guess and return wip_url from name # #-----------------------------------------# def create_wip_url(): - www_url = "https://www-wip.%s/" + wip_url = "https://www-wip.%s/" len_cn = name.count(".") # Domain name Format: a.b if len_cn == 1: - return www_url%name + return wip_url%name # Domain name format: (at least) a.b.c len_cn = len(name.rsplit(".")[0]) + 1 tld = name[len_cn:] - return www_url%tld + return wip_url%tld -#==========================================# -# Ask User to create new domain # -# Create NEW #: -# - default Domain Configuration file # -# - default User domain configuration file # -# If not User domains list file, create it # -#------------------------------------------# -def cf_create(): - compatible_name() + +#============================# +# Set all needed directories # +#----------------------------# +def set_values(): + global conf + conf = { + "activated" : cf.getboolean("DOMAIN", "activated"), + "post_code" : cf.getboolean("WEBSITE", "article_code"), + "sitemaps" : cf.getboolean("WEBSITE_MODULES", "sitemaps"), + "title" : cf.get("DOMAIN", "title"), + "about" : cf.get("DOMAIN", "about"), + "date" : cf.get("DOMAIN", "date"), + "tags" : cf.get("DOMAIN", "tags"), + "mail" : cf.get("DOMAIN", "mail"), + } - # This fonction is only called with "new domain" argument - # If a conf already exists, show important RESET log - if cf_exists(): - debug.out(102, "!?", cf_uri, True, 1, False) - - # Ask User to create new domain. Will exit if not ok. - forms.ask_domain_shortname(name) + global wrk_dirs + wrk_dirs = { + "articles" : wrk_root + "articles/", + "images" : wrk_root + "articles/images/", + "files" : wrk_root + "articles/files/", + "template" : wrk_root + "template/", + "modules" : wrk_root + "modules/", + "db" : wrk_root + ".db/", + } + + global srv_root, srv_domain + srv_root = cf.get("SERVER", "srv_root") + srv_domain = "%s%s/"%(srv_root, name) + + global wip, www, wip_dirs, www_dirs + wip = "%swip/"%srv_domain + www = "%swww/"%srv_domain + wip_dirs = { + "images" : "%simages/"%wip, + "files" : "%sfiles/"%wip, + "template" : "%stemplate/"%wip, + } + www_dirs = { + "images" : "%simages/"%www, + "files" : "%sfiles/"%www, + "template" : "%stemplate/"%www, + } + + global srv_wip_files + srv_wip_files = \ + { + "styles" : wip_dirs["template"] + cf.get("TEMPLATE_FILENAMES", "styles"), + } + + global web + web = { + "wip" : cf.get("WEBSITE", "wip_url"), + "www" : cf.get("WEBSITE", "www_url"), + "css" : cf.get("WEBSITE", "css"), + "lang" : cf.get("WEBSITE", "lang"), + "license" : cf.get("WEBSITE", "license"), + "license_url" : cf.get("WEBSITE", "license_url"), + } + + global lang + lang = cf.get("WEBSITE", "lang") + + create_srv_dirs("wrk") + + debug.out(202, name, cf_uri, False, 0, False) + + +#=======================================================# +# Create domain directorie for wrk or srv (wip or wwws) # +#-------------------------------------------------------# +def create_srv_dirs(process): + dirs = { + "wrk" : wrk_dirs, + "wip" : wip_dirs, + "www" : www_dirs, + } - # Create default files - tools.create_file(cf_uri, ini_template%name) + for d in dirs[process]: + tools.create_dirs(dirs[process][d]) + + +#===========================# +# Main domain check # +# Used for most processes # +# when domain must be ready # +# Exit if something wrong # +#---------------------------# +def is_ready(): + # Exit 3 if domain name not at leat "a.b" format + set_name() + cf_exists() or sys.exit(100) + cf_load() + cf_legacy() # Exit in error case + cf_update() + set_values() + + +#====================================# +# Domain Configuration file sections # +#------------------------------------#----------------------------------------- +#============================================================# +# Create NZW ID.ini config file in user local Tyto directory # +#------------------------------------------------------------# +def ult_cf_create(): tools.create_dirs(ult_dir) - tools.create_file(ult_cf_uri, tyto.ini_domain_user) + if os.path.exists(ult_cf_uri): + os.remove(ult_cf_uri) + with open(ult_cf_uri, "w") as f: + f.write(ini_ult_tpl) + else: + tools.create_file(ult_cf_uri, ini_ult_tpl) - # User Domains list file - if not os.path.exists(ult_dlf_uri): - tools.create_file(ult_dlf_uri, tyto.ini_domains_list) + ult_cf_load() + + # Also create domains lists in domains.ini + if not os.path.exists(ult_domains_uri): + tools.create_file(ult_domains_uri, ini_domains) + + ult_domains_load() + + +# +# Domain configuration file in .loacl/Tyto/ +# +def ult_cf_load(): + global ult_cf + + ult_cf = False + ult_cf = configparser.ConfigParser() + ult_cf.read(ult_cf_uri) + + +#====================================# +# Load list of all registred domains # +#------------------------------------# +def ult_domains_load(): + global ult_domains + + ult_domains = False + ult_domains = configparser.ConfigParser() + ult_domains.read(ult_domains_uri) + + +#==========================# +# Check configuration file # +#--------------------------# +def cf_exists(): + global found + + found = bool(os.path.exists(cf_uri)) + + # Show info + found or debug.out(100, name, cf_uri, True, 1, False) + + return found + + +#================================# +# Create default ini config file # +#--------------------------------# +def cf_create(): + if not found: + tools.create_file(cf_uri, ini_cf_tpl) + cf_exists() or debug.out(100, name, cf_uri, True, 2, True) - # Ask user for domain settings cf_load() - forms.ask_domain_title(True) - forms.ask_domain_date(True) - forms.ask_domain_about(True) - forms.ask_domain_mail(True) - forms.ask_domain_tags(True) - - # Set default lang, from config file or system lang - forms.ask_domain_lang(True) - - # Set server directory - forms.ask_domain_server(True) - - # Update Domain Configuration file - cf_update_values(True) - -#===================================================# -# Generic set section and values for ini file # -# File must exists (checked before) # -# Create section, set key if not exists # -# Ask yser when default is a key form and not value # -#---------------------------------------------------# -def cf_set(section, key, default): - global new_val - try: new_val - except: new_val = False +#=============================# +# Load ini configuration file # +#-----------------------------# +def cf_load(): + global cf + cf = False + cf = configparser.ConfigParser() + cf.read(cf_uri) + + +#=============================================# +# Check if current directory is domain legacy # +#---------------------------------------------# +def cf_legacy(): + cf_legacy_id() + cf_ult_file() + cf_legacy_domains() + + +#====================================================================# +# Config file was loaded yet # +# Check domain hash ID from current directory and domain config file # +#--------------------------------------------------------------------# +def cf_legacy_id(): try: - val = cf.get(section, key) + domain_id = cf.get("TYTO", "id") + domain_dir = cf.get("DOMAIN", "work") except: - try: cf.add_section(section) ; new_val = True - except: pass - val = "" + domain_id =domain_dir = "" + + legacy_id = tools.same_values(domain_id, cf_id) + legacy_id or debug.out(9, wrk_root, domain_dir, True, 2, True) + + +#=============================# +# Check if ult_cf file exists # +#-----------------------------# +def cf_ult_file(): + ult_config = bool(os.path.exists(ult_cf_uri)) + if ult_config: + try: + ult_cf_load() + domain_dir = ult_cf.get("DOMAIN", "directory") + except: + domain_dir = "" - # Values has a form - if default in tyto.keys_4q: - if val: return val - else: default = tyto.keys_questions[default](False) ; new_val = True - - # Optional key - elif not default: - if not val: - cf.set(section, key, default) - return default - else: - default = val - - # Force set default value - if val != default: - new_val = True - - cf.set(section, key, default) - debug.out(204, "[%s] %s"%(section, key), default, False, 0, False) - - return default + legacy_dir = tools.same_values(domain_dir, wrk_root) + legacy_dir or debug.out(9, wrk_root, domain_dir, True, 2, True) + else: + debug.out(100, "!?", ult_cf_uri, True, 2, True) -#===========================================# -# Set or Update domain configuration values # -# Ask for some values if empty # -# Ensure to set correct values # -#-------------------------------------------# -def cf_update_values(write): - # Load Domain Configuration file - cf_load() - debug.out(214, "...", cf_uri, False, 0, False) +#====================================================# +# Check current wrk_root dir with dir in domains.ini # +#----------------------------------------------------# +def cf_legacy_domains(): + try: + ult_domains_load() + domain_dir = ult_domains.get("DOMAINS", name) + except: + domain_dir = "" + legacy_dir = tools.same_values(domain_dir, wrk_root) + legacy_dir or debug.out(9, wrk_root, domain_dir, True, 2, True) + + +#============================================# +# from cf_load() # +# update all values and set some global ones # +#--------------------------------------------# +def cf_update(): + global cf_write + + # Check if domain configuration file has changed + # and update configuration file if so. + # (Based on last content ID, saved in ult domain config file) + try: + ult_cf_load() + if not args.action == "check": + if tools.same_values( + ult_cf.get("DOMAIN", "id"), + tools.get_HID(cf_uri, True) + ): + return + except: + debug.out(203, name, cf_uri, False, 0, False) + + try: cf_write + except: cf_write = False + # [DOMAIN] - # ======== - global activated, title, date, about, mail, tags, license, license_url - try: activated = cf.getboolean("DOMAIN", "activated") - except: activated = cf_set("DOMAIN", "activated", "no") - - cf_set("DOMAIN", "name", name) - - title = cf_set("DOMAIN", "title", "title") - date = cf_set("DOMAIN", "date", "date") - about = cf_set("DOMAIN", "about", "about") - mail = cf_set("DOMAIN", "mail", "mail") - tags = cf_set("DOMAIN", "tags", "tags") - license = cf_set("DOMAIN", "lincese", "") - if not license: - license = cf_set("DOMAIN", "license", "gfdl-1.3") - license_url = cf_set("DOMAIN", "license_url", - "https://www.gnu.org/licenses/fdl-1.3.txt") - elif not license == "gfdl-1.3" and \ - license_url == "https://www.gnu.org/licenses/fdl-1.3.txt": - license_url = cf_set("DOMAIN", "license_url", "") - - # [WEBSITE_FOOTER] - global legals_url, terms_url, bylaws_url - legals_url = cf_set("WEBSITE_FOOTER", "legals_url", "") - terms_url = cf_set("WEBSITE_FOOTER", "terms_url", "") - bylaws_url = cf_set("WEBSITE_FOOTER", "bylaws_url", "") - - - # [SERVER] - # ======== - global srv, srv_name, wip, www - srv = cf_set("SERVER", "root", "server") - if not tools.dir_exists(srv, False): - srv = cf_set("SERVER", "root", "server") - - srv_name = os.path.join(srv, name + "/") - wip = os.path.join(srv_name, "wip/") - www = os.path.join(srv_name, "www/") - - cf_set("SERVER", "domain", srv_name) - cf_set("SERVER", "wip", wip) - cf_set("SERVER", "www", www) - - - # [WIP_DIRS] - # ========== - global wip_tpl, wip_images, wip_files - wip_tpl = os.path.join(wip, "template/") - wip_images = os.path.join(wip, "images/") - wip_files = os.path.join(wip, "files/") - - cf_set("WIP_DIRS", "template", wip_tpl) - cf_set("WIP_DIRS", "images", wip_images) - cf_set("WIP_DIRS", "files", wip_files) - - - # [WWW_DIRS] - # ========== - global www_tpl, www_images, www_files - www_tpl = os.path.join(www, "template/") - www_images = os.path.join(www, "images/") - www_files = os.path.join(www, "files/") - - cf_set("WWW_DIRS", "template", www_tpl) - cf_set("WWW_DIRS", "images", www_images) - cf_set("WWW_DIRS", "files", www_files) - + cf_set("DOMAIN", "name", name, False) + cf_set("DOMAIN", "work", wrk_root, False) + cf_set("DOMAIN", "activated", False, True) + title = \ + cf_set("DOMAIN", "title", "", False) + cf_set("DOMAIN", "date", "", False) + cf_set("DOMAIN", "about", "", False) + cf_set("DOMAIN", "tags", "", False) + cf_set("DOMAIN", "mail", "", False) # [WEBSITE] - # ========= - global wip_url, www_url, lang, css, sep, article_code, static - wip_url = cf_set("WEBSITE", "wip_url", "") or \ - cf_set("WEBSITE", "wip_url", create_wip_url()) - - www_url = cf_set("WEBSITE", "www_url", "") or \ - cf_set("WEBSITE", "www_url", "https://%s/"%name) - - lang = cf_set("WEBSITE", "lang", "") - if not lang: - lang = cf_set("WEBSITE", "lang", langs.load_website_lang()) - elif not langs.translation_exists("website", lang, False): - lang = langs.get_sys_lang() - langs.load_website_lang() - - css = cf_set("WEBSITE", "css", "") or \ - cf_set("WEBSITE", "css", "tyto") - - sep = cf_set("WEBSITE", "separator", "") - if not sep or len(sep) > 2: - sep = cf_set("WEBSITE", "separator", "|") - - try: article_code = cf.getboolean("WEBSITE", "article_code") - except: article_code = cf_set("WEBSITE", "article_code", "yes") - try: static = cf.getboolean("WEBSITE", "static") - except: static = cf_set("WEBSITE", "static", "no") - - + cf_set("WEBSITE", "wip_url", create_wip_url(), False) + cf_set("WEBSITE", "www_url", "https://%s/"%name, False) + cf_set("WEBSITE", "css", "tyto", False) + cf_set("WEBSITE", "separator", "-", False) + cf_set("WEBSITE", "article_code", False, True) + license = \ + cf_set("WEBSITE", "license", "gfdl-1.3", False) + license_url = \ + cf_set("WEBSITE", "license_url", + "https://www.gnu.org/licenses/fdl-1.3.txt", False) + global lang + lang = \ + cf_set("WEBSITE", "lang", "", False) + # [WEBSITE_MODULES] - # ================= - global navbar, sidebar_title, sidebar_items, rss_items, sitemaps - try: navbar = cf.getboolean("WEBSITE_MODULES", "navbar") - except: navbar = cf_set("WEBSITE_MODULES", "navbar", "yes") - - sidebar_title = cf_set("WEBSITE_MODULES", "sidebar_title", "") or \ - cf_set("WEBSITE_MODULES", "sidebar_title", langs.site.sidebar_title) - - sidebar_items = cf_set("WEBSITE_MODULES", "sidebar_items", "") - if not sidebar_items or not sidebar_items.isdigit(): - sidebar_items = cf_set("WEBSITE_MODULES", "sidebar_items", "0") - - rss_items = cf_set("WEBSITE_MODULES", "rss_items", "") - if not rss_items or not rss_items.isdigit(): - rss_items = cf_set("WEBSITE_MODULES", "rss_items", "0") - - try: sitemaps = cf.getboolean("WEBSITE_MODULES", "sitemaps") - except: sitemaps = cf_set("WEBSITE_MODULES", "sitemaps", "yes") - - - # TEMPLATE_FILENAMES - # ================== - global favicon, logo, styles, rss, stats - favicon = cf_set("TEMPLATE_FILENAMES", "favicon", "") or \ - cf_set("TEMPLATE_FILENAMES", "favicon", "favicon.png") - - logo = cf_set("TEMPLATE_FILENAMES", "logo", "") or \ - cf_set("TEMPLATE_FILENAMES", "logo", "logo.png") - - styles = cf_set("TEMPLATE_FILENAMES", "styles", "") or \ - cf_set("TEMPLATE_FILENAMES", "styles", "styles.css") - - rss = cf_set("TEMPLATE_FILENAMES", "rss", "") or \ - cf_set("TEMPLATE_FILENAMES", "rss", "rss.xml") - - stats = cf_set("TEMPLATE_FILENAMES", "stats", "") or \ - cf_set("TEMPLATE_FILENAMES", "stats", "tyto_stats.ini") - - - # [USER_DIRS] - # =========== - cf_set("USER_DIRS", "root", wrk_dir) - cf_set("USER_DIRS", "articles", wrk_articles) - cf_set("USER_DIRS", "images", wrk_images) - cf_set("USER_DIRS", "files", wrk_files) - cf_set("USER_DIRS", "template", wrk_tpl) - cf_set("USER_DIRS", "modules", wrk_mods) - cf_set("USER_DIRS", "database", wrk_db) - - - # [USER_TEMPLATE_FILES] - # ===================== - global wrk_favicon, wrk_logo, wri_styles - wrk_favicon = os.path.join(wrk_tpl, favicon) - wrk_logo = os.path.join(wrk_tpl, logo) - wrk_styles = os.path.join(wrk_tpl, styles) - - cf_set("USER_TEMPLATE_FILES", "favicon", wrk_favicon) - cf_set("USER_TEMPLATE_FILES", "logo", wrk_logo) - cf_set("USER_TEMPLATE_FILES", "styles", wrk_styles) - - - # [USER_MODULES_FILES] - # ==================== - global wrk_metas, wrk_header, wrk_navbar, wrk_sidebar, wrk_footer - wrk_metas = os.path.join(wrk_mods, "tyto_metas.raw") - wrk_header = os.path.join(wrk_mods, "tyto_header.raw") - wrk_navbar = os.path.join(wrk_mods, "tyto_navbar.raw") - wrk_sidebar = os.path.join(wrk_mods, "tyto_sidebar.raw") - wrk_footer = os.path.join(wrk_mods, "tyto_footer.raw") - - cf_set("USER_MODULES_FILES", "metas", wrk_metas) - cf_set("USER_MODULES_FILES", "header", wrk_header) - cf_set("USER_MODULES_FILES", "navbar", wrk_navbar) - cf_set("USER_MODULES_FILES", "sidebar", wrk_sidebar) - cf_set("USER_MODULES_FILES", "footer", wrk_footer) - - - # [WIP_FILES] - # =========== - global wip_favicon, wip_logo, wip_styles, wip_rss, wip_stats - wip_favicon = os.path.join(wip_tpl, favicon) - wip_logo = os.path.join(wip_tpl, logo) - wip_styles = os.path.join(wip_tpl, styles) - wip_rss = os.path.join(wip_tpl, rss) - wip_stats = os.path.join(wip_tpl, stats) - - cf_set("WIP_FILES", "favicon", wip_favicon) - cf_set("WIP_FILES", "logo", wip_logo) - cf_set("WIP_FILES", "styles", wip_styles) - cf_set("WIP_FILES", "rss", wip_rss) - cf_set("WIP_FILES", "stats", wip_stats) - - global wip_metas, wip_header, wip_navbar, wip_sidebar, wip_footer - wip_metas = os.path.join(wip_tpl, "metas.html") - wip_header = os.path.join(wip_tpl, "header.html") - wip_navbar = os.path.join(wip_tpl, "navbar.html") - wip_sidebar = os.path.join(wip_tpl, "sidebar.html") - wip_footer = os.path.join(wip_tpl, "footer.html") - - cf_set("WIP_FILES", "metas", wip_metas) - cf_set("WIP_FILES", "header", wip_header) - cf_set("WIP_FILES", "navbar", wip_navbar) - cf_set("WIP_FILES", "sidebar", wip_sidebar) - cf_set("WIP_FILES", "footer", wip_footer) - - - # [WWW_FILES] - # =========== - global www_favicon, www_logo, www_styles, www_rss, www_stats - www_favicon = os.path.join(www_tpl, favicon) - www_logo = os.path.join(www_tpl, logo) - www_styles = os.path.join(www_tpl, styles) - www_rss = os.path.join(www_tpl, rss) - www_stats = os.path.join(www_tpl, stats) - - cf_set("WWW_FILES", "favicon", www_favicon) - cf_set("WWW_FILES", "logo", www_logo) - cf_set("WWW_FILES", "styles", www_styles) - cf_set("WWW_FILES", "rss", www_rss) - cf_set("WWW_FILES", "stats", www_stats) - - global www_metas, www_header, www_navbar, www_sidebar, www_footer - www_metas = os.path.join(www_tpl, "metas.html") - www_header = os.path.join(www_tpl, "header.html") - www_navbar = os.path.join(www_tpl, "navbar.html") - www_sidebar = os.path.join(www_tpl, "sidebar.html") - www_footer = os.path.join(www_tpl, "footer.html") - - cf_set("WWW_FILES", "metas", www_metas) - cf_set("WWW_FILES", "header", www_header) - cf_set("WWW_FILES", "navbar", www_navbar) - cf_set("WWW_FILES", "sidebar", www_sidebar) - cf_set("WWW_FILES", "footer", www_footer) - + langs.load_site() + cf_set("WEBSITE_MODULES", "sidebar_title", langs.site.sidebar_title, False) + cf_set("WEBSITE_MODULES", "sidebar_items", "6", False) + cf_set("WEBSITE_MODULES", "rss_items", "100", False) + cf_set("WEBSITE_MODULES", "sitemaps", True, True) + + # [WEBSITE_FOOTER] + # Footer links configuration. typle format: ("Show", "URL", "TITLE") + cf_set( + "WEBSITE_FOOTER", + "link_1", + '("%s%s %s", "%s", "%s %s")'%( + langs.site.license, langs.logs.pp, license, + license_url, + langs.site.license_title, title + ), False + ) + cf_set( + "WEBSITE_FOOTER", + "link_2", + '("%s", "/", "%s %s")'%( + langs.site.legals, langs.site.legals_title, title + ), False + ) + cf_set( + "WEBSITE_FOOTER", + "link_3", + '("%s", "/", "%s %s")'%( + langs.site.terms, langs.site.terms_title, title + ), False + ) + cf_set( + "WEBSITE_FOOTER", + "link_4", + '("%s", "/", "%s %s")'%( + langs.site.bylaws, langs.site.bylaws_title, title + ), False + ) + + # [SERVER] + global srv_root + cf_set("SERVER", "srv_root", "", False) + + # [TEMPLATE_FILENAMES] + cf_set("TEMPLATE_FILENAMES", "favicon", "favicon.png", False) + cf_set("TEMPLATE_FILENAMES", "logo", "logo.png", False) + cf_set("TEMPLATE_FILENAMES", "styles", "styles.css", False) + cf_set("TEMPLATE_FILENAMES", "rss", "rss.xml", False) + cf_set("TEMPLATE_FILENAMES", "stats", "tyto_stats.ini", False) # [TYTO] - # ====== - cf_set("TYTO", "domain_hash", cf_id) - cf_set("TYTO", "domain_conf", cf_uri) - cf_set("TYTO", "domain_user", ult_cf_uri) + cf_set("TYTO", "id", cf_id, False) + cf_set("TYTO", "conf_uri", cf_uri, False) + cf_set("TYTO", "user_uri", ult_cf_uri, False) - - # ================================= # - # Write Configuration file # - # Only if needed or when new domain # - # --------------------------------- # - if new_val or write: - with open(cf_uri, "w") as f: - cf.write(f) - - - #=============================================# - # Update User local domain configuration file # - #---------------------------------------------# - ult_write = False - ult_cf_load() - if ult_cf.get("DOMAIN", "name") != name: + # New config values was set: write new ini config files + if cf_write: + try: + with open(cf_uri, "w") as f: + cf.write(f) + debug.out(206, "tyto_domain.ini", cf_uri, False, 0, False) + except: + debug.out(5, langs.logs.error, cf_uri, True, 2, True) + + cf_write = False + cf_load() + + # With new ini file, update ult config file + ult_cf_create() ult_cf.set("DOMAIN", "name", name) - ult_write = True - - cf_hash_c = tools.get_filesum(cf_uri, True) - if ult_cf.get("DOMAIN", "hash") != cf_hash_c: - ult_cf.set("DOMAIN", "hash", cf_hash_c) - ult_write = True - - if ult_cf.get("DOMAIN", "root") != wrk_dir: - ult_cf.set("DOMAIN", "root", wrk_dir) - ult_write = True - - if ult_cf.get("DOMAIN", "conf") != cf_uri: - ult_cf.set("DOMAIN", "conf", cf_uri) - ult_write = True - - if ult_cf.get("SERVER", "root") != srv: - ult_cf.set("SERVER", "root", srv) - ult_write = True - - if ult_write: - with open(ult_cf_uri, "w") as f: - ult_cf.write(f) - - - # Update User local Domains List File - #------------------------------------ - ult_dlf_load() - dlf_write = False - try: - dlf_line = ult_dlf.get("DOMAINS", name) - if dlf_line != wrk_dir: - dlf_write = True - except: - dlf_write = True - - if dlf_write: - ult_dlf.set("DOMAINS", name, wrk_dir) - with open(ult_dlf_uri, "w") as f: - ult_dlf.write(f) + ult_cf.set("DOMAIN", "directory", wrk_root) + ult_cf.set("DOMAIN", "config", cf_uri) + ult_cf.set("DOMAIN", "id", tools.get_HID(cf_uri, True)) + try: + with open(ult_cf_uri, "w") as f: + ult_cf.write(f) + debug.out(206, "%s.ini"%cf_id, ult_cf_uri, False, 0, False) + ult_cf_load() + except: + debug.out(5, "!?", ult_cf_uri, True, 2, True) + + # Add domain to list if not exists yet + try: + domains_updated = \ + tools.same_values(ult_domains.get("DOMAINS", name), wrk_root) + except: + domains_updated = False + ult_domains.set("DOMAINS", name, wrk_root) + + if not domains_updated: + try: + with open(ult_domains_uri, "w") as f: + ult_domains.write(f) + debug.out(206, "domains.ini", ult_domains_uri, False, 0, False) + ult_domains_load() + except: + debug.out(5, "!?", ult_domains_uri, True, 2, True) -#========================================# -# When user wants to (de)activate domain # -# Update key value and ensure conf is ok # # -# if activated, check/create wrk dirs # -#----------------------------------------# -def userset_status(action): - do = { - "start" : "yes", - "stop" : "no" - } - - tools.update_ini_file(cf_uri, "DOMAIN", "activated", do[action]) - cf_update_values(False) - ready() - - if action == "start": - status = cf.get("DOMAIN", "activated") - debug.out(209, "[DOMAIN] activated = %s"%status, cf_uri, True, 0, False) - - -#========================================# -# (After configuration file was checked) # -# Exit if domain not activated # -# # -# or check/create wrk directories # -#----------------------------------------# -def ready(): - if not activated: - status = cf.get("DOMAIN", "activated") - debug.out(105, "[DOMAIN] activated = %s"%status, cf_uri, True, 1, True) - - for key, directory in cf.items("USER_DIRS"): - tools.create_dirs(directory) - - -#======#======================================================================= -# MAIN # -#------# -#===================================# -# Check if current directory exists # -# Exit Tyto if in black hole... # -#-----------------------------------# -try: - user_dir = os.getcwd() + "/" - home_dir = os.path.expanduser('~') -except: - debug.out(2, "PWD", "?", True, 2, True) - - -#======# -# main # -#======# -#==========================================# -# utl: $USER/.local/Tyto # -# cf: Domain Configuration File # -#------------------------------------------# -# Domain Configuration directory -wrk_dir = user_dir.rsplit("articles/")[0] -wrk_articles = os.path.join(wrk_dir, "articles/") -wrk_images = os.path.join(wrk_dir, "articles/images/") -wrk_files = os.path.join(wrk_dir, "articles/files/") -wrk_tpl = os.path.join(wrk_dir, "template/") -wrk_mods = os.path.join(wrk_dir, "modules/") -wrk_db = os.path.join(wrk_dir, ".db/") - -# Domain name from current basename directory -# Exit if not format at least "abc.tld" -name = os.path.basename(os.path.dirname(wrk_dir)) - -cf_name = "tyto_domain.ini" -cf_uri = os.path.join(wrk_dir, cf_name) -cf_id = tools.get_filesum(cf_uri, False) # ID from URI - -# Tyto directory in home local user files -ult_dir = os.path.join(home_dir, ".local/Tyto/") -ult_dlf_uri = os.path.join(ult_dir, "domains.ini") # Domains list file -ult_cf_uri = os.path.join(ult_dir, cf_id + ".ini") - - -#===========================# -# Templates #================================================== -#===========================# #=============================# -# Domain configuration file # -# Create file with new domain # +# Set item in config ini file # #-----------------------------# -ini_template = """[DOMAIN] -activated = no -name = %s -title = -date = -about = -tags = -mail = -license = gfdl-1.3 -license_url = https://www.gnu.org/licenses/fdl-1.3.txt +def cf_set(section, key, val, boolean): + global cf_write + + # if a new val has to be written, do not deactivate + try: cf_write + except: cf_write = False + + # Get current val set + # if section is not set, create it + try: + if boolean: cur_val = cf.getboolean(section, key) + else: cur_val = cf.get(section, key) + except: + #no_val = True + try: cf.add_section(section) + except: pass + cur_val = val + cf_write = True + + # User set with forms + forms_names = ("title", "about", "date", "tags", "mail", "lang") + forms_dirs = ("srv_root") + if key in forms_names and not cur_val or \ + key in forms_dirs and not os.path.exists(cur_val): + cur_val = user_set(key, cur_val) + + if cf_write: + cf.set(section, key, str(cur_val)) + + return str(cur_val) + + +#==========================================# +# Start form from key (if not already set) # +#------------------------------------------# +def user_set(key, default): + global cf_write, forms + + try: cf_write + except: cf_write = False + try: forms + except: + forms = { + "title" : form.ask_domain_title, + "date" : form.ask_domain_date, + "about" : form.ask_domain_about, + "tags" : form.ask_domain_tags, + "mail" : form.ask_domain_mail, + "lang" : form.ask_website_lang, + "srv_root" : form.ask_srv_root, + } + + for item in forms: + if item == key: + while True: + cur_val = forms[key](default) + if cur_val: + cf_write = True + return cur_val + + + +#======# +# Main #======================================================================= +#======# + +# configuration file (.ini) template (tyto_domain.ini) +ini_cf_tpl = """[DOMAIN] [WEBSITE] -www_url = -wip_url = -lang = -css = tyto -separator = | -article_code = yes -static = no -navbar = yes [WEBSITE_FOOTER] -legals_url = -terms_url = -bylaws_url = [WEBSITE_MODULES] -navbar = yes -sidebar_title = -sidebar_items = 6 -rss_items = 100 -sitemaps = yes [TEMPLATE_FILENAMES] -favicon = favicon.png -logo = logo.png -styles = styles.css -rss = rss.xml -stats = tyto_stats.ini - -[USER_DIRS] -root = -articles = -images = -files = -modules = -database = -template = - -[USER_TEMPLATE_FILES] -logo = -favicon = -styles = - -[USER_MODULES_FILES] -metas = -header = -navbar = -sidebar = -footer = [SERVER] -root = /var/www/ -domain = -wip = -www = - -[WIP_DIRS] -images = -files = -template = - -[WIP_FILES] -favicon = -logo = -styles = -rss = -stats = -metas = -header = -navbar = -sidebar = -footer = - -[WWW_DIRS] -images = -files = -template = - -[WWW_FILES] -favicon = -logo = -styles = -rss = -stats = -metas = -header = -navbar = -sidebar = -footer = [TYTO] -domain_hash = -domain_conf = -domain_user = +""" + +# Configuration file (.ini) template (.loacl/Tyto/id.ini) +ini_ult_tpl = """[DOMAIN] +""" + +# Configuration file (.ini) template (.loacl/Tyto/domains.ini) +ini_domains = """[DOMAINS] """ diff --git a/src/var/lib/tyto/program/feed.py b/src/var/lib/tyto/program/feed.py new file mode 100644 index 0000000..e015ee3 --- /dev/null +++ b/src/var/lib/tyto/program/feed.py @@ -0,0 +1,191 @@ +#!/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 General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see .. + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Create RSS/feed (when publish only) +# File: /var/lib/tyto/program/feed.py +#---------------------------------------------------------------------- + +import os, configparser, datetime, glob +import domain, args, debug, tools, langs + + +#=========================================# +# Create RSS/Feed file in www server only # +# Only published articles are included # +# Feed can be deactivated in domain conf # +#-----------------------------------------# +def create(): + max_item = int(domain.cf.get("WEBSITE_MODULES", "rss_items")) + if max_item == 0: + return + + db_dir = domain.wrk_dirs["db"] + + # Scan DB directory and sort files by last updated + items = filter(os.path.isfile, glob.glob(db_dir + '*.ini')) + items = sorted(items, key = os.path.getmtime, reverse=True) + + nbr_item = 0 + feed_items = "" + bld_date = tools.nowdate("feed") # General Build date + + for db_uri in items: + if nbr_item == max_item: + print("MAX") + break + + if not check_db(db_uri): + continue + + nbr_item += 1 + pub_date = datetime.datetime.strptime(DB["date"], '%Y-%m-%d').date() + pub_date = pub_date.strftime("%a, %d %b %Y") + + feed_item = \ + feed_item_tpl%( + nbr_item, max_item, + DB["title"], + DB["www_url"], + DB["www_url"], + pub_date, + DB["about"], + DB["authors"], + DB["logo"], + "%s (logo)"%DB["title"], + DB["www_url"], + DB["tags"] + ) + + + if not feed_items: feed_items = feed_item + else: feed_items = "%s\n%s"%(feed_items, feed_item) + + feed = \ + feed_tpl%( + bld_date, + domain.web["www"] + domain.cf.get("TEMPLATE_FILENAMES", "rss"), + domain.conf["title"], + domain.web["www"], + domain.conf["about"], + domain.web["www"] + "template/" + domain.cf.get("TEMPLATE_FILENAMES", "logo"), + "%s (logo)"%(domain.conf["title"]), + domain.web["www"], + domain.lang, + domain.conf["tags"], + bld_date, + domain.cf.get("WEBSITE", "license"), + domain.conf["mail"], + feed_items + ) + + feed_uri = domain.www + domain.cf.get("TEMPLATE_FILENAMES", "rss") + tools.create_file(feed_uri, feed) + + debug.out(250, "RSS/Feed", feed_uri, True, 0, False) + + +#===============================# +# Check/Set article DB # +# Check if article can be added # +#-------------------------------# +def check_db(db_uri): + global DB + + cf = configparser.ConfigParser() + cf.read(db_uri) + + try: + DB = { + "uri_web" : cf.get("URIS", "web"), + "title" : cf.get("ARTICLE", "title"), + "about" : cf.get("ARTICLE", "about"), + "tags" : cf.get("ARTICLE", "tags"), + "date" : cf.get("ARTICLE", "date"), + "authors" : cf.get("ARTICLE", "authors"), + "logo" : cf.get("ARTICLE", "logo"), + "www_url" : cf.get("ARTICLE", "www_url"), + "hash_www" : cf.get("HASHES", "www"), + "norss" : cf.getboolean("MODULES", "norss"), + } + except: + return False + + if not DB["hash_www"] or DB["norss"]: + return False + + return True + + +#======# +# MAIN #======================================================================= +#======# +#=======================# +# RSS/Feed xml template # +#-----------------------# +feed_tpl = """ + + + + + + + + + + + + %s + %s + %s + ' + %s + %s + %s + + %s + %s + %s + %s + %s + Tyto - Littérateur + + +%s + + + +""" + +feed_item_tpl = """ + + %s + %s + %s + %s + %s + %s + + %s + %s + %s + + %s + """ diff --git a/src/var/lib/tyto/program/form.py b/src/var/lib/tyto/program/form.py new file mode 100644 index 0000000..6ef9345 --- /dev/null +++ b/src/var/lib/tyto/program/form.py @@ -0,0 +1,179 @@ +#!/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 General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see .. + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Manage forms. When a question is asked. +# File: /var/lib/tyto/program/forms.py +#---------------------------------------------------------------------- + +import os, sys +import langs, domain, debug, tools + + +#======================================================# +# Ask a question # +# in yes_mod, value is from local langs.logs.ok values # +# has a keyboad interrupt # +#------------------------------------------------------# +def ask(q, yes_mod, default): + answer = expected = "" + if yes_mod: + expected = langs.logs.ok + + try: + answer = input(q) + except KeyboardInterrupt: + print() + debug.out(99, q, expected, False, 2, True) + + if yes_mod: + for ok in expected: + if answer.lower() == ok.lower(): + return True + debug.out(199, q, expected, True, 1, True) + + if default and not answer: return default + elif not answer: return False + + return answer + + +#=========================# +# Confirm domain creation # +#-------------------------# +def ask_domain_creation(): + q = '- %s "%s"%s '%( + langs.logs.ask_domain_name, domain.name, langs.logs.q) + + # Create domain configuration file + if ask(q, True, ""): + domain.cf_create() + + +#=================================================# +# Forms # +# Each questions are asked from domain.user_set() # +# In mismatch case, question is asked again # +#-------------------------------------------------# +#======================# +# Ask for domain title # +#----------------------# +def ask_domain_title(default): + q = '- %s "%s"%s '%( + langs.logs.ask_domain_title, default, langs.logs.q) + + return ask(q, False, default) or "" + + +#==============================# +# Ask for domain creation date # +#------------------------------# +def ask_domain_date(default): + default = tools.nowdate("int-short").rsplit("-")[0] # Set year only + debug.out(101, "[YYYY[-MM][-DD]]", "", True, 0, False) + q = '- %s "%s"%s '%( + langs.logs.ask_domain_date, default, langs.logs.q) + + return ask(q, False, default) or "" + + +#============================# +# Ask for domain description # +#----------------------------# +def ask_domain_about(default): + q = '- %s "%s"%s '%( + langs.logs.ask_domain_about, default, langs.logs.q) + + return ask(q, False, default) or "" + + +#=====================# +# Ask for domain tags # +#---------------------# +def ask_domain_tags(default): + default = domain.cf.get("DOMAIN", "title") + "," + q = '- %s "%s"%s '%( + langs.logs.ask_domain_tags, default, langs.logs.q) + + return ask(q, False, default) or "" + + +#============================# +# Ask for administrator mail # +#----------------------------# +def ask_domain_mail(default): + q = '- %s "%s"%s '%( + langs.logs.ask_domain_mail, default, langs.logs.q) + + return ask(q, False, default) or "" + + +#=============================# +# Ask for wzbsite language # +# Check if translation exists # +#-----------------------------# +def ask_website_lang(default): + default = langs.logs_lang + + q = '- %s "%s"%s '%( + langs.logs.ask_website_lang, default, langs.logs.q) + + lang = ask(q, False, default) + if not os.path.exists(langs.lang_files["site"]["uri"]%lang): + debug.out(8, lang, langs.lang_files["site"]["uri"]%lang, True, 2, False) + lang = "" + + return lang + + +#===============================# +# Ask for server root directory # +# Check if directory exists # +#-------------------------------# +def ask_srv_root(default): + srv_uris = (default, "/var/www/", "/srv/http/") + for srv_uri in srv_uris: + if os.path.exists(srv_uri): + default = srv_uri + break; + + q = '- %s "%s"%s '%( + langs.logs.ask_srv_root, default, langs.logs.q) + + srv_uri = ask(q, False, default) + if srv_uri: + if not srv_uri.endswith("/"): + srv_uri = srv_uri + "/" + if not os.path.exists(srv_uri): + debug.out(6, q, srv_uri, True, 2, False) + srv_uri = "" + + return srv_uri + + +# +# +# +def ask_reset_modules(): + q = "- %s%s "%(langs.logs.ask_reset_modules, langs.logs.q) + + if ask(q, True, ""): + return + diff --git a/src/var/lib/tyto/program/help.py b/src/var/lib/tyto/program/help.py index 5974fc9..6077eba 100644 --- a/src/var/lib/tyto/program/help.py +++ b/src/var/lib/tyto/program/help.py @@ -14,30 +14,64 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Show help commands +# Description: Show help, and manage some arguments with it # File: /var/lib/tyto/program/help.py #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- +import sys +import args, langs -import langs, debug +# +# +# +def manage(): + helps = { + "new" : langs.logs.help_new, + "check" : langs.logs.help_check, + "wip" : langs.logs.help_wip, + "modules" : langs.logs.help_modules, + "list" : langs.logs.help_list, + "cite" : langs.logs.help_cite, + "image" : langs.logs.help_image, + "link" : langs.logs.help_link, + "file" : langs.logs.help_file, + "code" : langs.logs.help_code, + "words" : langs.logs.help_words, + "abbr" : langs.logs.help_abbr, + "anchor" : langs.logs.help_anc, + "raw" : langs.logs.help_raw, + "article" : langs.logs.help_article, + } + norepeat = \ + ( + "list", "cite", "image", "link", "words", "code", "raw", "abbr", "anchor", + "file", + ) -#==========================================# -# Show help in system langage # -# Help Contents is in translations/logs_XX # -#------------------------------------------# -def show(action, target): - print(langs.logs.help_contents) + try: + sys.argv[2] + if not sys.argv[2] in helps: show_main = True + else: show_main = False + except: + show_main = True + + if show_main: + print(langs.logs.help_man) + + if args.commands["targets"]["all"]: + for h in helps: + if not h in norepeat: + print(helps[h]) + return + + for arg in range(2, len(sys.argv)): + if sys.argv[arg] in helps: + print(helps[sys.argv[arg]]) + + diff --git a/src/var/lib/tyto/program/langs.py b/src/var/lib/tyto/program/langs.py index 636b9b4..1f2a69f 100644 --- a/src/var/lib/tyto/program/langs.py +++ b/src/var/lib/tyto/program/langs.py @@ -14,127 +14,65 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see .. #---------------------------------------------------------------------- -# XMPP: echolib > im@echolib.re +# XMPP: echolib (im@echolib.re) # -# Description: Load lang logs file according to system language -# Load lang site file according to domain configuration +# Description: Set languages for logs and website # File: /var/lib/tyto/program/langs.py #---------------------------------------------------------------------- -#------------------------- -# Funny Stats Project -#------------------------- -# file lines : -# file comments : -# file functions: -# file program : -#-------------------------- - -import sys, locale, os -import args, debug, domain +import locale +import domain -trfs = "/var/lib/tyto/translations/" +# Settings files +def set_lang_files(): + global lang_files, files_set -#==================================# -# Check if translation file exists # -# module: # -# - "logs": logs_xx # -# - "website": website_xx # -# lang: 2 chars language # -# out: exit if True -# return True or False # -#----------------------------------# -def translation_exists(module, lang, out): - global tr_file - - modules = ("logs", "website") - # in case of internal typo error - if not module in modules: - print("! langs: internal error: 'logs', 'website'") - sys.exit(254) - - tr_file = "%s%s_%s.py"%(trfs, module, lang) - if not os.path.exists(tr_file): - debug.out(5, lang, tr_file, True, 2, False) - return False - - return True + lang_files = \ + { + "logs" : { + "name" : "logs_%s", + "uri" : "/var/lib/titi/translations/logs_%s.py", + }, + "site" : { + "name" : "site_%s", + "uri" : "/var/lib/titi/translations/site_%s.py", + }, + } + + files_set = True -#=============================================================================# -# LOGS | # -# Set and import file | # -#=============================================================================# -#=============================# -# Get system Lang to set logs # -#-----------------------------# -def get_sys_lang(): - global lang, tr_logs_uri - - tr_logs_uri = "%slogs_%s.py" - - try: lang = locale.getdefaultlocale()[0].rsplit("_")[0] - except: lang = "en" - - if not translation_exists("logs", lang, False): - lang = "en" - - tr_logs_uri = tr_logs_uri%(trfs, lang) - - return lang +# Use logs from system language or default "en" +def load_logs(): + global logs, logs_lang, logs_uri + set_lang_files() -#===============================# -# Import logs lang file in logs # -#-------------------------------# -def load_logs_lang(): - global logs, lang, set_logs - try: - set_logs + logs_lang = locale.getdefaultlocale()[0].rsplit("_")[0] except: - logs = __import__("logs_%s"%get_sys_lang()) - debug.out(201, lang, tr_logs_uri, False, 0, False) - set_logs = True + logs_lang = "en" + + # Load language file + logs_name = lang_files["logs"]["name"]%logs_lang + logs_uri = lang_files["logs"]["uri"]%logs_lang + logs = __import__(logs_name) -#=============================================================================# -# WEBSITE | # -# Get/Set and import file | # -#=============================================================================# -#=======================================# -# Get website lang from cf to set site # -#---------------------------------------# -def get_website_lang(): - global site_lang, tr_website_uri +# loan language website values +def load_site(): + global site - tr_website_uri = "%swebsite_%s.py" - try: site_lang = domain.cf.get("WEBSITE", "lang") - except: site_lang = get_sys_lang() + try: files_set + except: set_lang_files() - if not translation_exists("website", site_lang, False): - site_lang = get_sys_lang() # or default "en" - - tr_website_uri = tr_website_uri%(trfs, site_lang) - - return site_lang + try: site_lang = domain.lang + except: site_lang = "en" + try: site = __import__(lang_files["site"]["name"]%site_lang) + except: site = __import__(lang_files["site"]["name"]%"en") -#==================================# -# Import website lang file in site # -#----------------------------------# -def load_website_lang(): - global site, site_lang, set_site - - site = __import__("website_%s"%get_website_lang()) - - try: - set_site - except: - debug.out(208, site_lang, tr_website_uri, False, 0, False) - set_site = True - diff --git a/src/var/lib/tyto/program/modules.py b/src/var/lib/tyto/program/modules.py new file mode 100644 index 0000000..b689711 --- /dev/null +++ b/src/var/lib/tyto/program/modules.py @@ -0,0 +1,601 @@ +#!/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 General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see .. + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Manage modules creations (metas, to footer called by nginx) +# File: /var/lib/tyto/program/modules.py +#---------------------------------------------------------------------- + +import os, ast, configparser +import args, domain, langs, tools, debug, form + + +#=================================================# +# Manage modules creations # +# metas, header, navbar, sidebar, footer # +# - User must configure: navbar, sidebar # +# - Default contents: metas, header, footer # +# From [module].raw, create [module].html # +# All are called from file with nginx in web page # +#-------------------------------------------------# +def manage(module): + global files, wrk_dir, asked, mod_write, wip_dir + + # Set directories + wrk_dir = domain.wrk_dirs["modules"] + wip_dir = domain.wip_dirs["template"] + www_dir = domain.www_dirs["template"] + + debug.out(212, module, wrk_dir, False, 0, False) + + files = { + "metas" : { + "wrk" : wrk_dir + "metas.raw", + "wip" : wip_dir + "metas.html", + "www" : www_dir + "metas.html", + "set" : set_metas_raw, + }, + "header" : { + "wrk" : wrk_dir + "header.raw", + "wip" : wip_dir + "header.html", + "www" : www_dir + "header.html", + "set" : set_header_raw, + }, + "navbar" : { + "wrk" : wrk_dir + "navbar.raw", + "wip" : wip_dir + "navbar.html", + "www" : www_dir + "navbar.html", + "set" : set_navbar_raw, + }, + "sidebar" : { + "wrk" : wrk_dir + "sidebar.raw", + "wip" : wip_dir + "sidebar.html", + "www" : www_dir + "sidebar.html", + "set" : set_sidebar_raw, + }, + "footer" : { + "wrk" : wrk_dir + "footer.raw", + "wip" : wip_dir + "footer.html", + "www" : www_dir + "footer.html", + "set" : set_footer_raw, + }, + } + + # Load/Create new modules in DB file + cf_mod_load() + mod_write = False + + # Check/Set raw files in modules/ + for mod in files: + # For all modules to reset, confirm once + if os.path.exists(files[mod]["wrk"]): + if module == "all": + try: asked + except: form.ask_reset_modules() ; asked = True + + elif mod != module: + continue + + mod_write = True + files[mod]["set"](mod) + tools.create_file(files[mod]["wrk"], files[mod]["usr"]) + cid = tools.get_HID(files[mod]["wrk"], True) + cf_mod_set("RAWS", mod, cid) + + cf_mod_write() + if args.action == "new": + debug.out(250, module, wrk_dir, True, 0, False) + + # When an article is "wip" + # - create html modules + # - - if not already exists + # - - if raw module has changed + if module == "wip": + for mod in files: + if os.path.exists(files[mod]["wip"]): + raw_cid = tools.get_HID(files[mod]["wrk"], True) + try: db_id = cf_mod.get("RAWS", mod) + except: db_id = "" + if raw_cid == db_id: + continue + else: + cf_mod_set("RAWS", mod, raw_cid) + mod_write = True + + create_htmls(mod) + + cf_mod_write() + + # When user wants to force update HTML modules + # using wip [MODULE] ; called in wip.py + elif module == "update": + # Create again ALL modules in wip/template + if args.commands["modules"]["modules"]: + for mod in files: + if os.path.exists(files[mod]["wip"]): + create_htmls(mod) + # Create again specific module from command line + else: + for mod in args.commands["modules"]: + if args.commands["modules"][mod]: + if os.path.exists(files[mod]["wip"]): + create_htmls(mod) + + +#=======================================# +# Manage modules configuration ini file #====================================== +#---------------------------------------# +#================================# +# Create new modules DB ini file # +#--------------------------------# +def cf_mod_load(): + global cf_mod_uri, cf_mod + + cf_mod_uri = wrk_dir + "modules.ini" + + if not os.path.exists(cf_mod_uri): + tools.create_file(cf_mod_uri, "") + + cf_mod = False + cf_mod = configparser.ConfigParser() + cf_mod.read(cf_mod_uri) + + +#============================# +# Set datas to cf_mod ini DB # +#----------------------------# +def cf_mod_set(section, key, value): + global cf_mod + + # Add section if not exists + try: cf_mod.add_section(section) + except: pass + + cf_mod.set(section, key, value) + + +#==================================# +# Write to cf_mod ini DB if needed # +#----------------------------------# +def cf_mod_write(): + if mod_write: + with open(cf_mod_uri, "w") as f: + cf_mod.write(f) + + +#==================================================# +# Create default raw modules in modules/ directory #=========================== +#--------------------------------------------------# +#=====================================# +# Set metas.raw with default contents # +#-------------------------------------# +def set_metas_raw(mod): + global files + + files["metas"]["usr"] = langs.logs.metas_raw%( + domain.conf["title"], + domain.wrk_dirs["articles"], + files[mod]["wrk"], + files[mod]["wip"], + files[mod]["www"], + ) + metas_raw + + +#======================================# +# Set header.raw with default contents # +#--------------------------------------# +def set_header_raw(mod): + global files, header_raw + + # ----------------------------------- # + # Fille values for generic header_raw # + # ----------------------------------- # + header_raw = \ + header_raw%( + "%s %s"%(langs.site.home, domain.conf["title"]), + "/template/%s"%domain.cf.get("TEMPLATE_FILENAMES", "logo"), + "logo: %s"%domain.conf["title"], + domain.conf["title"], + domain.conf["about"] + ) + + files["header"]["usr"] = langs.logs.header_raw%( + domain.conf["title"], + domain.wrk_dirs["articles"], + files[mod]["wrk"], + files[mod]["wip"], + files[mod]["www"], + ) + header_raw + +#===============================# +# Set navbar.raw with help only # +#-------------------------------# +def set_navbar_raw(mod): + global files + + files["navbar"]["usr"] = langs.logs.metas_raw%( + domain.conf["title"], + domain.wrk_dirs["articles"], + files[mod]["wrk"], + files[mod]["wip"], + files[mod]["www"], + ) + + +#================================# +# Set sidebar.raw with help only # +#--------------------------------# +def set_sidebar_raw(mod): + global files + + files["sidebar"]["usr"] = langs.logs.sidebar_raw%( + domain.conf["title"], + domain.wrk_dirs["articles"], + files[mod]["wrk"], + files[mod]["wip"], + files[mod]["www"], + ) + +#======================================# +# Set footer.raw with default contents # +#--------------------------------------# +def set_footer_raw(mod): + global files, footer_raw + + # ---------------------------------- # + # Fill values for generic footer_raw # + # ---------------------------------- " + footer_menu = "" + footer_link = \ + '\n' + + for key, value in domain.cf.items("WEBSITE_FOOTER"): + try: + tupvalues = ast.literal_eval(value) + footer_menu = footer_menu + \ + footer_link%(tupvalues[1], tupvalues[2], "", tupvalues[0]) + except: + debug.out(102, "%s = %s"%(key, value), domain.cf_uri, True, 1, False) + + if domain.conf["sitemaps"]: + footer_menu = footer_menu + \ + footer_link%( + "/sitemap.html", + langs.site.sitemap, domain.conf["title"],langs.site.sitemap + ) + + footer_menu = footer_menu + \ + footer_link%( + "/" + domain.cf.get("TEMPLATE_FILENAMES", "rss"), + langs.site.feed_rss, domain.conf["title"], langs.site.feed_rss + ) + + # Copyright link + cur_date = tools.nowdate("year") + if domain.conf["date"][:4] != cur_date: + cur_date = "%s - %s"%(domain.conf["date"][:4], cur_date) + footer_copy = "Copyright © %s %s"%(cur_date, domain.conf["title"]) + + # Tyto credit links + tyto_site_link = "https://tyto.echolib.re/" + tyto_code_link = "https://forge.a-lec.org/echolib/tyto-litterateur" + footer_tyto = \ + '%s '%( + langs.site.tyto_credit, tyto_site_link, langs.site.off_website) + \ + 'Tyto - Littérateur' + \ + ' ['%tyto_code_link + \ + '%s'%langs.site.source_code + \ + ']' + + # Create full footer_raw contents + footer_raw = footer_raw%( + #

          + langs.site.about, domain.conf["title"], + # About + domain.conf["about"], + # Menu list items + footer_menu, + # Copyright and Tyto credit + footer_copy, + footer_tyto, + ) + + files["footer"]["usr"] = langs.logs.footer_raw%( + domain.conf["title"], + domain.wrk_dirs["articles"], + files[mod]["wrk"], + files[mod]["wip"], + files[mod]["www"], + ) + footer_raw + + +#===================================================# +# Create HTML modules files in template/ wip only #============================ +# At publish process, files are copied, not created # +#---------------------------------------------------# +#============================# +# Create [module].html files # +#----------------------------# +def create_htmls(mod): + html_file = ""%(mod, mod) + + if mod == "navbar": + html_file = create_navbar_html(html_file) + + elif mod == "sidebar": + html_file = create_sidebar_html(html_file) + + else: + with open(files[mod]["wrk"], "r") as f: + for line in f.read().rsplit("\n"): + if not line or line.isspace() or line.lstrip().startswith("#"): + continue + + html_file = "%s\n%s"%(html_file, line) + + tools.create_file(files[mod]["wip"], html_file) + debug.out(250, mod, wip_dir, True, 0, False) + + +#==============================================# +# Create HTML navbar contents, from navbar.raw # +#----------------------------------------------# +def create_navbar_html(html_file): + menu_links = "" + with open(files["navbar"]["wrk"], "r") as f: + for ln, line in enumerate(f.read().rsplit("\n"), 1): + line = line.lstrip() + if not line or line.isspace() or line.startswith("#"): + continue + + # Item has a "title" set with # as separator + set_dir = line.rstrip() + + + # Set_dir begins with "/" ; apply correction + if set_dir.startswith("/"): set_dir = set_dir[1:] + if set_dir.endswith("/"): set_dir = set_dir[:-1] + if not set_dir: # No home / in navbar menu + continue + + dir_uri = domain.wrk_dirs["articles"] + set_dir + + # Set_dir must exists + if not os.path.exists(dir_uri) or \ + not os.path.isdir(dir_uri): + debug.out(6, "%s. navbar '%s'"%( + ln, set_dir + ), files["navbar"]["wrk"], True, 1, False) + continue + + # Check for index.tyto, load DB if exists, get title + if cf_load(ln, set_dir + "/index.tyto", "navbar"): + try: set_title = cf.get("ARTICLE", "title") + except: continue + + # Check and inform if not index.html exists + srv_index_html = domain.wip + "/index.html" + if not os.path.exists(srv_index_html): + debug.out(11, "%s. navbar (404) wip/%s/index.html"%( + ln, set_dir + ), files["navbar"]["wrk"], True, 1, False) + + # Create menu links + if not menu_links: + menu_links = navbar_link%( + "/" + set_dir, set_title, os.path.basename(set_dir) + ) + else: + menu_links = "%s\n%s"%( + menu_links, + navbar_link%( + "/" + set_dir, set_title, os.path.basename(set_dir) + ) + ) + + if menu_links: + html_file = html_file + navbar_html%menu_links + + return html_file + + +#================================================# +# Create HTML sidebar contents, from sidebar.raw # +#------------------------------------------------# +def create_sidebar_html(html_file): + # Set from config file max links. Return if 0 + max_items = int(domain.cf.get("WEBSITE_MODULES", "sidebar_items")) + sidebar_title = domain.cf.get("WEBSITE_MODULES", "sidebar_title") + if max_items == 0: + return html_file + + html_items = "" + items = 0 + with open(files["sidebar"]["wrk"], "r") as f: + for ln, line in enumerate(f.read().rsplit("\n"), 1): + if items == max_items: + break + + line = line.lstrip() + if not line or \ + line.isspace() or \ + line.startswith("#") or \ + not line.endswith(".tyto"): + continue + + # Target begins with "/"... Remove it + if line.startswith("/"): line = line[1:] + + # check/load article files + if not cf_load(ln, line, "sidebar"): + continue + + items += 1 + print("> -",items, cf.get("ARTICLE", "title")) + + if not html_items: + html_items = sidebar_item%( + #

          + cf.get("URIS", "web"), + cf.get("ARTICLE", "title"), + #

          + cf.get("ARTICLE", "title"), + cf.get("ARTICLE", "authors"), + cf.get("ARTICLE", "local_date"), + # Show about + cf.get("ARTICLE", "about") + ) + else: + html_items = "%s\n%s"%(html_items, sidebar_item%( + cf.get("ARTICLE", "title"), + cf.get("ARTICLE", "about") + ) + ) + + if html_items: + html_file = html_file + sidebar_html%(sidebar_title, html_items) + + return html_file + + +#==========================================# +# Get DB post from URI, and load if exists # +#------------------------------------------# +def cf_load(ln, line, modname): + global cf + + uri = domain.wrk_dirs["articles"] + line + if not os.path.exists(uri): + debug.out(11, "%s. %s '%s'"%( + ln, modname, line + ), files[modname]["wrk"], True, 1, False) + return False + + db_uri = domain.wrk_dirs["db"] + tools.get_HID(uri, False) + ".ini" + if not os.path.exists(db_uri): + debug.out(12, "%s. %s '%s'"%( + ln, modname, line + ), files[modname]["wrk"], True, 1, False) + return False + + cf = configparser.ConfigParser() + cf.read(db_uri) + + try: srv_uri = cf.get("URIS", "wip") + except: srv_uri = "" + if not srv_uri or not os.path.exists(srv_uri): + debug.out(13, "%s. %s '%s'"%( + ln, modname, line + ), files[modname]["wrk"], True, 1, False) + return False + + return True + + +#===============# +# MAIN settings #============================================================== +#---------------# +# ---------------------------- # +# Default raw modules contents # +# ---------------------------- # +#-------# +# Metas # +#-------# +metas_raw = """ + + + + + +""" + +#--------# +# Header # +#--------# +header_raw = """ + +

        \n", "
      \n"] - OPENING = ["ERROR", "
        \n", "
          \n"] - - rank_stack = [] - rank = 0 - cur_rank = 0 - state = 0 - old_state = 0 - work_str = "" - - for i in range(len(items)): - rank = cur_rank - descriptor = items[i].split(" ")[0] - text = items[i][items[i].find(" "):].lstrip() - cur_rank = len(descriptor) - - if "=" in descriptor: - state = UL - elif "+" in descriptor: - state = OL - else: - raise(Exception) - - # rank up - if cur_rank > rank: - for i in range(cur_rank - rank): - work_str += " "*(rank+i) + OPENING[state] - rank_stack.append(state) - - # rank down - elif cur_rank < rank: - for i in range(rank - cur_rank): - work_str += " "*(rank-i-1) + CLOSING[rank_stack.pop()] - - work_str += " "*cur_rank + '
        1. ' + text + "
        2. \n" - - for i in range(cur_rank): - work_str += " "*(cur_rank-i-1) + CLOSING[rank_stack.pop()] - - return work_str - - -#===========================================# -# Create contents in