diff --git a/README.md b/README.md index af4b100..50bf72d 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,26 @@ Pour obtenir de l'aide, taper juste la commande tyto ## Répertoire de code du projet Tyto +TODO ## Comment définir les métas ``` # Obligatoires uniques +# Ces marqueurs se configurent sur UNE ligne title: Titre about: Infos de l'article author: Auteur -tags: mots-clé-1,mots-clé-2 +tags: mot-clé-1,mot 2, date: YYYY-MM-DD (AAAA-MM-JJ) +# L'image doit être configurée avec le même Nom +# dans les marqueurs multiples +# Utilise l'image précisée comme "avatar" dans les réseaux sociaux +snpic: Nom + + # Optionnels multiples +# Ces marqueurs se configurent sur 3 lignes link: Nom du lien URL Texte Alternatif @@ -33,10 +42,6 @@ abbr: abbrev Définition de abbrev ABBR (forme à afficher dans l'artile (optionnel)) -# L'image doit d'abord être configurée -# Utiliser l'image précisée comme défaut dans les réseaux sociaux -snpic: Nom - # Séparateur d'au moins 5 "-" pour définir la fin # des métadonnées d'entête de l'article ---------- @@ -103,13 +108,14 @@ def hello_world(): ### Ancres ``` -# Source` +# Source de l'ancre cible. "id" est son identité -> id # HTML -# Source +# Source de l'ancre d'appel +# Définir l'identité cible et le texte du lien (( >_id:Retourner au point d'ancre id_< )) @@ -120,7 +126,11 @@ def hello_world(): ### Retour à la ligne HTML ``` -| #
+# Source +| + +# HTML +
``` ### Lien vers URL @@ -136,7 +146,8 @@ Voir ce __Nom du lien+ # ouverture nouvelle fenêtre ``` Note: -Vous pouvez avoir un Nom identique pour file: et link: +Vous pouvez avoir un Nom identique pour les marqueur `file:` et `link:` + ### Gras, Strong, italique... ``` @@ -159,7 +170,7 @@ Vous pouvez avoir un Nom identique pour file: et link: ### Abréviations ``` # abbrev sera remplacé par "ABBR" dans la page si défini en entête -# sinon, abbrev sera conservé +# sinon, abbrev sera conservé # - Toujours écrire dans l'article : # - entre parenthèses ET majuscules les "(ABBREV)" @@ -186,12 +197,12 @@ _image:Nom c=CSS t=https://... w=320px h=240 # 240px ### Code brut depuis un fichier ``` -_raw:NOM +_raw:Nom ``` ### Citations Possibilité dans toute citation d'utiliser les marqueurs -optionnels _xxx. Pour la date, utilisez le FORMAT INTERNATIONAL +optionnels `_xxx:`. Pour la date, utilisez le FORMAT INTERNATIONAL ``` # Source: citation complète [[ CSS_TEST diff --git a/src/var/lib/tyto/program/args.py b/src/var/lib/tyto/program/args.py index c7e4732..2c9a86c 100644 --- a/src/var/lib/tyto/program/args.py +++ b/src/var/lib/tyto/program/args.py @@ -58,6 +58,7 @@ pass_db = \ 'edit-www', 'publish', 'show', + 'show-about', 'show-db', 'show-wip', 'show-www', diff --git a/src/var/lib/tyto/program/check.py b/src/var/lib/tyto/program/check.py index 556413a..5e02a6d 100644 --- a/src/var/lib/tyto/program/check.py +++ b/src/var/lib/tyto/program/check.py @@ -1,11 +1,28 @@ #!/usr/bin/env python3 -# Name: Tyto - Littérateur -# Type: Global functions for check -# Description: Check article contents. Create Stats and Database -# file: check.py -# Folder: /var/lib/tyto/program/ -# By echolib (XMPP: im@echolib.re) -# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +# Tyto - Littérateur +# +# Copyright (C) 2023 Cyrille Louarn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation, either version 3 of the License, or +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Manage 'check' argument. +# Check article's validity in Tyto's format +# File: /var/lib/tyto/program/check.py +#---------------------------------------------------------------------- #------------ # funny stats @@ -115,7 +132,7 @@ def check_all(option): if option == 'all' and not db.old_chk: continue found = True - print(':> [%s] - %s'%(db.title, db.post_src)) + print(' ├ [%s] > %s'%(db.title, db.post_src)) check_process(args.target) if post_err: logs.out("44", args.target, False) @@ -279,24 +296,19 @@ def count_words(post_bottom): #---------------------------------------------# def if_icodes_bcodes_quotes(post_bottom): global icode, quote, bcode, post_err - global nbr_titles, nbr_quotes, nbr_bcodes, nbr_ancs + global nbr_titles, nbr_quotes, nbr_bcodes, nbr_ancs, nbr_coms - icode = quote = in_quote = bcode = in_bcode = False - nbr_titles = nbr_quotes = nbr_bcodes = nbr_ancs = 0 + icode = quote = in_quote = bcode = in_bcode = in_bq = False + nbr_titles = nbr_quotes = nbr_bcodes = nbr_ancs = nbr_coms = 0 - for ln, line in enumerate(post_bottom.rsplit('\n'), 1): - # Comments, count titles + for ln, line in enumerate(post_bottom.rsplit('\n'), 1): + # Stat Comments, Titles, Anchors if not line: continue - elif line.startswith(tyto.titles_tags): - nbr_titles += 1 - continue - elif line.startswith('#'): - continue # quotes elif line.startswith(tyto.words_tags[11][0]) and not in_bcode: - quote = in_quote = True + quote = in_quote = in_bq = True continue elif line.startswith(tyto.words_tags[11][1]): in_quote = False @@ -305,18 +317,25 @@ def if_icodes_bcodes_quotes(post_bottom): # bcode elif line.startswith(tyto.words_tags[12][0]) and not in_quote: - bcode = in_bcode = True + bcode = in_bcode = in_bq = True continue elif line.startswith(tyto.words_tags[12][1]): in_bcode = False nbr_bcodes += 1 continue - if in_bcode or in_quote: + if in_bq: continue - elif line.startswith(tyto.single_tags[1][0]): + elif line.startswith(tyto.titles_tags) and not in_bq: + nbr_titles += 1 + continue + elif line.startswith('#') and not in_bq: + nbr_coms += 1 + continue + elif line.startswith(tyto.single_tags[1][0]) and not in_bq: nbr_ancs += 1 + continue # icodes elif tyto.words_tags[9][0] or tyto.words_tags[9][1] in line: @@ -409,14 +428,14 @@ def check_date(date): # Multiple settings for each on 3 lines # #-------------------------------------------# def check_opt_tags(post_header): - global stat_links, stat_images, stat_files, stat_raws, stat_abbrs - global stat_snpics, snpic_url + global stat_links, stat_images, stat_files, stat_raws + global stat_snpics, snpic_url, stat_abbrs, post_abbrs global opt_tags_post_name global files_post # Statistics - stat_links = stat_images = stat_files = stat_raws = stat_abbrs = 0 - stat_snpics = 0 + stat_links = stat_images = stat_files = stat_raws = 0 + stat_snpics = post_abbrs = stat_abbrs = 0 files_post = (()) # Set default post pic @@ -446,8 +465,9 @@ def check_opt_tags(post_header): #--------------------------------------------# def check_3lines(tag, ln, line): global post_err, db_tag, files_post + global post_abbrs - stat_tag = "stat_%ss"%tag + stat_tag = "stat_%ss"%tag # Create variable for post DB globals()[stat_tag] += 1 @@ -462,8 +482,7 @@ def check_3lines(tag, ln, line): name = line.rsplit('%s:'%tag)[1].lstrip() else: name = line.rsplit('%s:'%tag)[1].lstrip().rsplit(' ')[0] - if tag == "abbr": - name = name.upper() + if not name: logs.out("2", 'L=%s. "%s: %s" > %s'%( @@ -471,6 +490,11 @@ def check_3lines(tag, ln, line): ), False) post_err = True + # abbr: + elif tag == "abbr": + name_abbr = name.upper() + post_abbrs = post_bottom.count('(%s)'%name_abbr) + # snpic only needs a Name elif tag == "snpic": check_snpic(name) @@ -482,7 +506,7 @@ def check_3lines(tag, ln, line): ), False) post_err = True - globals()[db_tag] = ((name),) + globals()[db_tag] = ((opt_tags_post_name[tag]%name),) # URI/URL and Alt-Text #--------------------- @@ -509,9 +533,13 @@ def check_3lines(tag, ln, line): # Set data for post DB if l == 1 and tag in tyto.opt_tags_check_uri: check_file_uri(tag, data, ln) + if post_err: + return globals()[db_tag] = globals()[db_tag] + ((web_uri),) files_post = files_post + (('%s'%web_uri),) else: + if post_err: + return globals()[db_tag] = globals()[db_tag] + ((data),) @@ -603,78 +631,6 @@ def check_snpic(name): def check_content(post_bottom): global post_err - # Check if anchor has target - # Count anchors target - #--------------------------- - for ln, line in enumerate(post_bottom.rsplit('\n'), 1): - # Anchor link - if tyto.words_tags[0][0] and tyto.words_tags[0][1] in line: - anchors = re.findall(r">_(.*?)_<", line) - for anchor in anchors: - css_anchor = anchor.rsplit(':')[0] - tag = '%s %s'%(tyto.single_tags[1][0], css_anchor) - if not tag in post_bottom: - logs.out("12", 'L=%s. anchor "%s" > %s'%( - ln + ln_header, tag, db.uri_file - ), False) - post_err = True - - # Anchor source "->" - if line.startswith(tyto.single_tags[1][0]): - set_css = tyto.get_css(line) - is_uniq_anchor = "%s %s"%(tyto.single_tags[1][0], set_css) - c_uniq_anchor = post_bottom.count(is_uniq_anchor) - if c_uniq_anchor > 1: - logs.out("15", 'L=%s. %sx "%s" > %s'%( - ln + ln_header, c_uniq_anchor, is_uniq_anchor, - db.uri_file - ), False) - post_err = True - break - - - # Lists: check if contents are valid - #----------------------------------- - inlist = False - markers_lists = '%s, %s, or space'%( - tyto.markers_lists[0], - tyto.markers_lists[1] - ) - for ln, line in enumerate(article.rsplit('\n'), 1): - if line.startswith('-('): inlist = True;continue - elif line.startswith('-)'): inlist = False - if not inlist: continue - - if inlist and not line or not line[0] in tyto.markers_lists: - logs.out("3", 'line %s must start with %s'%( - ln, markers_lists - ), False - ) - post_err = True - - - # Check for all match _TAGS:NAME from content in header - #------------------------------------------------------ - set_tags = () - for ln, line in enumerate(post_bottom): - for htag in tyto.head_tags: - match = False - ptag = '_%s'%htag - - if line.startswith(ptag): - ptag_set = line.rsplit(':', 1)[1].lstrip().rsplit(' ')[0] - if ptag_set in set_tags: continue - for hline in post_header: - if re.search(r"^%s\s+%s$"%(htag,ptag_set), hline): - match = True - set_tags = (ptag_set) - - if match: continue - else: - logs.out("12", "%s %s"%(htag, ptag_set), False) - post_err = True - - # Check tags for words (strongs, italics...) # Set stats for each one #------------------------------------------- @@ -702,6 +658,85 @@ def check_content(post_bottom): globals()['post_%s'%tag[4]] = int(c_opened) + # Check if anchor has target + # Count anchors target + #--------------------------- + if post_anchors > 0: + for ln, line in enumerate(post_bottom.rsplit('\n'), 1): + # Anchor link + if tyto.words_tags[0][0] and tyto.words_tags[0][1] in line: + anchors = re.findall(r">_(.*?)_<", line) + for anchor in anchors: + css_anchor = anchor.rsplit(':')[0] + tag = '%s %s'%(tyto.single_tags[1][0], css_anchor) + if not tag in post_bottom: + logs.out("12", 'L=%s. anchor "%s" > %s'%( + ln + ln_header, tag, db.uri_file + ), False) + post_err = True + + # Anchor "->" id must be uniq + if nbr_ancs > 0: + if line.startswith(tyto.single_tags[1][0]): + set_css = tyto.get_css(line) + is_uniq_anchor = "%s %s"%(tyto.single_tags[1][0], set_css) + c_uniq_anchor = post_bottom.count(is_uniq_anchor) + if c_uniq_anchor > 1: + logs.out("15", 'L=%s. %sx "%s" > %s'%( + ln + ln_header, c_uniq_anchor, is_uniq_anchor, + db.uri_file + ), False) + post_err = True + break + + + # Lists: check if contents are valid + #----------------------------------- + if post_lists > 0: + inlist = False + for ln, line in enumerate(post_bottom.rsplit('\n'), 1): + if line.startswith(tyto.words_tags[13][0]): + inlist = True + continue + elif line.startswith(tyto.words_tags[13][1]): + inlist = False + + if not inlist: + continue + + if inlist and not line or not line[0] in tyto.markers_lists: + logs.out("3", 'L=%s. %s %s > %s'%( + ln + ln_header, + tyto.words_tags[13][4], + tyto.markers_lists, + db.uri_file + ), False + ) + post_err = True + + + # Check for all match _TAGS:NAME from content in header + #------------------------------------------------------ + set_tags = () + for ln, line in enumerate(post_bottom): + for htag in tyto.head_tags: + match = False + ptag = '_%s'%htag + + if line.startswith(ptag): + ptag_set = line.rsplit(':', 1)[1].lstrip().rsplit(' ')[0] + if ptag_set in set_tags: continue + for hline in post_header: + if re.search(r"^%s\s+%s$"%(htag, ptag_set), hline): + match = True + set_tags = (ptag_set) + + if match: continue + else: + logs.out("12", "%s %s"%(htag, ptag_set), False) + post_err = True + + # Template Tags (warning for not paired symbols) #----------------------------------------------- for tag in tyto.tpl_tags: @@ -803,24 +838,26 @@ def create_database(): 'uniq_files = %d\n'%stat_files + \ 'uniq_raws = %d\n'%stat_raws + \ '\n# Statistics from post content\n' + \ - 'stat_tags = %d\n'%stat_tags + \ - 'stat_lines = %d\n'%ln_article + \ - 'stat_words = %d\n'%stat_words + \ - 'stat_titles = %d\n'%nbr_titles + \ - 'stat_paragraphs = %d\n'%post_paragraphs + \ - 'stat_anchors = %d\n'%post_anchors + \ - 'stat_strongs = %d\n'%post_strongs + \ - 'stat_bolds = %d\n'%post_bolds + \ - 'stat_emphasis = %d\n'%post_emphasis + \ - 'stat_italics = %d\n'%post_italics + \ - 'stat_dels = %d\n'%post_dels + \ - 'stat_underlines = %d\n'%post_underlines + \ - 'stat_cites = %d\n'%post_cites + \ - 'stat_customs = %d\n'%post_customs + \ - 'stat_icodes = %d\n'%tyto.nbr_icodes + \ - 'stat_bcodes = %d\n'%nbr_bcodes + \ - 'stat_quotes = %d\n'%nbr_quotes + \ - 'stat_lists = %d\n'%post_lists + 'post_coms = %d\n'%nbr_coms + \ + 'post_tags = %d\n'%stat_tags + \ + 'post_lines = %d\n'%ln_article + \ + 'post_words = %d\n'%stat_words + \ + 'post_titles = %d\n'%nbr_titles + \ + 'post_paragraphs = %d\n'%post_paragraphs + \ + 'post_anchors = %d\n'%post_anchors + \ + 'post_abbrs = %d\n'%post_abbrs + \ + 'post_strongs = %d\n'%post_strongs + \ + 'post_bolds = %d\n'%post_bolds + \ + 'post_emphasis = %d\n'%post_emphasis + \ + 'post_italics = %d\n'%post_italics + \ + 'post_dels = %d\n'%post_dels + \ + 'post_underlines = %d\n'%post_underlines + \ + 'post_cites = %d\n'%post_cites + \ + 'post_customs = %d\n'%post_customs + \ + 'post_icodes = %d\n'%tyto.nbr_icodes + \ + 'post_bcodes = %d\n'%nbr_bcodes + \ + 'post_quotes = %d\n'%nbr_quotes + \ + 'post_lists = %d\n'%post_lists # Create Post DB #--------------- @@ -828,336 +865,3 @@ def create_database(): tyto.set_file(db.config, 'new', database) logs.out("21", db.uri_file, False) - - - - - - -#==================================# -# Check tags from article's header # -#----------------------------------# -def check_headers(post_header): - global post_err, err, web_uri, date_check - global date, title, author, tags, about - global stat_links, stat_images, stat_files, stat_raws, stat_abbrs - global post_tags - global snpic_url - global files_post - - # Contains all files URIs needed for article - # Used with publish command to copy needed files - files_post = ('') - - snshare = False - snpic_name = '' - - # Needed Tags - title = author = about = tags = '' - date = () - - # Statistics - stat_links = stat_images = stat_files = stat_raws = stat_abbrs = 0 - - -### - # Second session for optional tags # - # Read articles lines, till separator # - #-------------------------------------# - for ln, line in enumerate(post_header, 1): - if line.startswith('-----'): break - - # Set each optional tags - #----------------------- - # ABBR - #----- - tag = tyto.headers[8] # abbr: - if line.startswith(tag): - stat_abbrs += 1 - var_tag = 'abbr_%s'%stat_abbrs - - abbr_short = post_header[ln - 1].rsplit(tag)[1].lstrip() - if not abbr_short: - logs.out("2", "Line %s (SHORT, %s)"%(ln, tag), False) - post_err = True - if not abbr_short.isupper(): - logs.out("3", "Line %s (Upper SHORT, %s)"%(ln, tag), False) - post_err = True - continue - if not isin(r'!\b%s\b'%abbr_short, post_bottom): - logs.out("6", '!%s'%abbr_short, False) - post_err = True - - abbr_long = post_header[ln].lstrip() - if abbr_long.startswith(tyto.headers): abbr_long = '' - if not abbr_long: - logs.out("2", "Line %s (Long, %s)"%(ln + 1, tag), False) - post_err = True - - abbr_alt = post_header[ln + 1].lstrip() - if abbr_alt.startswith(tyto.headers): abbr_alt = '' - if not abbr_alt: abbr_alt = abbr_short - - if not post_err: - web_link = '%s'%( - 'abbr', abbr_long, abbr_alt - ) - globals()['abbr_%s'%stat_abbrs] = ( - '!%s'%abbr_short, web_link - ) - - # LINK - #----- - tag = tyto.headers[5] # link: - if line.startswith(tag): - stat_links += 1 - var_tag = 'link_%s'%stat_links - - # NAME - link_name = post_header[ln - 1].rsplit(tag)[1].lstrip() - if not link_name: - logs.out("2", 'L=%s. "%s %s" > %s'%( - ln, tag, tr.name, db.uri_file - ), False) - post_err = True - if not post_err and \ - not isin(r'\b_%s\b'%link_name, post_bottom): - logs.out("12", '"_%s%s" > %s'%( - tag, image_name, db.uri_file - ), False) - post_err = True - - # URL - try: - link_url = post_header[ln].lstrip() - if link_url.startswith(tyto.headers): link_url = '' - except: - link_url = '' - - if not link_url: - logs.out("2", 'L=%s. "%s URL" > %s)'%( - ln + 1, tag, db.uri_file - ), False) - post_err = True - - # ALT - try: - link_alt = post_header[ln + 1].lstrip() - if link_alt.startswith(tyto.headers): link_alt = '' - except: - link_alt = '' - - if not link_alt: - logs.out("2", 'L=%s. "%s Alt-Text" > %s'%( - ln + 2, tag, db.uri_file - ), False) - post_err = True - - if not post_err: - web_link = '%s'%( - '%s', link_alt, link_name - ) - globals()['link_%s'%stat_links] = ( - '_%s'%link_name, web_link - ) - - # IMAGE - #------ - tag = tyto.headers[6] # image: - if line.startswith(tag): - stat_images += 1 - var_tag = 'image_%s'%stat_images - - # NAME - image_name = post_header[ln - 1] - image_name = image_name.rsplit(tag)[1].lstrip().rsplit(' ')[0] - if not image_name: - logs.out("2", 'L=%s. "%s %s" > %s'%( - ln, tag, tr.name, db.uri_file - ), False) - post_err = True - if not post_err and \ - not isin(r'\b_%s%s\b'%(tag, image_name), post_bottom): - logs.out("12", '"_%s%s" > %s'%( - tag, image_name, db.uri_file - ), False) - post_err = True - - # URI - try: - image_uri = post_header[ln].lstrip() - if image_uri.startswith(tyto.headers): image_uri = '' - except: - image_uri = '' - - if not image_uri: - logs.out("2", 'L=%s. "%s URI" > %s)'%( - ln + 1, tag, db.uri_file - ), False) - post_err = True - else: - check_file_uri('image', image_uri, ln + 1) - if not post_err: - f_uri = web_uri[1:len(web_uri)] - files_post = (files_post + "'%s', "%f_uri) - - # ALT - try: - image_alt = post_header[ln + 1].lstrip() - if image_alt.startswith(tyto.headers): image_alt = '' - except: - image_alt = '' - - if not image_alt: - logs.out("2", 'L=%s. "%s Alt-Text" > %s'%( - ln + 2, tag, db.uri_file - ), False) - post_err = True - - if not post_err: - globals()['image_%s'%stat_images] = ( - '_%s%s'%(tag, image_name), - web_uri, - image_alt - ) - - # RAW File - #--------- - tag = tyto.headers[9] # raw: - if line.startswith(tag): - stat_raws += 1 - var_tag = 'raw_%s'%stat_raws - - # NAME - raw_name = post_header[ln - 1] - raw_name = raw_name.rsplit(tag)[1].lstrip().rsplit(' ')[0] - if not raw_name: - logs.out("2", 'L=%s. "%s %s" > %s'%( - ln, tag, tr.name, db.uri_file - ), False) - post_err = True - if not post_err and \ - not isin(r'\b_%s%s\b'%(tag, raw_name), post_bottom): - logs.out("12", '"_%s%s" > %s'%( - tag, raw_name, db.uri_file - ), False) - post_err = True - - # URI - try: - raw_uri = post_header[ln].lstrip() - if raw_uri.startswith(tyto.headers): raw_uri = '' - except: - raw_uri = '' - - if not raw_uri: - logs.out("2", 'L=%s. "%s URI" > %s)'%( - ln + 1, tag, db.uri_file - ), False) - post_err = True - else: - check_file_uri('file', raw_uri, ln + 1) - if not post_err: - f_uri = web_uri[1:len(web_uri)] - files_post = (files_post + "'%s', "%f_uri) - - # ALT - try: - raw_alt = post_header[ln + 1].lstrip() - if raw_alt.startswith(tyto.headers): raw_alt = '' - except: - raw_alt = '' - - if not raw_alt: - logs.out("2", 'L=%s. "%s Alt-Text" > %s'%( - ln + 2, tag, db.uri_file - ), False) - post_err = True - - if not post_err: - globals()['raw_%s'%stat_raws] = ( - '_%s%s'%(tag, raw_name), - web_uri, - raw_alt - ) - - # FILE - #----- - tag = 'file:' - if line.startswith(tag): - stat_files += 1 - var_tag = 'file_%s'%stat_files - - # NAME - file_name = post_header[ln - 1].rsplit(tag)[1].lstrip() - if not file_name: - logs.out("2", "Line %s (Name, %s)"%(ln, tag), False) - post_err = True - if not post_err and \ - not isin(r'\b__%s\b'%file_name, post_bottom): - logs.out("6", "__%s"%file_name, False) - post_err = True - - # URI - try: - file_uri = post_header[ln].lstrip() - if file_uri.startswith(tyto.headers): file_uri = '' - except: - file_uri = '' - - if not file_uri: - logs.out("2", 'L=%s. "%s URI" > %s)'%( - ln + 1, tag, db.uri_file - ), False) - post_err = True - else: - check_file_uri('file', file_uri, ln + 1) - if not post_err: - f_uri = web_uri[1:len(web_uri)] - files_post = (files_post + "'%s', "%f_uri) - - # ALT - try: - file_alt = post_header[ln + 1].lstrip() - if file_alt.startswith(tyto.headers): file_alt = '' - except: - file_alt = '' - - if not file_alt: - logs.out("2", 'L=%s. "%s Alt-Text" > %s'%( - ln + 2, tag, db.uri_file - ), False) - post_err = True - - if not post_err: - web_link = '%s'%( - '%s', file_alt, file_name - ) - globals()['file_%s'%stat_files] = ( - '__%s'%file_name, web_link - ) - - # snpic (set image to share to social networks) - #---------------------------------------------- - if snpic_name: continue - - snpic_url = '%s/template/%s'%(dom.www_url, dom.logo) - tag = tyto.headers[11] # snpic: - if line.startswith(tag): - snpic_name = post_header[ln - 1].rsplit(tag)[1].lstrip() - - for ln, line in enumerate(post_header, 1): - if re.search(r"^image:\s+%s$"%snpic_name, line): - image_uri = post_header[ln].lstrip() - check_file_uri('image', image_uri, ln + 1) - snshare = True - snpic_url = '%s%s'%(dom.www_url, web_uri) - break - - if not snshare: - logs.out("12", '%s %s'%(tyto.headers[6], snpic_name), False) - post_err = True - - diff --git a/src/var/lib/tyto/program/db.py b/src/var/lib/tyto/program/db.py index 2408988..df750ac 100644 --- a/src/var/lib/tyto/program/db.py +++ b/src/var/lib/tyto/program/db.py @@ -89,23 +89,25 @@ if args.target \ 'uniq_images', 'uniq_files', 'uniq_raws', - 'stat_tags', - 'stat_words', - 'stat_titles', - 'stat_paragraphs', - 'stat_anchors', - 'stat_strongs', - 'stat_bolds', - 'stat_emphasis', - 'stat_italics', - 'stat_dels', - 'stat_underlines', - 'stat_cites', - 'stat_customs', - 'stat_icodes', - 'stat_bcodes', - 'stat_quotes', - 'stat_lists' + 'post_coms', + 'post_tags', + 'post_words', + 'post_titles', + 'post_paragraphs', + 'post_anchors', + 'post_abbrs', + 'post_strongs', + 'post_bolds', + 'post_emphasis', + 'post_italics', + 'post_dels', + 'post_underlines', + 'post_cites', + 'post_customs', + 'post_icodes', + 'post_bcodes', + 'post_quotes', + 'post_lists' ) for value in values: diff --git a/src/var/lib/tyto/program/form.py b/src/var/lib/tyto/program/form.py index 94aa507..501a85f 100644 --- a/src/var/lib/tyto/program/form.py +++ b/src/var/lib/tyto/program/form.py @@ -659,7 +659,7 @@ def create_domain(target): except: active = False print(tr.form_warn) - show.read_lines(dom.config) + show.read_lines(dom.config, False) # Activate and prepare domain ? #------------------------------ @@ -677,12 +677,10 @@ def create_domain(target): #---------------- tyto.set_file(dom.config, False, '\nactivated = True') - # RELoad config #-------------- importlib.reload(dom) - # Create folders from configuration file #--------------------------------------- folders = \ diff --git a/src/var/lib/tyto/program/html.py b/src/var/lib/tyto/program/html.py index f126492..369199d 100644 --- a/src/var/lib/tyto/program/html.py +++ b/src/var/lib/tyto/program/html.py @@ -1,11 +1,26 @@ -#!/usr/bin/env python3 -# Name: Tyto - Littérateur -# Type: HTML template -# Description: Create raw HTML template -# file: html.py -# Folder: /var/lib/tyto/program/ -# By echolib (XMPP: im@echolib.re) -# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +# Tyto - Littérateur +# +# Copyright (C) 2023 Cyrille Louarn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation, either version 3 of the License, or +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Create HTML page +# File: /var/lib/tyto/program/show.py +#---------------------------------------------------------------------- #------------ # funny stats @@ -19,7 +34,7 @@ import os, sys, importlib -import logs, dom, tyto, form +import logs, db, dom, tyto, form # load locale translation trans_dir = '/var/lib/tyto/translations' @@ -64,22 +79,22 @@ def create_metas_page(): #------------------- metas_page = '' scale = 'width=device-width, initial-scale=1.0' - all_tags = db.domain_tags + ',' + db.tags + all_tags = dom.tags + ',' + db.tags css_file = 'styles.css' css_ref = 'href="%stemplate/%s"'%(db.sub_uri, css_file) rss_ref = 'type="application/rss+xml" ' + \ 'href="%s%s" title="RSS 2.0. %s %s %s"'%( - db.sub_uri, db.domain_rss, - db.domain_title, db.domain_sep, db.domain_short + db.sub_uri, dom.rss, + dom.title, dom.sep, dom.shortname ) icon_file = 'favicon.png' icon_ref = 'type="image/png" href="%stemplate/%s"'%( db.sub_uri, icon_file ) relme = '' # External URL in metas (if exists in config domain) - if db.domain_relme: + if dom.relme: relme = '\n '%( - db.domain_relme + dom.relme ) # Create author and date publish section infos @@ -91,10 +106,10 @@ def create_metas_page(): metas = \ '\n' + \ ' \n'%scale + \ - ' \n'%db.domain_www_url + \ - ' \n'%db.domain_lang + \ - ' \n'%db.domain_mail + \ - ' \n'%db.domain_license + \ + ' \n'%dom.www_url + \ + ' \n'%dom.lang_site + \ + ' \n'%dom.mail + \ + ' \n'%dom.license + \ ' \n'%tyto.Tyto + \ ' \n'%db.title + \ ' \n'%db.author + \ @@ -106,14 +121,14 @@ def create_metas_page(): ' \n'%css_ref + \ ' \n'%icon_ref + \ ' \n' + \ - ' \n'%db.domain_title + \ + ' \n'%dom.title + \ ' \n'%db.title + \ ' \n' + \ ' \n'%db.http_www + \ ' \n'%db.about + \ ' \n'%db.snpic + \ '%s'%relme + \ - ' %s'%db.title + ' %s %s %s'%(db.title, dom.sep, dom.title) #=======================================# @@ -122,23 +137,23 @@ def create_metas_page(): def create_main_page(target, article_bottom): global main_page - if not tyto.exists(db.wip_footer): - logs.out("1", db.wip_footer, True) + if not tyto.exists(dom.wip_footer_f): + logs.out("1", dom.wip_footer_f, True) - if not tyto.exists(db.wip_metas): - logs.out("24", '(HTML metas): %s'%db.wip_metas, False) + if not tyto.exists(dom.wip_metas_f): + logs.out("24", '(HTML metas): %s'%dom.wip_metas_f, False) # Create link for website's logo #------------------------------- logo_html = '\n'%(11 * " ") + \ '%s\n'%(15 * " ") + \ '%s'%(8 * " ") @@ -147,7 +162,7 @@ def create_main_page(target, article_bottom): #-----------------------# main_page = \ '\n' + \ - '\n'%db.domain_lang + \ + '\n'%dom.lang_site + \ ' \n' + \ '%s\n'%metas + \ ' \n\n' + \ @@ -161,9 +176,9 @@ def create_main_page(target, article_bottom): ' \n' + \ - '

%s

\n'%db.domain_title + \ + '

%s

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

%s

\n'%db.domain_about + \ + '

%s

\n'%dom.about + \ ' \n' + \ '\n' + \ ' \n' + \ @@ -187,7 +202,7 @@ def create_main_page(target, article_bottom): #--------------------------------------------# def create_html_infos_section(process): # Need to reload the DB to get last time updated - exec(open(db.post_db).read(), globals()) + exec(open(db.config).read(), globals()) global post_pub, meta_pub, date_raw if process == 'wip': @@ -205,7 +220,7 @@ def create_html_infos_section(process): # Show source code in article-infos if True in DB post_code = '' - if db.article_code: + if dom.article_code: # Set HTML post_code = \ ' ' + \ @@ -213,7 +228,7 @@ def create_html_infos_section(process): os.path.basename(db.short_src), tyto.trans[21][tyto.n], tyto.trans[3][tyto.n] - ) + ) + \ ' ' @@ -248,26 +263,24 @@ def create_html_infos_section(process): # Create HTML sidebar from file tyto.sidebar # #--------------------------------------------# def create_sidebar(option): - domain.domain_needed + dom.valid() try: - db.sidebar_load - if not tyto.exists(db.sidebar_load): - logs.out("1", db.sidebar_load, True) + if not tyto.exists(dom.sidebar_f): + logs.out("1", dom.sidebar_f, True) except: - logs.out("1", 'Sidebar load file', True) + logs.out("1", 'Sidebar ?', True) try: - db.sidebar_items - if int(db.sidebar_items) > 16: db.sidebar_items = 6 + if int(dom.sidebar_items) > 16: db.sidebar_items = 6 except: db.sidebar_items = 6 pub_opts = ('www', 'pub') - if option == 'wip': target = db.wip_sidebar - elif option == 'www': target = db.www_sidebar - elif option == 'pub': target = db.www_sidebar + if option == 'wip': target = dom.wip_sidebar_f + elif option == 'www': target = dom.www_sidebar_f + elif option == 'pub': target = dom.www_sidebar_f # If content in sidebar, go True sidebar_new = False @@ -285,8 +298,8 @@ def create_sidebar(option): counter = 0 nolines = ('#', '/') - sidebar_title = db.sidebar_title - sidebar_lines = open(db.sidebar_load, 'r').read() + sidebar_title = dom.sidebar_title + sidebar_lines = open(dom.sidebar_f, 'r').read() for line in sidebar_lines.rsplit('\n'): if not line or line.startswith(nolines): continue @@ -296,14 +309,14 @@ def create_sidebar(option): # Get full article URI and check if exists sidebar_has = True - f_uri = '%s%s'%(db.domain_articles, line) + f_uri = '%s%s'%(dom.articles_d, line) if not tyto.exists(f_uri): logs.out("24", f_uri, False) continue # Get Hash from uri to get db file hash_uri = tyto.get_filesum(f_uri, False) - db_uri = '%s%s.conf'%(db.articles_db, hash_uri) + db_uri = '%s%s.config'%(dom.articles_db_d, hash_uri) if not tyto.exists(db_uri): logs.out('25', line, False) continue @@ -332,9 +345,9 @@ def create_sidebar(option): # Count: no more than max configured items counter += 1 - if counter > db.sidebar_items: + if counter > dom.sidebar_items: logs.out("31", '(%s): %s "%s"'%( - db.sidebar_items, line, title), + dom.sidebar_items, line, title), False ) continue diff --git a/src/var/lib/tyto/program/show.py b/src/var/lib/tyto/program/show.py index 3d571a8..b8febea 100644 --- a/src/var/lib/tyto/program/show.py +++ b/src/var/lib/tyto/program/show.py @@ -1,11 +1,28 @@ #!/usr/bin/env python3 -# Name: Tyto - Littérateur -# Type: Command arguments 'show', 'showdb' manager -# Description: manage 'show' and 'showdb' from command action argument -# file: show.py -# Folder: /var/lib/tyto/program/ -# By echolib (XMPP: im@echolib.re) -# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +# Tyto - Littérateur +# +# Copyright (C) 2023 Cyrille Louarn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation, either version 3 of the License, or +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# +# Description: Manage show*/edit* arguments. +# Read or edit file from [target] argument +# File: /var/lib/tyto/program/show.py +#---------------------------------------------------------------------- #------------ # funny stats @@ -17,9 +34,64 @@ #********************************************************************** -import os, sys, importlib +import os, sys, locale, importlib import args, lang, logs, dom, db, form, tyto, check, stats +# load locale translation +trans_dir = '/var/lib/tyto/translations' +sys.path.insert(0, trans_dir) + +# System language +try: lang_sys = locale.getdefaultlocale()[0].split('_')[0] +except: lang_sys = 'en' + +# Get default system language +# or set "en" (english) if no translation file +try: + lang_site = lang_sys + os.path.exists('%s/site_%s.py'%(trans_dir, lang_site)) +except: + lang_site = 'en' + +# Set language site/form from configuration domain +# or set default english if not known +try: + dom.exists + lang_site = dom.lang_site + os.path.exists('%s/site_%s.py'%(trans_dir, lang_site)) + tr = importlib.import_module('site_%s'%lang_site, package=None) +except: + tr = importlib.import_module('site_%s'%lang_site, package=None) + + +#========================# +# Read lines from a file # +# alone: True/False # +#------------------------# +def read_lines(f, alone): + if not f: return + if not tyto.exists(f): logs.out("1", f, True) + + datas = open(f).read() + + # Align content line, after line number + ln_datas = len(datas.splitlines()) + 1 + sp_max = len(str(ln_datas)) + + print(' ├', f) + print(' ├─%s─┐'%(sp_max * '─')) + for ln, line in enumerate(datas.rsplit('\n'), 1): + sp = sp_max - len(str(ln)) + print(' │ %s %s│ %s'%(ln, int(sp) * " ", line)) + + # Ends for show. False should be for form + if alone: decor = '└' + else: decor = '├' + print(' %s─%s─┘'%(decor, sp_max * '─')) + + dom.valid() + + #======================# # From command line: # # - 'showdb' / 'show' # @@ -28,130 +100,126 @@ import args, lang, logs, dom, db, form, tyto, check, stats # final html, db, load # #----------------------# def manage(target): - # Domain must be valid if not dom.exists: logs.out("10", '', True) - if not target == "domain": dom.valid() + dom.valid() - do = False actions_read = ('show', 'show-about', 'show-db', 'show-wip', 'show-www') actions_edit = ('edit', 'edit-about', 'edit-db', 'edit-wip', 'edit-www') + post_src = post_db = post_wip = post_www = False - actions_about= ('show-about', 'edit-about') - actions_wip = ('show-wip', 'edit-wip') - actions_www = ('show-www', 'edit-www') - - actions_post = ('show', 'edit') - actions_db = ('show-db', 'edit-db') - - # Target is not a post uri (sidebar, navbar, metas, footer) - #---------------------------------------------------------- - if target in args.pass_targets: - if args.action in actions_post: - do = { - "domain" : dom.config, - "footer" : dom.footer_f, - "metas" : dom.metas_f, - "navbar" : dom.navbar_f, - "sidebar" : dom.sidebar_f - } - - elif args.action in actions_about: - do = { - "footer" : dom.footer_about_f - } - - elif args.action in actions_wip: - if target == 'stats': - stats.manage_stats('wip') - return + # Set file from post DB when target is an article + #------------------------------------------------ + try: post_src = db.uri_file + except: pass - do = { - "domain" : dom.config, - "footer" : dom.wip_footer_f, - "metas" : dom.wip_metas_f, - "navbar" : dom.wip_navbar_f, - "sidebar" : dom.wip_sidebar_f - } + if post_src: + target = "post" + try: post_db = db.config + except: pass - elif args.action in actions_www: - if target == 'stats': - stats.manage_stats('www') - return - - do = { - "domain" : dom.config, - "footer" : dom.www_footer_f, - "metas" : dom.www_metas_f, - "navbar" : dom.www_navbar_f, - "sidebar" : dom.www_sidebar_f - } - - # Target is a post uri - #--------------------- - elif db.uri_file: + try: post_wip = db.post_wip + except: pass - # Get hash when edit, and ask if file changed + try: post_www = db.post_www + except: pass + + # Except for show-about > Show post DB + if args.action == "show-about": + target = 'post' + + # When edit article, get src hash if args.action == "edit": curr_hash = tyto.get_filesum(db.uri_file, True) - - if args.action in actions_post: - target = "post" - do = {"post" : db.uri_file} - - # Post has database - elif db.exists: - do = { - "db" : db.config, - "wip" : db.post_wip, - "www" : db.post_www - } - if args.action in actions_wip: target = "wip" - elif args.action in actions_www: target = "www" - elif args.action in actions_db: target = "db" + + + # Convert command action to do[] + # as edit* &nd show* [action] have same target file + actions = \ + { + 'show' : 'src', + 'edit' : 'src', + 'post' : 'src', + 'show-db' : 'db', + 'edit-db' : 'db', + 'show-about' : 'about', + 'edit-about' : 'about', + 'show-wip' : 'wip', + 'edit-wip' : 'wip', + 'show-www' : 'www', + 'edit-www' : 'www' + } + action = actions[args.action] - #print('> show: target', target) - - if not do: - if not db.post: sys.exit(1) - - - # Read lines of, or edit file - if args.action in actions_read: read_lines(do[target]) - elif args.action in actions_edit: tyto.edit_file(do[target]) + # Set target file from "new" [action] + do = \ + { + 'src' : { + 'domain' : dom.config, + 'footer' : dom.footer_f, + 'metas' : dom.metas_f, + 'navbar' : dom.navbar_f, + 'sidebar' : dom.sidebar_f, + 'post' : post_src + }, + 'db' : { + 'domain' : dom.config, + 'footer' : dom.footer_f, + 'metas' : dom.metas_f, + 'navbar' : dom.navbar_f, + 'sidebar' : dom.sidebar_f, + 'post' : post_db + }, + 'about' : { + 'domain' : dom.config, + 'footer' : dom.footer_about_f, + 'metas' : dom.metas_f, + 'navbar' : dom.navbar_f, + 'sidebar' : dom.sidebar_f, + 'post' : post_db + }, + 'wip' : { + 'domain' : dom.config, + 'footer' : dom.wip_footer_f, + 'metas' : dom.wip_metas_f, + 'navbar' : dom.wip_navbar_f, + 'sidebar' : dom.wip_sidebar_f, + 'post' : post_wip + }, + 'www' : { + 'domain' : dom.config, + 'footer' : dom.www_footer_f, + 'metas' : dom.www_metas_f, + 'navbar' : dom.www_navbar_f, + 'sidebar' : dom.www_sidebar_f, + 'post' : post_www + }, + } - # After editing article, if change, ask to check again - if args.action == "edit" and not args.pass_targets: - new_hash = tyto.get_filesum(db.uri_file, True) + # Read or edit file, according to legacy args.action + try: + file = do[action][target] + if args.action in actions_read: + read_lines(file, True) + elif args.action in actions_edit: + tyto.edit_file(file) + except: + logs.out("28", '%s + %s'%(action, target), True) + + + # If edit article and hash changed, ask to check + if args.action == "edit" and post_src: + new_hash = tyto.get_filesum(post_src, True) if curr_hash != new_hash: ask = '' - try: ask = input('-> Check your changes ? ') + try: ask = input(' ├ %s%s '%(tr.post_chg, tr.q)) except KeyboardInterrupt: print('') logs.out("255", '', True) - if not ask in ['y', 'Y']: + if not ask in form.answer_yes: logs.out("255", '', True) # Reload post DB (if edited article, and check it if ask "y") importlib.reload(db) - check.manage_check(db.uri_file) - - -#============================================# -# Generic function to read lines from a file # -#--------------------------------------------# -def read_lines(f): - if not f: return # Maybe - if not tyto.exists(f): logs.out("1", f, True) + check.manage(post_src) - datas = open(f).read() - - # Align content line, after line number - ln_datas = len(datas.splitlines()) + 1 - sp_max = len(str(ln_datas)) - - for ln, line in enumerate(datas.rsplit('\n'), 1): - sp = sp_max - len(str(ln)) - print(' │ %s %s│ %s'%(ln, int(sp) * " ", line)) - - dom.valid() diff --git a/src/var/lib/tyto/program/tyto.py b/src/var/lib/tyto/program/tyto.py index 295f4cf..c3b01f2 100644 --- a/src/var/lib/tyto/program/tyto.py +++ b/src/var/lib/tyto/program/tyto.py @@ -135,14 +135,22 @@ words_tags = [ ('-(', '-)', '-(', '-)', 'lists', 't') ] +# Tags that do not need to be paired +#----------------------------------- +single_tags = [ +('|', '
'), # New Line +('->', '') # Anchors +] + # When counting words, do no count line starting with: nolinewords = \ ( -'((', '))', -'{{', ']]', -'{{', '}}', -'->', '|', -'_image:', '_taw:' +words_tags[10][0], words_tags[10][1], # paragraphs +words_tags[11][0], words_tags[11][1], # quotes +words_tags[12][0], words_tags[12][1], # bcodes +words_tags[13][0], words_tags[13][1], # lists +single_tags[0][0], single_tags[1][0], # New line, anchor +'_%s:'%opt_header_tags[1], '_%s:'%opt_header_tags[4] # _image:, _raw: ) # warning symbols (Check if paired) @@ -156,16 +164,9 @@ tpl_tags = [ ] -# Tags that do not need to be paired -#----------------------------------- -single_tags = [ -('|', '
'), # New Line -('->', '') # Anchors -] - # Markers for lists, to check in list content #-------------------------------------------- -markers_lists = ('+', '=', ' ') +markers_lists = ('+', '=', ' ', '#') # Tags used for titles #--------------------- @@ -335,23 +336,34 @@ def protect_bcodes_quotes(process, post_bottom): b64_quote = b64('Encode', quote, 'Q64.', '.Q64') line = b64_quote + ''' + # Remove coemments and empty lines for wip if not in_quote and not in_bcode: - if not line: continue + if not line: + continue if line.startswith('#') and not line.startswith(titles_tags): continue - + ''' # Counters and keep tags for check process #----------------------------------------- if process == "check": if in_bcode and not start_bcode \ or in_quote and not start_quote : - continue + line = '#-Protectedline-' # Set new article content for wip process #---------------------------------------- elif process == "wip": + # Remove empty line and comments + if not in_quote and not in_bcode: + if not line: + continue + elif line.startswith('#') \ + and not line.startswith(titles_tags): + continue + # bcode convertion to base64 if in_bcode: # Convert lines to b64 @@ -369,7 +381,6 @@ def protect_bcodes_quotes(process, post_bottom): #---------------- # check: remove quote/bcode, keep tags # wip: replace close tag with quote/bcode (keep in open tag) - if not line: continue if not protect_article: protect_article = line else: protect_article = '%s\n%s'%(protect_article, line) @@ -498,34 +509,43 @@ def replace_in_db(post_db, process, hash_post): # Copy files used by article to srv # #-----------------------------------# def files_to_srv(server): + import db for uri in db.uris: + + # Extract Directories from uri file + d_in = uri.split("/")[-1] + d_in = uri.rsplit(d_in)[0] + # Destination file to server - f_src = '%s%s'%(db.domain_articles, uri) - if server == 'wip': f_dst = '%s%s'%(db.srv_wip, uri) - elif server == 'www': f_dst = '%s%s'%(db.srv_www, uri) + f_src = '%s%s'%(dom.articles_d[:-1], uri) + if server == 'wip': + f_dst = '%s%s'%(dom.srv_wip[:-1], uri) + d_dst = '%s%s'%(dom.srv_wip[:-1], d_in) + elif server == 'www': + f_dst = '%s%s'%(dom.srv_www[:-1], uri) + d_dst = '%s%s'%(dom.srv_www[:-1], d_in) # Create folder and subfolders - f_uri = uri.split("/")[-1] - f_uri = uri.rsplit(f_uri)[0] try: - os.makedirs('%s%s'%(f_dst, f_uri), exist_ok=True) + os.makedirs(d_dst, exist_ok=True) + logs.out("33", d_dst, False) except: - logs.out('4', '%s%s'%(f_dst, f_uri), True) + logs.out('4', d_dst, True) try: shutil.copy2(f_src, f_dst) - logs.out("33", f_dst, False) + logs.out("32", f_dst, False) except: logs.out('4', f_dst, True) - if db.article_code: - if server == "wip": base_srv = db.srv_wip - elif server == "www": base_srv = db.srv_www + if dom.article_code: + if server == "wip": base_srv = dom.srv_wip + elif server == "www": base_srv = dom.srv_www f_dst = "%s%s"%(base_srv, db.short_src) try: shutil.copy2(db.post_src, f_dst) - logs.out("33", f_dst, False) + logs.out("32", f_dst, False) except: logs.out('4', f_dst, True) diff --git a/src/var/lib/tyto/program/wip.py b/src/var/lib/tyto/program/wip.py index 43ac736..6d84b9a 100644 --- a/src/var/lib/tyto/program/wip.py +++ b/src/var/lib/tyto/program/wip.py @@ -1,11 +1,30 @@ #!/usr/bin/env python3 -# Name: Tyto - Littérateur -# Type: Convert article to HTML -# Description: Converters from Source to HTML -# file: wip.py -# Folder: /var/lib/tyto/program/ -# By echolib (XMPP: im@echolib.re) -# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +# Tyto - Littérateur +# +# Copyright (C) 2023 Cyrille Louarn +# Copyright (C) 2023 Adrien Bourmault +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation, either version 3 of the License, or +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +#---------------------------------------------------------------------- +# XMPP: echolib (im@echolib.re) +# XMPP: neox (neox@a-lec.org) +# +# Description: Manage 'wip' argument +# Convert article to HTML from Tyto's format markers +# File: /var/lib/tyto/program/wip.py +#---------------------------------------------------------------------- #------------ # funny stats @@ -17,10 +36,36 @@ #********************************************************************** -import os, re, sys, shutil, importlib +import os, re, sys, locale, shutil, importlib, time from pathlib import Path -import args, logs, dom, db, tyto, html, form, stats +import args, logs, lang, dom, db, tyto, html, form, stats + +# load locale translation +trans_dir = '/var/lib/tyto/translations' +sys.path.insert(0, trans_dir) + +# System language +try: lang_sys = locale.getdefaultlocale()[0].split('_')[0] +except: lang_sys = 'en' + +# Get default system language +# or set "en" (english) if no translation file +try: + lang_site = lang_sys + os.path.exists('%s/site_%s.py'%(trans_dir, lang_site)) +except: + lang_site = 'en' + +# Set language site/form from configuration domain +# or set default english if not known +try: + dom.exists + lang_site = dom.lang_site + os.path.exists('%s/site_%s.py'%(trans_dir, lang_site)) + tr = importlib.import_module('site_%s'%lang_site, package=None) +except: + tr = importlib.import_module('site_%s'%lang_site, package=None) #=========================================# @@ -55,10 +100,10 @@ def manage_wip(target): # Per article with target #------------------------ # Exit with these conditions - if not target: logs.out("5", '', True) - if not db.post_exists: sys.exit(1) - if not db.db_exists: logs.out("25", db.uri_file, True) - if db.old_chk: logs.out("9", db.uri_file, True) + if not target: logs.out("5", '', True) + if not db.exists: sys.exit(1) + if not db.config: logs.out("25", db.uri_file, True) + if db.old_chk: logs.out("9", db.uri_file, True) # Article has changed or wip file missing if db.old_wip or not db.file_wip: @@ -68,12 +113,14 @@ def manage_wip(target): else: logs.out("19", db.date_wip, False) try: - res = input(' ├ Create new wip page ? ') + res = input(' ├ [%s] %s%s '%(db.title, tr.wip_new, tr.q)) except KeyboardInterrupt: print('') logs.out("255", '', True) - if not res in ['y', 'Y']: return + if not res in form.answer_yes: + logs.out("255", '', True) + wip_article(db.uri_file) @@ -85,7 +132,7 @@ def wip_all(process): tyto.process_all('Wip') # Sort by newer articles (created by last check) - db_articles = sorted(Path(db.articles_db).iterdir(), + db_articles = sorted(Path(db.articles_db_d).iterdir(), key=os.path.getmtime ) @@ -93,7 +140,7 @@ def wip_all(process): option = args.target found = False for post_db in db_articles: - if not str(post_db).endswith('.conf'): continue + if not str(post_db).endswith('.config'): continue # Load DB exec(open(post_db).read(),globals()) @@ -104,7 +151,7 @@ def wip_all(process): if option == "again" and db.old_wip: continue if option == "newer" and not db.old_wip: continue - print(':> [%s] - %s'%(db.title, db.post_src)) + print(' ├ [%s] > %s'%(db.title, db.post_src)) if db.old_chk: logs.out("9", '', False) continue @@ -127,14 +174,14 @@ def wip_article(target): global post_bottom # Protect block-codes and quotes - if db.stat_bcodes or db.stat_quotes > 0: + if db.post_bcodes or db.post_quotes > 0: tyto.protect_bcodes_quotes('wip', post_bottom) post_bottom = tyto.protect_article # Protect inline-codes - if db.stat_icodes > 0: + if db.post_icodes > 0: tyto.protect_icodes(post_bottom) - post_bottom = tyto.protect_article + post_bottom = tyto.protect_article # Convert contents from modules @@ -151,16 +198,17 @@ def wip_article(target): wip_tabs() # make HTML tabulations # Replace in DB hash_wip and date_wip - tyto.replace_in_db(db.post_db, 'wip', db.hash_post) + tyto.replace_in_db(db.config, 'wip', db.hash_post) # Get article DB in html.py html.set_page(db.uri_file, post_bottom) #print(html.main_page) # Create wip file - os.makedirs('%s%s'%(db.srv_wip, db.direc_src), exist_ok=True) + os.makedirs('%s%s'%(dom.srv_wip, db.direc_src), exist_ok=True) + logs.out("33", '%s%s'%(dom.srv_wip, db.direc_src), False) tyto.set_file(db.post_wip, 'New', html.main_page) - logs.out("33", db.post_wip, False) + logs.out("32", db.post_wip, False) # Copy needed files (Also create sub-folders) tyto.files_to_srv('wip') @@ -182,8 +230,9 @@ def file_to_string(post_file): continue if sep: - if not post_bottom: post_bottom = line - else: post_bottom = '%s\n%s'%(post_bottom, line) + if not line: continue + elif not post_bottom: post_bottom = line + else: post_bottom = '%s\n%s'%(post_bottom, line) else: if not post_header: post_header = line else: post_header = '%s\n%s'%(post_header, line) @@ -237,7 +286,7 @@ def wip_words_tags(): for ln, line in enumerate(post_bottom.rsplit('\n')): # Paragraphs # Open tag - if db.stat_paragraphs > 0: + if db.post_paragraphs > 0: if line.startswith(tyto.words_tags[10][0]): set_css = tyto.get_css(line) post_bottom = post_bottom.replace(post_bottom.rsplit('\n')[ln], @@ -249,7 +298,8 @@ def wip_words_tags(): tyto.words_tags[10][3] ) # Open anchors - if db.stat_anchors == 0: continue + if db.post_anchors == 0: continue + anchor_links = re.findall(r'>_(.+?):', line) for item in anchor_links: anchor_id = '%s%s:'%(tyto.words_tags[0][0], item) @@ -333,7 +383,7 @@ def wip_images(): for i in range(1, db.uniq_images + 1): image = 'db.image_%s'%i target = width = height = False - set_css = db.domain_css + '_image' + set_css = dom.css + '_image' imag_html = '' if eval(image)[0] == values[0]: @@ -401,7 +451,7 @@ def quote_params(qline): # Convert quote in article # #--------------------------# def wip_quotes() : - if db.stat_quotes == 0: return + if db.post_quotes == 0: return global post_bottom global author, link, lang, book, date @@ -545,7 +595,7 @@ def wip_quotes() : # Content is HTML ready # #--------------------------# def wip_icodes(): - if db.stat_icodes == 0: return + if db.post_icodes == 0: return global post_bottom @@ -561,7 +611,7 @@ def wip_icodes(): # Content is raw, and have to be converted in HTML # #--------------------------------------------------# def wip_bcodes(): - if db.stat_bcodes == 0: return + if db.post_bcodes == 0: return global post_bottom @@ -595,7 +645,7 @@ def wip_bcodes(): # Check between titles to set div or not # #----------------------------------------# def wip_titles(): - if db.stat_titles == 0: return + if db.post_titles == 0: return global post_bottom article_temp = post_bottom @@ -740,3 +790,94 @@ def wip_tabs(): post_bottom = article_temp + +#=================================# +# Convert list in markers to HTML # +# Def from neox <- Thx a lot # +#---------------------------------# +def convert_list(markdown_str): + + # First step : reshape lines + + items = [] + inside_item = 0 + index = -1 + + # Cut string with \n's + strlist = markdown_str.split("\n") + + # Find items + for i in range(len(strlist)): + if "-(" in strlist[i] or "-)" in strlist[i]: + continue + + if strlist[i][0] != "=" and strlist[i][0] != "+": + if inside_item != 1: + inside_item = 1 + else: + inside_item = 0 + + if inside_item == 0: + items.append(strlist[i]) + index += 1 + + if inside_item == 1: + items[index] += strlist[i].lstrip() + + #print("[{}] index {}, inside_item {}, curstr {}\n".format(i, index, inside_item, strlist[i])) + + # Second step : parsing + UL = 1 + OL = 2 + CLOSING = ["ERROR", "\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)): + if "-(" in items[i] or "-)" in items[i]: + continue + rank = cur_rank + descriptor = items[i].split(" ")[0] + text = items[i][items[i].find(" "):] + 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 - 1): + work_str += " "*(rank+i) + OPENING[rank_stack.append(UL)] + rank_stack.append(state) + + work_str += " "*rank + OPENING[state] + + # rank down + elif cur_rank < rank: + for i in range(rank - cur_rank - 1): + work_str += " "*(rank-i-1) + CLOSING[rank_stack.pop()] + + work_str += " "*cur_rank + CLOSING[rank_stack.pop()] + + + work_str += " "*cur_rank + "
    1. " + text + "
    2. \n" + + print("[{}] rank_stack {}, state {}, old_state {}, rank {}, cur_rank {}, text {}\n".format( + i, rank_stack, state, old_state, rank, cur_rank, text)) + + work_str += " "*(cur_rank-1) + CLOSING[rank_stack.pop()] + + return(work_str) + + +#print(convert_list(str_exemple)) diff --git a/src/var/lib/tyto/translations/site_en.py b/src/var/lib/tyto/translations/site_en.py index 23b83b8..163bb06 100644 --- a/src/var/lib/tyto/translations/site_en.py +++ b/src/var/lib/tyto/translations/site_en.py @@ -59,10 +59,15 @@ mail_to = "Contact by %s the admin of"%mail.lower() feed = "Feed" generator = "Generator" +# Check +post_chg = "Article was edited. Check it" + +# Wip +wip_new = "Create new HTML page in wip" + # Form #---------------------------------------------------------------------- form_edit = "Edit the domain with the form" - form_start = ' ├──────────────────────────────────────────────┐\n' + \ ' │ Configure a new domain for current directory │\n' + \ ' │ Answer Y/y = yes. Enter to keep {default} │\n' + \ diff --git a/src/var/lib/tyto/translations/site_fr.py b/src/var/lib/tyto/translations/site_fr.py index 783ea43..72051a6 100644 --- a/src/var/lib/tyto/translations/site_fr.py +++ b/src/var/lib/tyto/translations/site_fr.py @@ -59,6 +59,12 @@ mail_to = "Contacter par %s l'administrateur de"%mail.lower() feed = "Flux" generator = "Generateur" +# Check +post_chg = "Article édité. Le vérifier" + +# Wip +wip_new = "Créer une nouvelle page HTML dans wip" + # Formulaire #---------------------------------------------------------------------- form_edit = "Éditer le domaine avec le formulaire"