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("/", 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'[^>]*>(.+?)', lambda m: m.group(0) if m.group(2) == la else '', content )