Updated for show, check, wip, html, databases

This commit is contained in:
Cyrille L 2023-03-30 17:53:55 +02:00
parent db7c1687a7
commit d08b96cf37
11 changed files with 678 additions and 709 deletions

View File

@ -2,17 +2,26 @@
Pour obtenir de l'aide, taper juste la commande tyto Pour obtenir de l'aide, taper juste la commande tyto
## Répertoire de code du projet Tyto ## Répertoire de code du projet Tyto
TODO
## Comment définir les métas ## Comment définir les métas
``` ```
# Obligatoires uniques # Obligatoires uniques
# Ces marqueurs se configurent sur UNE ligne
title: Titre title: Titre
about: Infos de l'article about: Infos de l'article
author: Auteur author: Auteur
tags: mots-clé-1,mots-clé-2 tags: mot-clé-1,mot 2,
date: YYYY-MM-DD (AAAA-MM-JJ) 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 # Optionnels multiples
# Ces marqueurs se configurent sur 3 lignes
link: Nom du lien link: Nom du lien
URL URL
Texte Alternatif Texte Alternatif
@ -33,10 +42,6 @@ abbr: abbrev
Définition de abbrev Définition de abbrev
ABBR (forme à afficher dans l'artile (optionnel)) 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 # Séparateur d'au moins 5 "-" pour définir la fin
# des métadonnées d'entête de l'article # des métadonnées d'entête de l'article
---------- ----------
@ -103,13 +108,14 @@ def hello_world():
### Ancres ### Ancres
``` ```
# Source` # Source de l'ancre cible. "id" est son identité
-> id -> id
# HTML # HTML
<a href="id2"></a> <a href="id2"></a>
# Source # Source de l'ancre d'appel
# Définir l'identité cible et le texte du lien
(( ((
>_id:Retourner au point d'ancre id_< >_id:Retourner au point d'ancre id_<
)) ))
@ -120,7 +126,11 @@ def hello_world():
### Retour à la ligne HTML ### Retour à la ligne HTML
``` ```
| # <br /> # Source
|
# HTML
<br />
``` ```
### Lien vers URL ### Lien vers URL
@ -136,7 +146,8 @@ Voir ce __Nom du lien+ # ouverture nouvelle fenêtre
``` ```
Note: 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... ### Gras, Strong, italique...
``` ```
@ -159,7 +170,7 @@ Vous pouvez avoir un Nom identique pour file: et link:
### Abréviations ### Abréviations
``` ```
# abbrev sera remplacé par "ABBR" dans la page si défini en entête # 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 : # - Toujours écrire dans l'article :
# - entre parenthèses ET majuscules les "(ABBREV)" # - 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 ### Code brut depuis un fichier
``` ```
_raw:NOM _raw:Nom
``` ```
### Citations ### Citations
Possibilité dans toute citation d'utiliser les marqueurs 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 # Source: citation complète
[[ CSS_TEST [[ CSS_TEST

View File

@ -58,6 +58,7 @@ pass_db = \
'edit-www', 'edit-www',
'publish', 'publish',
'show', 'show',
'show-about',
'show-db', 'show-db',
'show-wip', 'show-wip',
'show-www', 'show-www',

View File

@ -1,11 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Name: Tyto - Littérateur # Tyto - Littérateur
# Type: Global functions for check #
# Description: Check article contents. Create Stats and Database # Copyright (C) 2023 Cyrille Louarn <echolib@dismail.de>
# file: check.py #
# Folder: /var/lib/tyto/program/ # This program is free software: you can redistribute it and/or modify
# By echolib (XMPP: im@echolib.re) # it under the terms of the GNU Affero General Public License
# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 # 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 <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
# 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 # funny stats
@ -115,7 +132,7 @@ def check_all(option):
if option == 'all' and not db.old_chk: continue if option == 'all' and not db.old_chk: continue
found = True found = True
print(':> [%s] - %s'%(db.title, db.post_src)) print(' ├ [%s] > %s'%(db.title, db.post_src))
check_process(args.target) check_process(args.target)
if post_err: logs.out("44", args.target, False) 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): def if_icodes_bcodes_quotes(post_bottom):
global icode, quote, bcode, post_err 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 icode = quote = in_quote = bcode = in_bcode = in_bq = False
nbr_titles = nbr_quotes = nbr_bcodes = nbr_ancs = 0 nbr_titles = nbr_quotes = nbr_bcodes = nbr_ancs = nbr_coms = 0
for ln, line in enumerate(post_bottom.rsplit('\n'), 1): for ln, line in enumerate(post_bottom.rsplit('\n'), 1):
# Comments, count titles # Stat Comments, Titles, Anchors
if not line: if not line:
continue continue
elif line.startswith(tyto.titles_tags):
nbr_titles += 1
continue
elif line.startswith('#'):
continue
# quotes # quotes
elif line.startswith(tyto.words_tags[11][0]) and not in_bcode: elif line.startswith(tyto.words_tags[11][0]) and not in_bcode:
quote = in_quote = True quote = in_quote = in_bq = True
continue continue
elif line.startswith(tyto.words_tags[11][1]): elif line.startswith(tyto.words_tags[11][1]):
in_quote = False in_quote = False
@ -305,18 +317,25 @@ def if_icodes_bcodes_quotes(post_bottom):
# bcode # bcode
elif line.startswith(tyto.words_tags[12][0]) and not in_quote: elif line.startswith(tyto.words_tags[12][0]) and not in_quote:
bcode = in_bcode = True bcode = in_bcode = in_bq = True
continue continue
elif line.startswith(tyto.words_tags[12][1]): elif line.startswith(tyto.words_tags[12][1]):
in_bcode = False in_bcode = False
nbr_bcodes += 1 nbr_bcodes += 1
continue continue
if in_bcode or in_quote: if in_bq:
continue 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 nbr_ancs += 1
continue
# icodes # icodes
elif tyto.words_tags[9][0] or tyto.words_tags[9][1] in line: 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 # # Multiple settings for each on 3 lines #
#-------------------------------------------# #-------------------------------------------#
def check_opt_tags(post_header): def check_opt_tags(post_header):
global stat_links, stat_images, stat_files, stat_raws, stat_abbrs global stat_links, stat_images, stat_files, stat_raws
global stat_snpics, snpic_url global stat_snpics, snpic_url, stat_abbrs, post_abbrs
global opt_tags_post_name global opt_tags_post_name
global files_post global files_post
# Statistics # Statistics
stat_links = stat_images = stat_files = stat_raws = stat_abbrs = 0 stat_links = stat_images = stat_files = stat_raws = 0
stat_snpics = 0 stat_snpics = post_abbrs = stat_abbrs = 0
files_post = (()) files_post = (())
# Set default post pic # Set default post pic
@ -446,8 +465,9 @@ def check_opt_tags(post_header):
#--------------------------------------------# #--------------------------------------------#
def check_3lines(tag, ln, line): def check_3lines(tag, ln, line):
global post_err, db_tag, files_post 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 # Create variable for post DB
globals()[stat_tag] += 1 globals()[stat_tag] += 1
@ -462,8 +482,7 @@ def check_3lines(tag, ln, line):
name = line.rsplit('%s:'%tag)[1].lstrip() name = line.rsplit('%s:'%tag)[1].lstrip()
else: else:
name = line.rsplit('%s:'%tag)[1].lstrip().rsplit(' ')[0] name = line.rsplit('%s:'%tag)[1].lstrip().rsplit(' ')[0]
if tag == "abbr":
name = name.upper()
if not name: if not name:
logs.out("2", 'L=%s. "%s: %s" > %s'%( logs.out("2", 'L=%s. "%s: %s" > %s'%(
@ -471,6 +490,11 @@ def check_3lines(tag, ln, line):
), False) ), False)
post_err = True post_err = True
# abbr:
elif tag == "abbr":
name_abbr = name.upper()
post_abbrs = post_bottom.count('(%s)'%name_abbr)
# snpic only needs a Name # snpic only needs a Name
elif tag == "snpic": elif tag == "snpic":
check_snpic(name) check_snpic(name)
@ -482,7 +506,7 @@ def check_3lines(tag, ln, line):
), False) ), False)
post_err = True post_err = True
globals()[db_tag] = ((name),) globals()[db_tag] = ((opt_tags_post_name[tag]%name),)
# URI/URL and Alt-Text # URI/URL and Alt-Text
#--------------------- #---------------------
@ -509,9 +533,13 @@ def check_3lines(tag, ln, line):
# Set data for post DB # Set data for post DB
if l == 1 and tag in tyto.opt_tags_check_uri: if l == 1 and tag in tyto.opt_tags_check_uri:
check_file_uri(tag, data, ln) check_file_uri(tag, data, ln)
if post_err:
return
globals()[db_tag] = globals()[db_tag] + ((web_uri),) globals()[db_tag] = globals()[db_tag] + ((web_uri),)
files_post = files_post + (('%s'%web_uri),) files_post = files_post + (('%s'%web_uri),)
else: else:
if post_err:
return
globals()[db_tag] = globals()[db_tag] + ((data),) globals()[db_tag] = globals()[db_tag] + ((data),)
@ -603,78 +631,6 @@ def check_snpic(name):
def check_content(post_bottom): def check_content(post_bottom):
global post_err 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...) # Check tags for words (strongs, italics...)
# Set stats for each one # Set stats for each one
#------------------------------------------- #-------------------------------------------
@ -702,6 +658,85 @@ def check_content(post_bottom):
globals()['post_%s'%tag[4]] = int(c_opened) 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) # Template Tags (warning for not paired symbols)
#----------------------------------------------- #-----------------------------------------------
for tag in tyto.tpl_tags: for tag in tyto.tpl_tags:
@ -803,24 +838,26 @@ def create_database():
'uniq_files = %d\n'%stat_files + \ 'uniq_files = %d\n'%stat_files + \
'uniq_raws = %d\n'%stat_raws + \ 'uniq_raws = %d\n'%stat_raws + \
'\n# Statistics from post content\n' + \ '\n# Statistics from post content\n' + \
'stat_tags = %d\n'%stat_tags + \ 'post_coms = %d\n'%nbr_coms + \
'stat_lines = %d\n'%ln_article + \ 'post_tags = %d\n'%stat_tags + \
'stat_words = %d\n'%stat_words + \ 'post_lines = %d\n'%ln_article + \
'stat_titles = %d\n'%nbr_titles + \ 'post_words = %d\n'%stat_words + \
'stat_paragraphs = %d\n'%post_paragraphs + \ 'post_titles = %d\n'%nbr_titles + \
'stat_anchors = %d\n'%post_anchors + \ 'post_paragraphs = %d\n'%post_paragraphs + \
'stat_strongs = %d\n'%post_strongs + \ 'post_anchors = %d\n'%post_anchors + \
'stat_bolds = %d\n'%post_bolds + \ 'post_abbrs = %d\n'%post_abbrs + \
'stat_emphasis = %d\n'%post_emphasis + \ 'post_strongs = %d\n'%post_strongs + \
'stat_italics = %d\n'%post_italics + \ 'post_bolds = %d\n'%post_bolds + \
'stat_dels = %d\n'%post_dels + \ 'post_emphasis = %d\n'%post_emphasis + \
'stat_underlines = %d\n'%post_underlines + \ 'post_italics = %d\n'%post_italics + \
'stat_cites = %d\n'%post_cites + \ 'post_dels = %d\n'%post_dels + \
'stat_customs = %d\n'%post_customs + \ 'post_underlines = %d\n'%post_underlines + \
'stat_icodes = %d\n'%tyto.nbr_icodes + \ 'post_cites = %d\n'%post_cites + \
'stat_bcodes = %d\n'%nbr_bcodes + \ 'post_customs = %d\n'%post_customs + \
'stat_quotes = %d\n'%nbr_quotes + \ 'post_icodes = %d\n'%tyto.nbr_icodes + \
'stat_lists = %d\n'%post_lists 'post_bcodes = %d\n'%nbr_bcodes + \
'post_quotes = %d\n'%nbr_quotes + \
'post_lists = %d\n'%post_lists
# Create Post DB # Create Post DB
#--------------- #---------------
@ -828,336 +865,3 @@ def create_database():
tyto.set_file(db.config, 'new', database) tyto.set_file(db.config, 'new', database)
logs.out("21", db.uri_file, False) 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 = '<abbr class="%s" title="%s">%s</abbr>'%(
'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 = '<a class="%s" href="%s" '%('link', link_url) + \
'target="%s" title="%s">%s</a>'%(
'%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 = '<a class="%s" href=".%s" '%('file', web_uri) + \
'target="%s" title="%s">%s</a>'%(
'%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

View File

@ -89,23 +89,25 @@ if args.target \
'uniq_images', 'uniq_images',
'uniq_files', 'uniq_files',
'uniq_raws', 'uniq_raws',
'stat_tags', 'post_coms',
'stat_words', 'post_tags',
'stat_titles', 'post_words',
'stat_paragraphs', 'post_titles',
'stat_anchors', 'post_paragraphs',
'stat_strongs', 'post_anchors',
'stat_bolds', 'post_abbrs',
'stat_emphasis', 'post_strongs',
'stat_italics', 'post_bolds',
'stat_dels', 'post_emphasis',
'stat_underlines', 'post_italics',
'stat_cites', 'post_dels',
'stat_customs', 'post_underlines',
'stat_icodes', 'post_cites',
'stat_bcodes', 'post_customs',
'stat_quotes', 'post_icodes',
'stat_lists' 'post_bcodes',
'post_quotes',
'post_lists'
) )
for value in values: for value in values:

View File

@ -659,7 +659,7 @@ def create_domain(target):
except: active = False except: active = False
print(tr.form_warn) print(tr.form_warn)
show.read_lines(dom.config) show.read_lines(dom.config, False)
# Activate and prepare domain ? # Activate and prepare domain ?
#------------------------------ #------------------------------
@ -677,12 +677,10 @@ def create_domain(target):
#---------------- #----------------
tyto.set_file(dom.config, False, '\nactivated = True') tyto.set_file(dom.config, False, '\nactivated = True')
# RELoad config # RELoad config
#-------------- #--------------
importlib.reload(dom) importlib.reload(dom)
# Create folders from configuration file # Create folders from configuration file
#--------------------------------------- #---------------------------------------
folders = \ folders = \

View File

@ -1,11 +1,26 @@
#!/usr/bin/env python3 # Tyto - Littérateur
# Name: Tyto - Littérateur #
# Type: HTML template # Copyright (C) 2023 Cyrille Louarn <echolib@dismail.de>
# Description: Create raw HTML template #
# file: html.py # This program is free software: you can redistribute it and/or modify
# Folder: /var/lib/tyto/program/ # it under the terms of the GNU Affero General Public License
# By echolib (XMPP: im@echolib.re) # as published by the Free Software Foundation, either version 3 of the License, or
# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 # 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 <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
# XMPP: echolib (im@echolib.re)
#
# Description: Create HTML page
# File: /var/lib/tyto/program/show.py
#----------------------------------------------------------------------
#------------ #------------
# funny stats # funny stats
@ -19,7 +34,7 @@
import os, sys, importlib import os, sys, importlib
import logs, dom, tyto, form import logs, db, dom, tyto, form
# load locale translation # load locale translation
trans_dir = '/var/lib/tyto/translations' trans_dir = '/var/lib/tyto/translations'
@ -64,22 +79,22 @@ def create_metas_page():
#------------------- #-------------------
metas_page = '' metas_page = ''
scale = 'width=device-width, initial-scale=1.0' 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_file = 'styles.css'
css_ref = 'href="%stemplate/%s"'%(db.sub_uri, css_file) css_ref = 'href="%stemplate/%s"'%(db.sub_uri, css_file)
rss_ref = 'type="application/rss+xml" ' + \ rss_ref = 'type="application/rss+xml" ' + \
'href="%s%s" title="RSS 2.0. %s %s %s"'%( 'href="%s%s" title="RSS 2.0. %s %s %s"'%(
db.sub_uri, db.domain_rss, db.sub_uri, dom.rss,
db.domain_title, db.domain_sep, db.domain_short dom.title, dom.sep, dom.shortname
) )
icon_file = 'favicon.png' icon_file = 'favicon.png'
icon_ref = 'type="image/png" href="%stemplate/%s"'%( icon_ref = 'type="image/png" href="%stemplate/%s"'%(
db.sub_uri, icon_file db.sub_uri, icon_file
) )
relme = '' # External URL in metas (if exists in config domain) relme = '' # External URL in metas (if exists in config domain)
if db.domain_relme: if dom.relme:
relme = '\n <link rel="me" type="text/html" href="%s">'%( relme = '\n <link rel="me" type="text/html" href="%s">'%(
db.domain_relme dom.relme
) )
# Create author and date publish section infos # Create author and date publish section infos
@ -91,10 +106,10 @@ def create_metas_page():
metas = \ metas = \
'<!--# include virtual="/template/metas.html"-->\n' + \ '<!--# include virtual="/template/metas.html"-->\n' + \
' <meta name="viewport" content="%s" />\n'%scale + \ ' <meta name="viewport" content="%s" />\n'%scale + \
' <meta name=”url” content=”%s” />\n'%db.domain_www_url + \ ' <meta name=”url” content=”%s” />\n'%dom.www_url + \
' <meta name="language" content="%s" />\n'%db.domain_lang + \ ' <meta name="language" content="%s" />\n'%dom.lang_site + \
' <meta name="reply-to" content="%s" />\n'%db.domain_mail + \ ' <meta name="reply-to" content="%s" />\n'%dom.mail + \
' <meta name="copyright" content="%s" />\n'%db.domain_license + \ ' <meta name="copyright" content="%s" />\n'%dom.license + \
' <meta name="generator" content="%s" />\n'%tyto.Tyto + \ ' <meta name="generator" content="%s" />\n'%tyto.Tyto + \
' <meta name="title" content="%s" />\n'%db.title + \ ' <meta name="title" content="%s" />\n'%db.title + \
' <meta name="author" content="%s" />\n'%db.author + \ ' <meta name="author" content="%s" />\n'%db.author + \
@ -106,14 +121,14 @@ def create_metas_page():
' <link rel="stylesheet" %s />\n'%css_ref + \ ' <link rel="stylesheet" %s />\n'%css_ref + \
' <link rel="shortcut icon" %s />\n'%icon_ref + \ ' <link rel="shortcut icon" %s />\n'%icon_ref + \
' <!-- Open Graph data -->\n' + \ ' <!-- Open Graph data -->\n' + \
' <meta property="og:site_name" content="%s" />\n'%db.domain_title + \ ' <meta property="og:site_name" content="%s" />\n'%dom.title + \
' <meta property="og:title" content="%s" />\n'%db.title + \ ' <meta property="og:title" content="%s" />\n'%db.title + \
' <meta property="og:type" content="article" />\n' + \ ' <meta property="og:type" content="article" />\n' + \
' <meta property="og:url" content="%s" />\n'%db.http_www + \ ' <meta property="og:url" content="%s" />\n'%db.http_www + \
' <meta property="og:description" content="%s" />\n'%db.about + \ ' <meta property="og:description" content="%s" />\n'%db.about + \
' <meta property="og:image" content="%s" />\n'%db.snpic + \ ' <meta property="og:image" content="%s" />\n'%db.snpic + \
'%s'%relme + \ '%s'%relme + \
' <title>%s</title>'%db.title ' <title>%s %s %s</title>'%(db.title, dom.sep, dom.title)
#=======================================# #=======================================#
@ -122,23 +137,23 @@ def create_metas_page():
def create_main_page(target, article_bottom): def create_main_page(target, article_bottom):
global main_page global main_page
if not tyto.exists(db.wip_footer): if not tyto.exists(dom.wip_footer_f):
logs.out("1", db.wip_footer, True) logs.out("1", dom.wip_footer_f, True)
if not tyto.exists(db.wip_metas): if not tyto.exists(dom.wip_metas_f):
logs.out("24", '(HTML metas): %s'%db.wip_metas, False) logs.out("24", '(HTML metas): %s'%dom.wip_metas_f, False)
# Create link for website's logo # Create link for website's logo
#------------------------------- #-------------------------------
logo_html = '<a href="/"\n' + \ logo_html = '<a href="/"\n' + \
'%stitle="%s %s logo: %s"\n'%(11 * " ", '%stitle="%s %s logo: %s"\n'%(11 * " ",
tyto.trans[1][tyto.n], db.domain_sep, db.domain_title tyto.trans[1][tyto.n], dom.sep, dom.title
) + \ ) + \
'%sid="site_logo_link">\n'%(11 * " ") + \ '%sid="site_logo_link">\n'%(11 * " ") + \
'%s<img src="%stemplate/%s"\n'%( '%s<img src="%stemplate/%s"\n'%(
10 * " ", db.sub_uri, db.domain_logo 10 * " ", db.sub_uri, dom.logo
) + \ ) + \
'%salt="logo: %s"\n'%(15 * " ", db.domain_title) + \ '%salt="logo: %s"\n'%(15 * " ", dom.title) + \
'%sid="site_logo_image" />\n'%(15 * " ") + \ '%sid="site_logo_image" />\n'%(15 * " ") + \
'%s</a>'%(8 * " ") '%s</a>'%(8 * " ")
@ -147,7 +162,7 @@ def create_main_page(target, article_bottom):
#-----------------------# #-----------------------#
main_page = \ main_page = \
'<!Doctype html>\n' + \ '<!Doctype html>\n' + \
'<html lang="%s">\n'%db.domain_lang + \ '<html lang="%s">\n'%dom.lang_site + \
' <head>\n' + \ ' <head>\n' + \
'%s\n'%metas + \ '%s\n'%metas + \
' </head>\n\n' + \ ' </head>\n\n' + \
@ -161,9 +176,9 @@ def create_main_page(target, article_bottom):
' <a href="/"\n' + \ ' <a href="/"\n' + \
' title="%s"\n'%(tyto.trans[1][tyto.n]) + \ ' title="%s"\n'%(tyto.trans[1][tyto.n]) + \
' id="site_link">\n' + \ ' id="site_link">\n' + \
' <h1 id="site_title">%s</h1>\n'%db.domain_title + \ ' <h1 id="site_title">%s</h1>\n'%dom.title + \
' </a>\n' + \ ' </a>\n' + \
' <p id="site_about">%s</p>\n'%db.domain_about + \ ' <p id="site_about">%s</p>\n'%dom.about + \
' </div>\n' + \ ' </div>\n' + \
'<!--# include virtual="/template/navbar.html"-->\n' + \ '<!--# include virtual="/template/navbar.html"-->\n' + \
' </header>\n' + \ ' </header>\n' + \
@ -187,7 +202,7 @@ def create_main_page(target, article_bottom):
#--------------------------------------------# #--------------------------------------------#
def create_html_infos_section(process): def create_html_infos_section(process):
# Need to reload the DB to get last time updated # 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 global post_pub, meta_pub, date_raw
if process == 'wip': if process == 'wip':
@ -205,7 +220,7 @@ def create_html_infos_section(process):
# Show source code in article-infos if True in DB # Show source code in article-infos if True in DB
post_code = '' post_code = ''
if db.article_code: if dom.article_code:
# Set HTML # Set HTML
post_code = \ post_code = \
'<span id="article_code"> ' + \ '<span id="article_code"> ' + \
@ -213,7 +228,7 @@ def create_html_infos_section(process):
os.path.basename(db.short_src), os.path.basename(db.short_src),
tyto.trans[21][tyto.n], tyto.trans[21][tyto.n],
tyto.trans[3][tyto.n] tyto.trans[3][tyto.n]
) ) + \
' </span>' ' </span>'
@ -248,26 +263,24 @@ def create_html_infos_section(process):
# Create HTML sidebar from file tyto.sidebar # # Create HTML sidebar from file tyto.sidebar #
#--------------------------------------------# #--------------------------------------------#
def create_sidebar(option): def create_sidebar(option):
domain.domain_needed dom.valid()
try: try:
db.sidebar_load if not tyto.exists(dom.sidebar_f):
if not tyto.exists(db.sidebar_load): logs.out("1", dom.sidebar_f, True)
logs.out("1", db.sidebar_load, True)
except: except:
logs.out("1", 'Sidebar load file', True) logs.out("1", 'Sidebar ?', True)
try: try:
db.sidebar_items if int(dom.sidebar_items) > 16: db.sidebar_items = 6
if int(db.sidebar_items) > 16: db.sidebar_items = 6
except: except:
db.sidebar_items = 6 db.sidebar_items = 6
pub_opts = ('www', 'pub') pub_opts = ('www', 'pub')
if option == 'wip': target = db.wip_sidebar if option == 'wip': target = dom.wip_sidebar_f
elif option == 'www': target = db.www_sidebar elif option == 'www': target = dom.www_sidebar_f
elif option == 'pub': target = db.www_sidebar elif option == 'pub': target = dom.www_sidebar_f
# If content in sidebar, go True # If content in sidebar, go True
sidebar_new = False sidebar_new = False
@ -285,8 +298,8 @@ def create_sidebar(option):
counter = 0 counter = 0
nolines = ('#', '/') nolines = ('#', '/')
sidebar_title = db.sidebar_title sidebar_title = dom.sidebar_title
sidebar_lines = open(db.sidebar_load, 'r').read() sidebar_lines = open(dom.sidebar_f, 'r').read()
for line in sidebar_lines.rsplit('\n'): for line in sidebar_lines.rsplit('\n'):
if not line or line.startswith(nolines): continue if not line or line.startswith(nolines): continue
@ -296,14 +309,14 @@ def create_sidebar(option):
# Get full article URI and check if exists # Get full article URI and check if exists
sidebar_has = True 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): if not tyto.exists(f_uri):
logs.out("24", f_uri, False) logs.out("24", f_uri, False)
continue continue
# Get Hash from uri to get db file # Get Hash from uri to get db file
hash_uri = tyto.get_filesum(f_uri, False) 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): if not tyto.exists(db_uri):
logs.out('25', line, False) logs.out('25', line, False)
continue continue
@ -332,9 +345,9 @@ def create_sidebar(option):
# Count: no more than max configured items # Count: no more than max configured items
counter += 1 counter += 1
if counter > db.sidebar_items: if counter > dom.sidebar_items:
logs.out("31", '(%s): %s "%s"'%( logs.out("31", '(%s): %s "%s"'%(
db.sidebar_items, line, title), dom.sidebar_items, line, title),
False False
) )
continue continue

View File

@ -1,11 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Name: Tyto - Littérateur # Tyto - Littérateur
# Type: Command arguments 'show', 'showdb' manager #
# Description: manage 'show' and 'showdb' from command action argument # Copyright (C) 2023 Cyrille Louarn <echolib@dismail.de>
# file: show.py #
# Folder: /var/lib/tyto/program/ # This program is free software: you can redistribute it and/or modify
# By echolib (XMPP: im@echolib.re) # it under the terms of the GNU Affero General Public License
# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 # 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 <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
# 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 # 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 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: # # From command line: #
# - 'showdb' / 'show' # # - 'showdb' / 'show' #
@ -28,130 +100,126 @@ import args, lang, logs, dom, db, form, tyto, check, stats
# final html, db, load # # final html, db, load #
#----------------------# #----------------------#
def manage(target): def manage(target):
# Domain must be valid
if not dom.exists: logs.out("10", '', True) 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_read = ('show', 'show-about', 'show-db', 'show-wip', 'show-www')
actions_edit = ('edit', 'edit-about', 'edit-db', 'edit-wip', 'edit-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') # Set file from post DB when target is an article
actions_wip = ('show-wip', 'edit-wip') #------------------------------------------------
actions_www = ('show-www', 'edit-www') try: post_src = db.uri_file
except: pass
actions_post = ('show', 'edit') if post_src:
actions_db = ('show-db', 'edit-db') target = "post"
try: post_db = db.config
except: pass
# Target is not a post uri (sidebar, navbar, metas, footer) try: post_wip = db.post_wip
#---------------------------------------------------------- except: pass
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: try: post_www = db.post_www
do = { except: pass
"footer" : dom.footer_about_f
}
elif args.action in actions_wip: # Except for show-about > Show post DB
if target == 'stats': if args.action == "show-about":
stats.manage_stats('wip') target = 'post'
return
do = { # When edit article, get src hash
"domain" : dom.config,
"footer" : dom.wip_footer_f,
"metas" : dom.wip_metas_f,
"navbar" : dom.wip_navbar_f,
"sidebar" : dom.wip_sidebar_f
}
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:
# Get hash when edit, and ask if file changed
if args.action == "edit": if args.action == "edit":
curr_hash = tyto.get_filesum(db.uri_file, True) 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 # Convert command action to do[]
elif db.exists: # as edit* &nd show* [action] have same target file
do = { actions = \
"db" : db.config, {
"wip" : db.post_wip, 'show' : 'src',
"www" : db.post_www 'edit' : 'src',
} 'post' : 'src',
if args.action in actions_wip: target = "wip" 'show-db' : 'db',
elif args.action in actions_www: target = "www" 'edit-db' : 'db',
elif args.action in actions_db: target = "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) # 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
},
}
if not do: # Read or edit file, according to legacy args.action
if not db.post: sys.exit(1) 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)
# Read lines of, or edit file # If edit article and hash changed, ask to check
if args.action in actions_read: read_lines(do[target]) if args.action == "edit" and post_src:
elif args.action in actions_edit: tyto.edit_file(do[target]) new_hash = tyto.get_filesum(post_src, True)
# 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)
if curr_hash != new_hash: if curr_hash != new_hash:
ask = '' ask = ''
try: ask = input('-> Check your changes ? ') try: ask = input('%s%s '%(tr.post_chg, tr.q))
except KeyboardInterrupt: except KeyboardInterrupt:
print('') print('')
logs.out("255", '', True) logs.out("255", '', True)
if not ask in ['y', 'Y']: if not ask in form.answer_yes:
logs.out("255", '', True) logs.out("255", '', True)
# Reload post DB (if edited article, and check it if ask "y") # Reload post DB (if edited article, and check it if ask "y")
importlib.reload(db) importlib.reload(db)
check.manage_check(db.uri_file) check.manage(post_src)
#============================================#
# 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)
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()

View File

@ -135,14 +135,22 @@ words_tags = [
('-(', '-)', '-(', '-)', 'lists', 't') ('-(', '-)', '-(', '-)', 'lists', 't')
] ]
# Tags that do not need to be paired
#-----------------------------------
single_tags = [
('|', '<br />'), # New Line
('->', '<a class="anchor_target" id="%s"></a>') # Anchors
]
# When counting words, do no count line starting with: # When counting words, do no count line starting with:
nolinewords = \ nolinewords = \
( (
'((', '))', 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
'_image:', '_taw:' 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) # warning symbols (Check if paired)
@ -156,16 +164,9 @@ tpl_tags = [
] ]
# Tags that do not need to be paired
#-----------------------------------
single_tags = [
('|', '<br />'), # New Line
('->', '<a class="anchor_target" id="%s"></a>') # Anchors
]
# Markers for lists, to check in list content # Markers for lists, to check in list content
#-------------------------------------------- #--------------------------------------------
markers_lists = ('+', '=', ' ') markers_lists = ('+', '=', ' ', '#')
# Tags used for titles # Tags used for titles
#--------------------- #---------------------
@ -335,23 +336,34 @@ def protect_bcodes_quotes(process, post_bottom):
b64_quote = b64('Encode', quote, 'Q64.', '.Q64') b64_quote = b64('Encode', quote, 'Q64.', '.Q64')
line = b64_quote line = b64_quote
'''
# Remove coemments and empty lines for wip
if not in_quote and not in_bcode: 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): if line.startswith('#') and not line.startswith(titles_tags):
continue continue
'''
# Counters and keep tags for check process # Counters and keep tags for check process
#----------------------------------------- #-----------------------------------------
if process == "check": if process == "check":
if in_bcode and not start_bcode \ if in_bcode and not start_bcode \
or in_quote and not start_quote : or in_quote and not start_quote :
continue line = '#-Protectedline-'
# Set new article content for wip process # Set new article content for wip process
#---------------------------------------- #----------------------------------------
elif process == "wip": 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 # bcode convertion to base64
if in_bcode: if in_bcode:
# Convert lines to b64 # Convert lines to b64
@ -369,7 +381,6 @@ def protect_bcodes_quotes(process, post_bottom):
#---------------- #----------------
# check: remove quote/bcode, keep tags # check: remove quote/bcode, keep tags
# wip: replace close tag with quote/bcode (keep in open tag) # wip: replace close tag with quote/bcode (keep in open tag)
if not line: continue
if not protect_article: protect_article = line if not protect_article: protect_article = line
else: protect_article = '%s\n%s'%(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 # # Copy files used by article to srv #
#-----------------------------------# #-----------------------------------#
def files_to_srv(server): def files_to_srv(server):
import db
for uri in db.uris: 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 # Destination file to server
f_src = '%s%s'%(db.domain_articles, uri) f_src = '%s%s'%(dom.articles_d[:-1], uri)
if server == 'wip': f_dst = '%s%s'%(db.srv_wip, uri) if server == 'wip':
elif server == 'www': f_dst = '%s%s'%(db.srv_www, uri) 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 # Create folder and subfolders
f_uri = uri.split("/")[-1]
f_uri = uri.rsplit(f_uri)[0]
try: 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: except:
logs.out('4', '%s%s'%(f_dst, f_uri), True) logs.out('4', d_dst, True)
try: try:
shutil.copy2(f_src, f_dst) shutil.copy2(f_src, f_dst)
logs.out("33", f_dst, False) logs.out("32", f_dst, False)
except: except:
logs.out('4', f_dst, True) logs.out('4', f_dst, True)
if db.article_code: if dom.article_code:
if server == "wip": base_srv = db.srv_wip if server == "wip": base_srv = dom.srv_wip
elif server == "www": base_srv = db.srv_www elif server == "www": base_srv = dom.srv_www
f_dst = "%s%s"%(base_srv, db.short_src) f_dst = "%s%s"%(base_srv, db.short_src)
try: try:
shutil.copy2(db.post_src, f_dst) shutil.copy2(db.post_src, f_dst)
logs.out("33", f_dst, False) logs.out("32", f_dst, False)
except: except:
logs.out('4', f_dst, True) logs.out('4', f_dst, True)

View File

@ -1,11 +1,30 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Name: Tyto - Littérateur # Tyto - Littérateur
# Type: Convert article to HTML #
# Description: Converters from Source to HTML # Copyright (C) 2023 Cyrille Louarn <echolib@dismail.de>
# file: wip.py # Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
# Folder: /var/lib/tyto/program/ #
# By echolib (XMPP: im@echolib.re) # This program is free software: you can redistribute it and/or modify
# License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 # 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 <https://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
# 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 # funny stats
@ -17,10 +36,36 @@
#********************************************************************** #**********************************************************************
import os, re, sys, shutil, importlib import os, re, sys, locale, shutil, importlib, time
from pathlib import Path 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 # Per article with target
#------------------------ #------------------------
# Exit with these conditions # Exit with these conditions
if not target: logs.out("5", '', True) if not target: logs.out("5", '', True)
if not db.post_exists: sys.exit(1) if not db.exists: sys.exit(1)
if not db.db_exists: logs.out("25", db.uri_file, True) if not db.config: logs.out("25", db.uri_file, True)
if db.old_chk: logs.out("9", db.uri_file, True) if db.old_chk: logs.out("9", db.uri_file, True)
# Article has changed or wip file missing # Article has changed or wip file missing
if db.old_wip or not db.file_wip: if db.old_wip or not db.file_wip:
@ -68,12 +113,14 @@ def manage_wip(target):
else: else:
logs.out("19", db.date_wip, False) logs.out("19", db.date_wip, False)
try: try:
res = input('Create new wip page ? ') res = input('[%s] %s%s '%(db.title, tr.wip_new, tr.q))
except KeyboardInterrupt: except KeyboardInterrupt:
print('') print('')
logs.out("255", '', True) 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) wip_article(db.uri_file)
@ -85,7 +132,7 @@ def wip_all(process):
tyto.process_all('Wip') tyto.process_all('Wip')
# Sort by newer articles (created by last check) # 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 key=os.path.getmtime
) )
@ -93,7 +140,7 @@ def wip_all(process):
option = args.target option = args.target
found = False found = False
for post_db in db_articles: for post_db in db_articles:
if not str(post_db).endswith('.conf'): continue if not str(post_db).endswith('.config'): continue
# Load DB # Load DB
exec(open(post_db).read(),globals()) exec(open(post_db).read(),globals())
@ -104,7 +151,7 @@ def wip_all(process):
if option == "again" and db.old_wip: continue if option == "again" and db.old_wip: continue
if option == "newer" and not 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: if db.old_chk:
logs.out("9", '', False) logs.out("9", '', False)
continue continue
@ -127,14 +174,14 @@ def wip_article(target):
global post_bottom global post_bottom
# Protect block-codes and quotes # 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) tyto.protect_bcodes_quotes('wip', post_bottom)
post_bottom = tyto.protect_article post_bottom = tyto.protect_article
# Protect inline-codes # Protect inline-codes
if db.stat_icodes > 0: if db.post_icodes > 0:
tyto.protect_icodes(post_bottom) tyto.protect_icodes(post_bottom)
post_bottom = tyto.protect_article post_bottom = tyto.protect_article
# Convert contents from modules # Convert contents from modules
@ -151,16 +198,17 @@ def wip_article(target):
wip_tabs() # make HTML tabulations wip_tabs() # make HTML tabulations
# Replace in DB hash_wip and date_wip # 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 # Get article DB in html.py
html.set_page(db.uri_file, post_bottom) html.set_page(db.uri_file, post_bottom)
#print(html.main_page) #print(html.main_page)
# Create wip file # 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) 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) # Copy needed files (Also create sub-folders)
tyto.files_to_srv('wip') tyto.files_to_srv('wip')
@ -182,8 +230,9 @@ def file_to_string(post_file):
continue continue
if sep: if sep:
if not post_bottom: post_bottom = line if not line: continue
else: post_bottom = '%s\n%s'%(post_bottom, line) elif not post_bottom: post_bottom = line
else: post_bottom = '%s\n%s'%(post_bottom, line)
else: else:
if not post_header: post_header = line if not post_header: post_header = line
else: post_header = '%s\n%s'%(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')): for ln, line in enumerate(post_bottom.rsplit('\n')):
# Paragraphs # Paragraphs
# Open tag # Open tag
if db.stat_paragraphs > 0: if db.post_paragraphs > 0:
if line.startswith(tyto.words_tags[10][0]): if line.startswith(tyto.words_tags[10][0]):
set_css = tyto.get_css(line) set_css = tyto.get_css(line)
post_bottom = post_bottom.replace(post_bottom.rsplit('\n')[ln], post_bottom = post_bottom.replace(post_bottom.rsplit('\n')[ln],
@ -249,7 +298,8 @@ def wip_words_tags():
tyto.words_tags[10][3] tyto.words_tags[10][3]
) )
# Open anchors # Open anchors
if db.stat_anchors == 0: continue if db.post_anchors == 0: continue
anchor_links = re.findall(r'>_(.+?):', line) anchor_links = re.findall(r'>_(.+?):', line)
for item in anchor_links: for item in anchor_links:
anchor_id = '%s%s:'%(tyto.words_tags[0][0], item) 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): for i in range(1, db.uniq_images + 1):
image = 'db.image_%s'%i image = 'db.image_%s'%i
target = width = height = False target = width = height = False
set_css = db.domain_css + '_image' set_css = dom.css + '_image'
imag_html = '' imag_html = ''
if eval(image)[0] == values[0]: if eval(image)[0] == values[0]:
@ -401,7 +451,7 @@ def quote_params(qline):
# Convert quote in article # # Convert quote in article #
#--------------------------# #--------------------------#
def wip_quotes() : def wip_quotes() :
if db.stat_quotes == 0: return if db.post_quotes == 0: return
global post_bottom global post_bottom
global author, link, lang, book, date global author, link, lang, book, date
@ -545,7 +595,7 @@ def wip_quotes() :
# Content is HTML ready # # Content is HTML ready #
#--------------------------# #--------------------------#
def wip_icodes(): def wip_icodes():
if db.stat_icodes == 0: return if db.post_icodes == 0: return
global post_bottom global post_bottom
@ -561,7 +611,7 @@ def wip_icodes():
# Content is raw, and have to be converted in HTML # # Content is raw, and have to be converted in HTML #
#--------------------------------------------------# #--------------------------------------------------#
def wip_bcodes(): def wip_bcodes():
if db.stat_bcodes == 0: return if db.post_bcodes == 0: return
global post_bottom global post_bottom
@ -595,7 +645,7 @@ def wip_bcodes():
# Check between titles to set div or not # # Check between titles to set div or not #
#----------------------------------------# #----------------------------------------#
def wip_titles(): def wip_titles():
if db.stat_titles == 0: return if db.post_titles == 0: return
global post_bottom global post_bottom
article_temp = post_bottom article_temp = post_bottom
@ -740,3 +790,94 @@ def wip_tabs():
post_bottom = article_temp 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", "</ul>\n", "</ol>\n"]
OPENING = ["ERROR", "<ul>\n", "<ol>\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 + "<li>" + text + "</li>\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))

View File

@ -59,10 +59,15 @@ mail_to = "Contact by %s the admin of"%mail.lower()
feed = "Feed" feed = "Feed"
generator = "Generator" generator = "Generator"
# Check
post_chg = "Article was edited. Check it"
# Wip
wip_new = "Create new HTML page in wip"
# Form # Form
#---------------------------------------------------------------------- #----------------------------------------------------------------------
form_edit = "Edit the domain with the form" form_edit = "Edit the domain with the form"
form_start = ' ├──────────────────────────────────────────────┐\n' + \ form_start = ' ├──────────────────────────────────────────────┐\n' + \
' │ Configure a new domain for current directory │\n' + \ ' │ Configure a new domain for current directory │\n' + \
' │ Answer Y/y = yes. Enter to keep {default}\n' + \ ' │ Answer Y/y = yes. Enter to keep {default}\n' + \

View File

@ -59,6 +59,12 @@ mail_to = "Contacter par %s l'administrateur de"%mail.lower()
feed = "Flux" feed = "Flux"
generator = "Generateur" generator = "Generateur"
# Check
post_chg = "Article édité. Le vérifier"
# Wip
wip_new = "Créer une nouvelle page HTML dans wip"
# Formulaire # Formulaire
#---------------------------------------------------------------------- #----------------------------------------------------------------------
form_edit = "Éditer le domaine avec le formulaire" form_edit = "Éditer le domaine avec le formulaire"