flask-website/flask/floritiweb/__init__.py
2025-05-15 21:05:28 +02:00

207 lines
6.7 KiB
Python

import os, json, re
from glob import glob
from datetime import date
from jinja2 import pass_context
from markdown import Markdown
#from jinja2 import contextfilter
from flask import Flask, render_template, make_response, abort, redirect, send_from_directory, request
from .lib.dynamic_snippet_library import DynamicSnippetLibrary
TEMPLATES_DIR = '/web/htdocs'
def create_app(test_config=None):
app = Flask(
__name__,
instance_relative_config=True,
template_folder=TEMPLATES_DIR
)
if test_config is None:
if os.environ.get('FLASK_ENV') == 'development':
app.config.from_object("config.DevelopmentConfig")
else:
app.config.from_object("config.ProductionConfig")
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
app.jinja_env.filters['DSL.i18n'] = locate_content
markdown = Markdown()
app.jinja_env.filters['markdown'] = lambda s: markdown.convert(s)
@app.route("/")
def home():
return redirect(app.config['SITE_URL'] + '/index.html', code=302)
@app.route("/<path:template>", methods=["GET","POST"])
def call_template(template):
if template.endswith('/Welcome.html'):
return redirect(app.config['SITE_URL'] + '/' + re.sub(r'[^/]+$', '', template), code=301)
m = re.search(r"([/.])[^/.]*$", template)
if m and m.group(1) == '/':
template = re.sub(r"/?$", "/index.jtml", template, count=1)
elif template.endswith('.html') and os.path.exists(
os.path.join(TEMPLATES_DIR, template)
):
pass # fall-through to upcoming send_from_directory()
else:
template = re.sub(r'h(?=tml$)', 'j', template)
if not template.endswith('.jtml'):
return send_from_directory(TEMPLATES_DIR, template)
m = re.match('([Ee]nglisc?h|[a-z]{2})/', template)
if m:
orig_lang = m.group(0)[:-1]
if 'nglis' in orig_lang:
# normalize
orig_lang = 'Englisch'
langcode = m.group(1)[:2].lower()
else:
langcode = ''
if langcode not in ('de', 'en', 'it', 'fr'):
orig_lang = ''
langcode = 'de'
elif m:
template = template.split('/', 1)[1]
layout = gather_layouts_in_ascending_directory_levels(template)
innermost_layout_name = 'layout.jtml'
next(layout)
def layout_finder(name=None):
nonlocal innermost_layout_name
if name is not None:
innermost_layout_name = name
return layout.send(innermost_layout_name)
template_lang, mtime_date, found_template = try_templates(template, langcode)
other_templates_rx = r'(?<=\.)\w\w(?=.jtml$)'
other_templates = re.sub(other_templates_rx, '*', found_template)
if '*' in other_templates:
other_templates = glob(os.path.join(TEMPLATES_DIR, other_templates))
available_la = []
for ot in other_templates:
if m := re.search(other_templates_rx, ot):
la = m.group(0)
else: continue
available_la.append(la)
if not('de' in available_la or 'en' in available_la):
raise RuntimeError(
"No other templates: Probed {}. de and en missing".
format(', '.join(available_la))
)
else:
available_la = None
def check_user_scope():
# find out if access comes from
# - Earth,
# - university campus (and if member or student)
# - or ourselves, Heidelberg University Library, IT dep.
ip_address = (
request.headers.getlist('X-Forwarded-For')[0]
or request.remote_addr
)
return ip_address
return render_template(found_template,
layout=layout_finder,
date=mtime_date,
DSL=DynamicSnippetLibrary(
root=app.config['SITE_URL'],
path=template.replace('.jtml', '.html'),
available_la=available_la,
orig_la=orig_lang,
requested_la=langcode,
found_la=template_lang,
passed_args=request.args,
passed_data=request.form
),
get_real_ip=check_user_scope,
)
@app.errorhandler(404)
def notfound_page(e):
return make_response(render_template(
'http404.jtml',
DSL=DynamicSnippetLibrary(
root=app.config['SITE_URL'],
path=request.path,
orig_la='',
requested_la='',
found_la='de',
)), 404
)
return app
def gather_layouts_in_ascending_directory_levels(template):
layout_name = yield
if template.endswith("/" + layout_name):
template, _ = os.path.split(template)
while True:
directory, _ = os.path.split(template)
if not len(directory.strip('/.')):
layout_name = yield layout_name
break
possible_template = os.path.join(directory, layout_name)
if os.path.exists(os.path.join(TEMPLATES_DIR, possible_template)):
layout_name = yield possible_template
template = directory
def try_templates(template, langcode):
tried = []
for lang in (langcode, 'en', 'de'):
suffixed = re.sub(r'(?=\.jtml$)', f'.{lang}', template)
path = os.path.join(TEMPLATES_DIR, suffixed)
if os.path.isfile(path):
template = suffixed
break
else:
tried.append(path)
else:
path = os.path.join(TEMPLATES_DIR, template)
lang = None
if not os.path.isfile(path):
abort(404)
last_update_date = date.fromtimestamp(os.path.getmtime(path)).strftime(
"%d.%m.%Y" )
return lang, last_update_date, template
@pass_context
#@contextfilter
def locate_content(ctx, content):
"""
Show elements with lang or xml:lang attributes, plus their contents,
only if the requested language matches.
Always show elements neither lang-specified nor contained in a
lang-specified ancestor element.
"""
lang_attr_value_rx = r'\blang=["\']?(\w\w)'
included = set((i, i) for i in re.findall(lang_attr_value_rx, content))
la = ctx['DSL'].i18n(**dict(included))
return re.sub(
r'<(\w+)[^>]+?' + lang_attr_value_rx + r'[^>]*>(.+?)</\1>',
lambda m: m.group(0) if m.group(2) == la else '',
content
)