diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ab09b1..8b247ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,22 @@ Tyto - Littérateur
# CURRENTLY IN DEV (in devel branch) !
+## [1.9.39]
+- fix datepub in modules when "check domain"
+- add Tyto - Littérateur generator links at HTML footer creation
+- fix settings from command line options
+- add "RSS" and "sitemap" links to default footer (not yet processed)
+- add command "set 'MODULE_NAME'" to create default module (header, footer...)
+- Create again all HTML modules with option -F, --force
+- With option -E, --errors: only show warning and error logs
+- Avoid creating a domain in a directory domain set yet
+- better modules managed in "wip" process
+- Added logs file (include ALL logs) in /var/log/tyto
+- - current.log for current session only
+- - archive.log for all sessions
+- Sitemap (! In dev)
+- - Create sitemaps in root and sub-directories
+
## [1.9.38]
- Moved: HTML footer in div site_container
- fix: main title in HTML page h1
diff --git a/README.md b/README.md
index b971008..1d83be2 100644
--- a/README.md
+++ b/README.md
@@ -216,7 +216,7 @@ abbr: css
#2 Une liste mixée {_
, _}
((
<: mylist
- + numeric o: item 1
+ + numeric ol item 1
++ numeric ol sub-Item 1
+++ numeric ol sub-sub-item 1
==== ul item >_top: Go to Top_<
@@ -325,7 +325,7 @@ Here, i am
Une liste mixée <ol>, <ul>
- - numeric o: item 1
+ - numeric ol item 1
- numeric ol sub-Item 1
diff --git a/debian/control b/debian/control
index 50e74c9..ab58014 100644
--- a/debian/control
+++ b/debian/control
@@ -1,5 +1,5 @@
Package: tyto
-Version: 1.9.38
+Version: 1.9.39
Section: custom
Priority: optional
Architecture: all
diff --git a/src/usr/bin/tyto b/src/usr/bin/tyto
index aabcf33..583cb87 100755
--- a/src/usr/bin/tyto
+++ b/src/usr/bin/tyto
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Version: 1.9.38
+# Version: 1.9.39
# Updated: 2023-11-11 1699742831
# Tyto - Littérateur
@@ -41,7 +41,7 @@
# file program :
#--------------------------
-import os
+import os, sys
#===============#
@@ -56,8 +56,8 @@ def error_message(path):
# A little checker to be sure, all files are installed #
#------------------------------------------------------#
def check_install():
- if not os.path.exists(libs): error_message(libs)
- if not os.path.exists(trfs): error_message(trfs)
+ os.path.exists(libs) or error_message(libs)
+ os.path.exists(trfs) or error_message(trfs)
for f in prog_files:
f = os.path.join(libs, f + ".py")
@@ -75,7 +75,6 @@ def check_install():
#======#=======================================================================
# MAIN #
#======#
-import sys
if not __name__ == "__main__":
print("! Error: '%s' not '%s'"%(__name__, "__main__"))
sys.exit(1)
diff --git a/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc
index 0e76276..db3b35c 100644
Binary files a/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/args.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc
index 21b5b06..631844e 100644
Binary files a/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/check.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc
index 9f03236..3bfa598 100644
Binary files a/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/debug.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc
index ebbefbe..d58e1d1 100644
Binary files a/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/domain.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc
new file mode 100644
index 0000000..4e4dd43
Binary files /dev/null and b/src/var/lib/tyto/program/__pycache__/logs.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc
index 7a26788..f94cad6 100644
Binary files a/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/new.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc
index e6a8ccd..4ce594f 100644
Binary files a/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/post.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc
new file mode 100644
index 0000000..6ea26d4
Binary files /dev/null and b/src/var/lib/tyto/program/__pycache__/sitemaps.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc
index 6e9b21d..cd6f46a 100644
Binary files a/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/tools.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc
index 7fec1f0..dec9a6c 100644
Binary files a/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/tyto.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc
index 08a9d41..3bba342 100644
Binary files a/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/userset.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc b/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc
index 13fe706..8020c89 100644
Binary files a/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc and b/src/var/lib/tyto/program/__pycache__/wip.cpython-311.pyc differ
diff --git a/src/var/lib/tyto/program/args.py b/src/var/lib/tyto/program/args.py
index 525884c..01f331b 100644
--- a/src/var/lib/tyto/program/args.py
+++ b/src/var/lib/tyto/program/args.py
@@ -59,15 +59,21 @@ def get_target():
#================================#
# Searching options in arguments #
+# Done only once #
#--------------------------------#
def get_options():
- global dlogs, force, erron
+ global dlogs, force, erron, set_options
+
+ try: set_options ; return
+ except: pass
dlogs = force = erron = False
for arg in range(1, len(sys.argv)):
- dlogs = sys.argv[arg] in tyto.debug_options
- force = sys.argv[arg] in tyto.force_options
- erron = sys.argv[arg] in tyto.debug_errors
+ if sys.argv[arg] in tyto.debug_options: dlogs = True
+ if sys.argv[arg] in tyto.force_options: force = True
+ if sys.argv[arg] in tyto.debug_errors: erron = True
+
+ set_options = True
#===========#
@@ -75,6 +81,7 @@ def get_options():
#-----------#
def valid_action():
global action
+
if not action in tyto.actions:
debug.out(1, "[action]", action, False, 2, False)
action = "help"
@@ -93,14 +100,14 @@ def start_process():
valid_action()
do = {
- "help" : help.show,
- "check" : check.manage,
- "new" : new.manage,
- "set" : userset.manage,
- "start" : userset.manage,
- "stop" : userset.manage,
- "show" : show.manage,
- "wip" : wip.manage,
+ "help" : help.show,
+ "check" : check.manage,
+ "new" : new.manage,
+ "set" : userset.manage,
+ "start" : userset.manage,
+ "stop" : userset.manage,
+ "show" : show.manage,
+ "wip" : wip.manage,
}
do[action](action, target)
diff --git a/src/var/lib/tyto/program/check.py b/src/var/lib/tyto/program/check.py
index b6dd512..e8c2740 100644
--- a/src/var/lib/tyto/program/check.py
+++ b/src/var/lib/tyto/program/check.py
@@ -46,8 +46,8 @@ import args, domain, langs, debug, post, tools, tyto, wip
# Domain must be activated #
#-------------------------------------#
def ready():
- domain.cf_update_values(False)
- domain.ready()
+ domain.cf_update_values(False)
+ domain.ready()
#===========================================#
@@ -67,6 +67,7 @@ def manage(action, target):
target == "domain" and wrk_domain()
target in ("wip", "www") and srv_domain(target)
+
#==========================================#
# Check some things used by current domain #
#------------------------------------------#
@@ -80,23 +81,43 @@ def wrk_domain():
#==========================================================#
# Warn if unused registred file in wrk template/ directory #
+# Done only once #
+# - in case of "all" or direct "wip" #
#----------------------------------------------------------#
def wrk_template_files():
+ global check_tpl_files
+
+ try: check_tpl_files ; return
+ except: pass
+
for key, uri in domain.cf.items("USER_TEMPLATE_FILES"):
if not os.path.exists(uri):
debug.out(5, key, uri, True, 1, False)
+ check_tpl_files = True
+
-#==================================#
-# Check wip and www dirs and files #
-#----------------------------------#
+#====================================#
+# Check wip and www dirs and files #
+# Done only once #
+# - in case of "all" or direct "wip" #
+#------------------------------------#
def srv_domain(target):
+ global check_srv_domain
+
+ try: check_srv_domain ; return
+ except: pass
+
for section in ("%s_DIRS"%target.upper(), "%s_FILES"%target.upper()):
+ # Set err number for directories or files
if section.endswith("DIRS"): err = 6
else: err = 5
+
for key, uri in domain.cf.items(section):
if not os.path.exists(uri):
debug.out(err, key, uri, True, 1, False)
+
+ check_srv_domain = True
#================================#
@@ -111,7 +132,7 @@ def is_article(target):
# When all is OK
# Will create post database, but now, show some values
"""
- print("Final texts string:")
+ print("> check: Final texts string...")
print('\n'.join(texts))
"""
@@ -126,7 +147,7 @@ def is_article(target):
def valid(target):
global targets
targets = args.targets
-
+
# Target is a tyto article format
if not post.is_article(target):
return False
@@ -192,6 +213,7 @@ def valid(target):
# Head contents
# =============
# One Line targs in head_contents
+ debug.out(215, "check. Headers", post.cf_uri, False, 0, False)
post.error == 0 and ol_tags() \
or tools.exit(targets, post.error)
# Multiple and optional Tags on 3 linges
@@ -253,7 +275,7 @@ def ol_tags():
global stats_total_files
stats_total_files = 0
- sitemap = "True"
+ post.cf_set("HEADERS", "sitemap", "True")
stats_tyto_head_coms = 0
for ln, line in enumerate(headers, 1):
@@ -614,6 +636,8 @@ def is_value2_file_exists(ln, tag, val2):
def sl_ptags(markers):
global texts
+ debug.out(215, "check. %s"%markers[2], post.cf_uri, False, 0, False)
+
convert = {
"bcodes" : wip.bcode,
"quotes" : wip.quote,
@@ -736,6 +760,8 @@ def sl_ptags(markers):
def icodes():
global texts
+ debug.out(215, "check. icodes", post.cf_uri, False, 0, False)
+
stats_text_icodes = 0
markers = post.words_markers
@@ -853,6 +879,8 @@ def icodes():
def sl_stags():
global anchors_ids, stats_tyto_text_coms
+ debug.out(215, "check. Contents", post.cf_uri, False, 0, False)
+
anchors_ids = () # Uniq anchors IDs
stats_tyto_text_coms = stats_html_coms = 0
stats_text_anc_ids = 0
@@ -871,7 +899,9 @@ def sl_stags():
# Avoid wanting #6 - #9 (but accept #1x.. #5x.. as comments...)
elif linels[1].isdigit() and int(linels[1]) >= 6:
post.error = \
- debug.out(52, "%s) %s..."%(ln, linels[0:10]), post.uri, True, 1, False)
+ debug.out(52, "%s) %s..."%(
+ ln, linels[0:10]
+ ), post.uri, True, 1, False)
return False
stats_titles += 1
diff --git a/src/var/lib/tyto/program/debug.py b/src/var/lib/tyto/program/debug.py
index 3587bbd..2bd8f2c 100644
--- a/src/var/lib/tyto/program/debug.py
+++ b/src/var/lib/tyto/program/debug.py
@@ -33,43 +33,19 @@
#--------------------------
import sys
-import langs, args
+import langs, args, logs
-#===================================#
-# Show all logs if "show" is True #
-# or with -D argument #
-# color is for "*" in message #
-# - 3 colors levels: #
-# - - 0 = Green #
-# - - 1 = Yellow #
-# - - 2 = Red #
-# stop to sys exit with nbr if True #
-#-----------------------------------#
-def out(nbr, var, val, show, color, stop):
- args.get_options()
+
+#===================#
+# Messages for logs #
+#-------------------#
+def set_messages():
+ global messages, got_messages
- logit = show or args.erron and color > 0 or args.dlogs
- if not logit:
- return nbr
+ try: got_messages ; return
+ except: pass
- # COlors
- CS = '\033[0;0m' # Unset
- CL = '\033[0;2m' # Gray
- CB = '\033[1;34m' # Blue
- CC = '\033[1;36m' # Cyan
- CR = '\033[1;31m' # Red
- CG = '\033[1;32m' # Green
- CY = '\033[1;33m' # Yellow
- CP = '\033[1;35m' # Pink
-
- # Color of "*"
- SC = CL # Default gray
- if color == 0: SC = CG
- elif color == 1: SC = CY
- elif color == 2: SC = CR
-
- # Messages for logs
messages = \
{
# ERRORS (1-100)
@@ -83,6 +59,8 @@ def out(nbr, var, val, show, color, stop):
8 : langs.logs.err_lang,
9 : langs.logs.err_ini_file,
10 : langs.logs.err_post_global,
+ 11 : langs.logs.err_post_not_chk,
+ 13 : langs.logs.err_post_not_www,
20 : langs.logs.err_bad_uri,
21 : langs.logs.err_post_sep,
22 : langs.logs.err_post_head,
@@ -114,10 +92,59 @@ def out(nbr, var, val, show, color, stop):
208 : langs.logs.website_lang,
209 : langs.logs.domain_on,
210 : langs.logs.post_chk_yet,
+ 211 : langs.logs.checking_post,
+ 212 : langs.logs.wipping_post,
+ 214 : langs.logs.reading_domain,
+ 215 : langs.logs.processing,
+ 216 : langs.logs.create_sitemaps,
+ 250 : langs.logs.completed,
254 : langs.logs.post_chk_ready,
255 : langs.logs.later,
}
+ got_messages = True
+
+
+#===================================#
+# Show all logs if "show" is True #
+# or with -D argument #
+# color is for "*" in message #
+# - 3 colors levels: #
+# - - 0 = Green #
+# - - 1 = Yellow #
+# - - 2 = Red #
+# stop to sys exit with nbr if True #
+#-----------------------------------#
+def out(nbr, var, val, show, color, stop):
+ set_messages()
+
+ logs.add_line(nbr, messages[nbr], var, val)
+
+ args.get_options()
+ logit = show or args.erron and color > 0 or args.dlogs
+
+ if args.erron:
+ if color == 0:
+ logit = False
+
+ if not logit:
+ return nbr
+
+ # COlors
+ CS = '\033[0;0m' # Unset
+ CL = '\033[0;2m' # Gray
+ CB = '\033[1;34m' # Blue
+ CC = '\033[1;36m' # Cyan
+ CR = '\033[1;31m' # Red
+ CG = '\033[1;32m' # Green
+ CY = '\033[1;33m' # Yellow
+ CP = '\033[1;35m' # Pink
+
+ # Color of "*"
+ SC = CL # Default gray
+ if color == 0: SC = CG
+ elif color == 1: SC = CY
+ elif color == 2: SC = CR
# Print, acoording to parameters
print("%s*%s %s%s%s > %s%s%s < %s%s%s"%(
diff --git a/src/var/lib/tyto/program/domain.py b/src/var/lib/tyto/program/domain.py
index ae6295f..4fe74f7 100644
--- a/src/var/lib/tyto/program/domain.py
+++ b/src/var/lib/tyto/program/domain.py
@@ -56,6 +56,7 @@ def cf_load():
cf = False
cf = configparser.ConfigParser()
cf.read(cf_uri)
+ debug.out(200, langs.logs.domain, cf_uri, False, 0, False)
#=====================================#
@@ -221,6 +222,7 @@ def cf_set(section, key, default):
def cf_update_values(write):
# Load Domain Configuration file
cf_load()
+ debug.out(214, "...", cf_uri, False, 0, False)
# [DOMAIN]
# ========
diff --git a/src/var/lib/tyto/program/logs.py b/src/var/lib/tyto/program/logs.py
new file mode 100644
index 0000000..15a3acd
--- /dev/null
+++ b/src/var/lib/tyto/program/logs.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+
+# Copyright (C) 2023 Cyrille Louarn
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib > im@echolib.re
+#
+# Description: Manage logs files in /var/log/tyto/
+# File: /var/lib/tyto/program/logs.py
+#----------------------------------------------------------------------
+
+#-------------------------
+# Funny Stats Project
+#-------------------------
+# file lines :
+# file comments :
+# file functions:
+# file program :
+#--------------------------
+
+import os, sys, datetime
+
+# 2 Files for logs
+f_current = "/var/log/tyto/current.log" # Last session
+f_archive = "/var/log/tyto/archive.log" # All sessions
+
+
+#==============================#
+# Set and return date and time #
+#------------------------------#
+def nowdate():
+ return(datetime.datetime.utcnow().isoformat())
+
+
+#===================================#
+# Create logs files (if not exists) #
+#-----------------------------------#
+def create_files():
+ global error_write, check
+
+ # Process done.
+ # In error case or files exist, not need to create again
+ try: check ; return
+ except: pass
+
+ # Process could not write file, no need to continue
+ try: error_write ; return
+ except: pass
+
+ CS = '\033[0;0m' # Unset
+ CL = '\033[0;2m' # Gray
+ CR = '\033[1;31m' # Red
+
+ for f in (f_current, f_archive):
+ if not os.path.exists(f):
+ try:
+ with open(f, "w") as f:
+ f.write("")
+ except:
+ print("%s!%s %sUnused%s %s"%(
+ CR,CS, CL,CS, f )
+ )
+ error_write = True
+
+ check = True
+
+
+#=====================================#
+# Create current.log for each session #
+#
+def add_line(nbr, message, var, val):
+ global first_log
+
+ # Create logs files (if not exists)
+ create_files()
+
+ # In error case (unused log file), return
+ try: error_write ; return
+ except: pass
+
+ # Merge current logs in archive logs (done only at first log)
+ archive_current_logs()
+
+ # At first log only, add command line to current logs
+ try:
+ first_log
+ except:
+ first_log = True
+ # Get command line
+ command = ""
+ for arg in range(1, len(sys.argv)):
+ command = command + sys.argv[arg] + " "
+
+ with open(f_current, "w") as f:
+ f.write("[%s] $ tyto %s"%(nowdate(), command))
+
+ # Create line for file
+ n_spaces = 3 - len(str(nbr))
+ with open(f_current, "a") as f:
+ f.write("\n[%s] %s%s => %s > %s < %s"%(
+ nowdate(), nbr, (n_spaces * " "), message, var, val
+ )
+ )
+
+
+#==================================#
+# Merge current.log to archive.log #
+# Done only once, at first log #
+#----------------------------------#
+def archive_current_logs():
+ global merge_done
+
+ try: merge_done ; return
+ except: pass
+
+ # Load current logs file lines
+ current_logs = open(f_current, "r").read()
+
+ # No need to append empty current logs file
+ if current_logs:
+ # Append current logs file to archive logs
+ with open(f_archive, "a") as f:
+ f.write("%s\n\n"%(current_logs))
+
+ merge_done = True
+
diff --git a/src/var/lib/tyto/program/new.py b/src/var/lib/tyto/program/new.py
index d3aace2..558aa6c 100644
--- a/src/var/lib/tyto/program/new.py
+++ b/src/var/lib/tyto/program/new.py
@@ -32,7 +32,8 @@
# file program :
#--------------------------
-import args, domain
+import os
+import args, domain, debug, sitemaps
#====================================#
@@ -41,7 +42,8 @@ import args, domain
#------------------------------------#
def manage(action, target):
do = {
- "domain" : create_domain,
+ "domain" : create_domain,
+ "sitemaps" : sitemaps.manage,
}
do[target]()
@@ -54,8 +56,18 @@ def manage(action, target):
# or if user "force" option
#-----------------------------------#
def create_domain():
- if not domain.cf_exists() or args.force:
- domain.cf_create()
- return
+ # Check if in a domain set yet
+ d_uri = "/"
+ dirs = domain.user_dir.rsplit("/")
+ for d in dirs:
+ if not d:
+ continue
+
+ d_uri = d_uri + d + "/"
+ if os.path.exists(d_uri + "tyto_domain.ini"):
+ debug.out(202, d, d_uri, True, 2, True)
+
+ domain.cf_create()
+
diff --git a/src/var/lib/tyto/program/post.py b/src/var/lib/tyto/program/post.py
index ac28397..d8f2405 100644
--- a/src/var/lib/tyto/program/post.py
+++ b/src/var/lib/tyto/program/post.py
@@ -48,6 +48,8 @@ write = False # When updating database in cf_set(), cf_write()
#--------------------------------------------#
def is_article(target):
global error
+
+ debug.out(211, '"%s"'%target, "", False, 0, False)
# User MUST be in articles/
domain.user_dir.startswith(domain.wrk_articles) or \
@@ -146,41 +148,47 @@ def is_tyto_format():
# return True, or False if unused (yet) #
#---------------------------------------#
def cf_load():
- global cf
- cf = False
+ global cf, not_chk
+ cf = not_chk = False
- os.path.exists(cf_uri) or tools.create_file(cf_uri, ini_template)
+
+ if not os.path.exists(cf_uri):
+ not_chk = True
+ tools.create_file(cf_uri, ini_template)
cf = configparser.ConfigParser()
cf.read(cf_uri)
+ debug.out(200, uri_id, cf_uri, False, 0, False)
return True
#======================================#
# Load another post configuration file #
+# Used by modules: navbar, sidebar #
#--------------------------------------#
-def tmp_load(wrk_post, wrk_post_uri):
+def tmp_load(module, wrk_post, wrk_post_uri):
global error
db_uri = os.path.join(
domain.wrk_db,
- tools.get_filesum(wrk_post_uri, False) + ".ini"
+ tools.get_filesum(wrk_post_uri, False) + ".ini"
)
if not os.path.exists(db_uri):
error = \
- debug.out(5, wrk_post, db_uri, True, 2, False)
+ debug.out(11, '%s. "%s"'%(module, wrk_post), db_uri, True, 2, False)
return False
global tmp_cf
tmp_cf = ""
tmp_cf = configparser.ConfigParser()
tmp_cf.read(db_uri)
+ debug.out(200, '%s. "%s"'%(module, wrk_post), db_uri, False, 0, False)
if tmp_cf.getboolean("CHECK", "errors"):
error = \
- debug.out(10, "True", db_uri, True, 2, False)
+ debug.out(10, "check: %s"%langs.logs.error, db_uri, True, 2, False)
return False
global tmp_title, tmp_about, tmp_logo
@@ -208,6 +216,55 @@ def tmp_load(wrk_post, wrk_post_uri):
return True
+#=======================================#
+# Load in tmp mode db post for sitemaps #
+#---------------------------------------#
+def tmp_load_db(article_uri):
+ global tmp_db, tmp_db_error
+ tmp_db_error = False
+
+ target = article_uri.rsplit(domain.wrk_articles)[1]
+ db_uri = domain.wrk_db + tools.get_filesum(article_uri, False) + ".ini"
+ if not os.path.exists(db_uri):
+ debug.out(11, 'Sitemap. "%s"'%target, db_uri, False, 0, False)
+ return False
+
+ tmp_db = ""
+ tmp_db = configparser.ConfigParser()
+ tmp_db.read(db_uri)
+ debug.out(200, 'Sitemap. "%s"'%target, db_uri, False, 0, False)
+
+ if not tmp_db_item("HEADERS", "sitemap", True):
+ tmp_db_error = True
+ return True
+
+ # Must be published to be included in sitemap
+ www_uri = tmp_db_item("WWW", "uri", False)
+ if not tmp_db_item("WWW", "hash", False):
+ tmp_db_error = True
+ debug.out(13, 'Sitemap. "%s"'%target, www_uri, False, 1, False)
+ return True
+
+ # Article is not in www server
+ if not os.path.exists(www_uri):
+ debug.out(5, 'Sitemap. "www/"', www_uri, False, 1, False)
+ tmp_db_error = True
+
+ return True
+
+#===============================================#
+# Get DB value (for sitemap, with tmp_load_db() #
+#-----------------------------------------------#
+def tmp_db_item(section, key, boolean):
+ if boolean:
+ try: return tmp_db.getboolean(section, key)
+ except: return False
+
+ try: return tmp_db.get(section, key)
+ except: return ""
+
+
+
#================================#
# Return value from section, key #
#--------------------------------#
@@ -325,6 +382,7 @@ def cf_write():
if write:
with open(cf_uri, "w") as f:
cf.write(f)
+ debug.out(207, uri_id, cf_uri, False, 0, False)
write = False
@@ -336,7 +394,7 @@ def find_tyto_article():
nothere = (domain.wrk_files, domain.wrk_images)
os.chdir(domain.wrk_articles)
- for root,dirs,files in os.walk(domain.wrk_articles):
+ for root, dirs, files in os.walk(domain.wrk_articles):
if root.startswith(nothere):
continue
diff --git a/src/var/lib/tyto/program/sitemaps.py b/src/var/lib/tyto/program/sitemaps.py
new file mode 100644
index 0000000..570e3f5
--- /dev/null
+++ b/src/var/lib/tyto/program/sitemaps.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python3
+# Tyto - Littérateur
+
+# Copyright (C) 2023 Cyrille Louarn
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+#----------------------------------------------------------------------
+# XMPP: echolib > im@echolib.re
+#
+# Description: Create sitemaps
+# File: /var/lib/tyto/program/sitemaps.py
+#----------------------------------------------------------------------
+
+#-------------------------
+# Funny Stats Project
+#-------------------------
+# file lines :
+# file comments :
+# file functions:
+# file program :
+#--------------------------
+
+import os
+
+import args, debug, domain, post, langs, tools, check, wip
+
+
+sitemap_post = """# %s Tyto - Littérateur
+! NoSitemap
+
+title: %s (%s)
+about: %s (%s)
+tags: %s
+author: Tyto
+date: %s
+
+-----
+#1 %s. %s%s "%s"
+
+(( sitemap
+ <: sitemap-items
+%s
+ :>
+))
+
+#1 %s
+
+(( sitemap
+ <: sitemap-items
+%s
+ :>
+))
+
+"""
+
+sitemap_post_empty = """ %s Tyto - Littérateur
+! NoSitemap
+
+title: %s (%s)
+about: %s (%s)
+tags: %s
+author: Tyto
+date: %s
+
+-----
+
+(( sitemap
+ %s
+))
+
+"""
+
+# Generic link for sitemap
+link = ' %s %s%s'
+
+
+#===========================#
+# Manage sitemaps creations #
+#---------------------------#
+def manage():
+ domain.cf_update_values(False)
+ domain.ready()
+
+ if not domain.sitemaps:
+ return
+
+ global nowdate
+ nowdate = tools.nowdate().rsplit()[0]
+
+ debug.out(216, "sitemap.tyto", "sitemap.html", False, 0, False)
+
+ # Create sitemap for root and sub-directories
+ create_by_dir(domain.wrk_articles)
+ create_by_subdir()
+
+
+#====================================================#
+# Search in specific path directory #
+# for Tyto articles, and filter them by newest first #
+#---------------------------------------------------#
+def articles_sorted(path):
+ os.chdir(path)
+ files = filter(os.path.isfile, os.listdir(path))
+ files = [os.path.join(path, f) for f in files if f.endswith(".tyto")]
+ files.sort(key=lambda t: os.path.getmtime(t), reverse=True)
+
+ return files
+
+#=======================#
+# Return post DB values #
+#-----------------------#
+def set_db_values():
+ return post.tmp_db_item("FILE", "target", False), \
+ post.tmp_db_item("FILE", "web", False), \
+ post.tmp_db_item("HEADERS", "title", False), \
+ post.tmp_db_item("HEADERS", "about", False), \
+ post.tmp_db_item("HEADERS", "date", False), \
+ post.tmp_db_item("HEADERS", "authors", False)
+
+
+#=========================================================#
+# Main function to make sitemap.tyto and sitemap.html #
+# List Tyto articles by newest in each directory #
+# Create 2 links lists (only if a valid article is added) #
+# 1. directories pointing to sitemap.html
+# 2. articles
+# ...
+#
+def create_by_dir(path):
+ target_dir = path.rsplit(domain.wrk_articles[:-1])[1]
+ target_slashes = target_dir.count("/")
+ curr_root = root = list_pages_links = list_posts_links = ""
+
+
+ has_article = False
+ for root, dirs, files in os.walk(path):
+ articles = articles_sorted(root)
+
+ for article in articles:
+ has_article = True
+ if curr_root != root:
+ curr_root = root
+ add_page_link = True
+ else:
+ add_page_link = False
+
+ # Load post DB (if exists)
+ post.tmp_db_error = False
+ if not post.tmp_load_db(article) or post.tmp_db_error:
+ curr_root = ""
+ continue
+
+ target, web, title, about, date, authors = set_db_values()
+ web_slashes = web.count("/")
+
+ # Add page link (pointing to sitemap.html)
+ # Add only one same link if an article exists
+ if add_page_link:
+ # For sub directories, mark "="
+ # is the différence with web target and root domain
+ if path != domain.wrk_articles:
+ web_slashes = web_slashes - target_slashes
+
+ if list_pages_links: nl1 = "\n"
+ else: nl1 = ""
+ list_pages_links = \
+ list_pages_links + nl1 + link%(
+ "=" * web_slashes,
+ domain.css, "%ssitemap.html"%web,
+ "%s%s %s"%(
+ langs.site.sitemap, langs.logs.pp, web
+ ), "Tyto", "", web
+ )
+
+ # Add article link
+ if list_posts_links: nl2 = "\n"
+ else: nl2 = ""
+ list_posts_links = \
+ list_posts_links + nl2 + link%(
+ "=" * web_slashes,
+ domain.css, web, about, authors, date + ", ", title
+ )
+
+ # A sitemap is created if a Tyto article file is found
+ if not list_posts_links and not has_article:
+ return
+
+ if not list_posts_links:
+ root_sitemap = \
+ sitemap_post_empty%(
+ langs.site.sitemap_gen,
+ langs.site.sitemap, target_dir,
+ langs.site.sitemap, target_dir,
+ langs.site.sitemap,
+ nowdate,
+ langs.site.sitemap_empty
+ )
+
+ else:
+ root_sitemap = \
+ sitemap_post%(
+ langs.site.sitemap_gen,
+ langs.site.sitemap, target_dir,
+ langs.site.sitemap, target_dir,
+ langs.site.sitemap,
+ nowdate,
+ langs.site.sitemap, langs.site.directory, langs.logs.pp, target_dir,
+ list_pages_links,
+ langs.site.posts_list,
+ list_posts_links,
+ )
+
+ #print(root_sitemap)
+ if path != domain.wrk_articles: target_file = "/sitemap.tyto"
+ else: target_file = "sitemap.tyto"
+ target_file = target_dir[1:] + target_file
+ #print(">> target_f", target_file)
+
+ file_uri = domain.wrk_articles + target_file
+ #print(">> file_uri", file_uri)
+
+ os.chdir(domain.wrk_articles)
+
+ args.targets = "all"
+ tools.create_file(file_uri, root_sitemap)
+ wip.is_article(target_file)
+
+
+#==========================================================#
+# Loop from all sub directories from root domain articles/ #
+# For each, create_by_dir() sitemaps #
+#
+def create_by_subdir():
+ for root, dirs, files in os.walk(domain.wrk_articles):
+ for d in dirs:
+ dir_uri = os.path.join(root,d)
+ create_by_dir(dir_uri)
+
+
+
diff --git a/src/var/lib/tyto/program/tools.py b/src/var/lib/tyto/program/tools.py
index 827eff3..bfda232 100644
--- a/src/var/lib/tyto/program/tools.py
+++ b/src/var/lib/tyto/program/tools.py
@@ -109,13 +109,13 @@ def dir_exists(dir_path, out):
# Create directories #
#--------------------#
def create_dirs(path):
- try:
- if not os.path.exists(path):
+ if not os.path.exists(path):
+ try:
os.makedirs(path, exist_ok=True)
- debug.out(203, "True", path, False, 0, False)
- except:
- # Exit if not created
- debug.out(6, "False", path, True, 2, True)
+ debug.out(203, langs.logs.success, path, True, 0, False)
+ except:
+ # Exit if not created
+ debug.out(6, langs.logs.error, path, True, 2, True)
#============================#
@@ -128,7 +128,7 @@ def create_file(file_path, contents):
f.write(contents)
except:
# Exit if not created
- debug.out(7, "False", file_path, True, 2, True)
+ debug.out(7, langs.logs.error, file_path, True, 2, True)
# log "update" or "new"
file_name = os.path.basename(file_path)
diff --git a/src/var/lib/tyto/program/tyto.py b/src/var/lib/tyto/program/tyto.py
index 8aab998..db24bf0 100644
--- a/src/var/lib/tyto/program/tyto.py
+++ b/src/var/lib/tyto/program/tyto.py
@@ -41,6 +41,8 @@ targets = (
"domains",
"title", "date", "about", "mail", "tags", "lang", "server",
"all",
+ "metas", "header", "navbar", "sidebar", "footer",
+ "sitemaps",
)
force_options = ("--force", "-F")
@@ -53,7 +55,7 @@ debug_errors = ('--errors', "-E")
#
full_name = "Tyto - Littérateur"
web_url = "https://tyto.echolib.re"
-git_url = "https://git.a-lec.org/echolib/tyto-litterateur"
+git_url = "https://forge.a-lec.org/echolib/tyto-litterateur"
#=============================================================#
diff --git a/src/var/lib/tyto/program/userset.py b/src/var/lib/tyto/program/userset.py
index ede333d..4372122 100644
--- a/src/var/lib/tyto/program/userset.py
+++ b/src/var/lib/tyto/program/userset.py
@@ -32,7 +32,7 @@
# file program :
#--------------------------
-import langs, forms, domain
+import langs, forms, domain, wip
#====================================#
@@ -45,16 +45,22 @@ def manage(action, target):
if action == "set":
do = {
- "title" : forms.ask_domain_title,
- "date" : forms.ask_domain_date,
- "about" : forms.ask_domain_about,
- "mail" : forms.ask_domain_mail,
- "lang" : forms.ask_domain_lang,
- "server" : forms.ask_domain_server,
+ "title" : forms.ask_domain_title,
+ "date" : forms.ask_domain_date,
+ "about" : forms.ask_domain_about,
+ "mail" : forms.ask_domain_mail,
+ "lang" : forms.ask_domain_lang,
+ "server" : forms.ask_domain_server,
+ "metas" : wip.create_default_module,
+ "header" : wip.create_default_module,
+ "navbar" : wip.create_default_module,
+ "sidebar" : wip.create_default_module,
+ "footer" : wip.create_default_module,
}
- do[target](True)
+ try: do[target](target)
+ except: pass
elif action in ("start", "stop") and target == "domain":
domain.userset_status(action)
-
+
diff --git a/src/var/lib/tyto/program/wip.py b/src/var/lib/tyto/program/wip.py
index 167ebf1..770f9dd 100644
--- a/src/var/lib/tyto/program/wip.py
+++ b/src/var/lib/tyto/program/wip.py
@@ -92,6 +92,13 @@ def is_article(target):
# Copy article files
create_files()
+
+ post.cf_set("WIP", "hash", post.wrk_id)
+ post.cf_set("WIP", "date", datepub)
+ post.cf_write()
+
+ debug.out(250, langs.logs.success, post.wip_uri, True, 0, False)
+
#print()
#print(html_post)
@@ -143,7 +150,18 @@ def page_html_create():
#========================================#
# Create and copy files for this article #
#----------------------------------------#
-def create_files():
+def create_files():
+ # Create source post wip dirs and html file
+ wip_dirs = \
+ post.wip_uri.rsplit(os.path.basename(post.wip_uri))[0] or domain.wip
+ tools.create_dirs(wip_dirs)
+ tools.create_file(post.wip_uri, page_html)
+
+ # Check/Create modules files
+ # navbar, sidebar, header, footer
+ if not get_modules("wip"):
+ return False
+
# Copy files used by article
for key, src_uri in post.cf.items("SOURCE_FILES"):
dst_uri = src_uri.replace(domain.wrk_articles, domain.wip)
@@ -152,12 +170,6 @@ def create_files():
tools.create_dirs(wip_dirs)
tools.copy_files(src_uri, dst_uri)
-
- # Create source post wip dirs and html file
- wip_dirs = \
- post.wip_uri.rsplit(os.path.basename(post.wip_uri))[0] or domain.wip
- tools.create_dirs(wip_dirs)
- tools.create_file(post.wip_uri, page_html)
# Copy domain work template directory
shutil.copytree(domain.wrk_tpl,
@@ -182,7 +194,7 @@ def convert(target):
# Target is a tyto article format
if not post.is_article(target):
return False
-
+
datepub = tools.nowdate()
# Check/create wip server directories
@@ -191,20 +203,20 @@ def convert(target):
tools.create_dirs(directory)
check.wrk_template_files()
-
- # Check/Create modules files
- # navbar, sidebar, header, footer
- if not get_modules("wip"):
- return False
+
+ debug.out(212, target, post.wip_uri, False, 0, False)
# Article has errors or changed : needs check first
if post.has_changed or post.chk_errors:
- if post.has_changed: debug.out(106, "'check'", post.uri, True, 1,False)
- elif post.chk_errors: debug.out(10, "'check'", post.uri, True, 2, False)
+ if post.chk_errors: debug.out(10, "'check'", post.uri, True, 2, False)
+ elif post.not_chk: debug.out(11, "'check'", post.uri, True, 1, False)
+ elif post.has_changed: debug.out(106, "'check'", post.uri, True, 1, False)
+
args.force = True
check.is_article(target)
if post.error != 0:
return False
+
is_article(target)
return
@@ -232,6 +244,7 @@ def convert(target):
value_replace("CODES", False, True)
footer_post_create()
+
return True
@@ -252,6 +265,8 @@ def text_replace(src, html):
# tgt_b64 : True to decode base64 target # #
#------------------------------------------#
def value_replace(section, src_b64, tgt_b64):
+ debug.out(215, "wip. %s"%section.lower(), post.cf_uri, False, 0, False)
+
for key, val in post.cf.items(section):
if key.startswith("html_"):
continue
@@ -333,6 +348,7 @@ def sl_tags():
elif line.lstrip().startswith(post.html_brline[0]):
css = tools.get_css(line, post.html_brline[0][0], "?")
text_replace(html_post.rsplit("\n")[ln], post.html_brline[1]%css)
+
#
elif line.lstrip().startswith(post.html_hrline[0]):
css = tools.get_css(line, post.html_hrline[0][0], "?")
@@ -704,6 +720,27 @@ def footer_post_create():
#=========#
# Modules #====================================================================
#---------#
+#============================#
+# With command "set [module] #
+# remove if any [module] #
+# Create default [module] #
+#----------------------------#
+def create_default_module(target):
+ domain.cf_update_values(False)
+ domain.ready()
+
+ files = {
+ "metas" : domain.wrk_metas,
+ "header" : domain.wrk_header,
+ "navbar" : domain.wrk_navbar,
+ "sidebar" : domain.wrk_sidebar,
+ "footer" : domain.wrk_footer
+ }
+
+ os.path.exists(files[target]) and os.remove(files[target])
+ get_modules("wip")
+
+
#==================================#
# Create modules ini database file #
# return False if: #
@@ -731,7 +768,10 @@ def modules_db():
# - footer #
#--------------------------------------------#
def get_modules(srv):
- global modules
+ global modules, datepub
+
+ try: datepub
+ except: datepub = tools.nowdate()
# Modules settings
fill_footer_raw()
@@ -806,6 +846,7 @@ def get_modules(srv):
# Manage modules
# Create raw files, and set html "srv" file
for mod in modules:
+ debug.out(215, "wip. Module %s"%(mod), modules[mod][srv], False, 0, False)
create_html_module = False
# Create user raw files modules in modules/ directory
@@ -825,11 +866,11 @@ def get_modules(srv):
create_html_module = True
# Unused module HTML file or "--force" option
- if not os.path.exists(modules[mod][srv]) or args.force:
+ if not os.path.exists(modules[mod][srv]):
create_html_module = True
# Create HTML module if needed (raw updated, or unused html file)
- if create_html_module:
+ if args.force or create_html_module:
modules[mod]["set"](srv)
if post.error != 0:
@@ -965,7 +1006,7 @@ def navbar_html_create(srv):
# Load post configuration file in tmp module
# return if unused or post errors
- if not post.tmp_load(wrk_index, wrk_index_uri):
+ if not post.tmp_load("Navbar", wrk_index, wrk_index_uri):
return False
items = True
@@ -1073,7 +1114,7 @@ def sidebar_html_create(srv):
# Load post configuration file in tmp module
# return if unused or post errors
- if not post.tmp_load(wrk_post, wrk_post_uri):
+ if not post.tmp_load("Sidebar", wrk_post, wrk_post_uri):
return False
# Post can be added to list
@@ -1122,15 +1163,26 @@ def fill_footer_raw():
if not domain.license_url: domain.license_url = "/"
show_license = "%s%s %s"%(langs.site.license, langs.logs.pp, domain.license)
- footer_items = "%s\n%s"%(
+ footer_items = "%s\n%s\n%s\n%s"%(
footer_items,
item%(
domain.license_url,
langs.site.license_title, domain.title,
show_license
+ ),
+ item%(
+ "/%s"%domain.rss,
+ langs.site.feed_rss, domain.title,
+ langs.site.feed_rss
+ ),
+ item%(
+ "/sitemap.html",
+ langs.site.sitemap, domain.title,
+ langs.site.sitemap
)
)
-
+
+
# Loop to [WEBSITE_FOOTER] and make link from key_url
for key, link in domain.cf.items("WEBSITE_FOOTER"):
if link:
@@ -1159,6 +1211,19 @@ def fill_footer_raw():
# modules/tyto_footer.raw has default contents #
#------------------------------------------------#
def footer_html_create(srv):
+ generator = ''
+
with open(domain.wrk_footer, "r") as f:
footer_raw = f.read().rsplit("\n")
footer_html = ""
@@ -1172,6 +1237,9 @@ def footer_html_create(srv):
if not footer_html: footer_html = line
else: footer_html = "%s\n%s"%(footer_html, line)
+ # Add generator credentials
+ footer_html = "%s\n%s"%(footer_html, generator)
+
tools.create_file(modules["footer"][srv], footer_html)
return True
@@ -1319,4 +1387,4 @@ page_tpl = """