203 lines
6.5 KiB
Python
203 lines
6.5 KiB
Python
import os, json, re
|
|
from glob import glob
|
|
from datetime import date
|
|
from jinja2 import pass_context
|
|
#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
|
|
|
|
@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
|
|
)
|