diff options
Diffstat (limited to 'src/koszko_org_website/app.py')
-rw-r--r-- | src/koszko_org_website/app.py | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/koszko_org_website/app.py b/src/koszko_org_website/app.py new file mode 100644 index 0000000..e3e6339 --- /dev/null +++ b/src/koszko_org_website/app.py @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: CC0-1.0 + +# koszko.org website logic. + +# Copyright (C) 2021, 2022 Wojtek Kosior + +import gettext +import dataclasses as dc +import typing as t + +from pathlib import Path, PurePosixPath + +import flask +import werkzeug +import jinja2 +import jinja2.exceptions + + +here = Path(__file__).resolve().parent + + +pages = { + 'index.html' +} + + +langs_short_2_long = { + 'en': 'en_US', + 'pl': 'pl_PL' +} + +langs_long_2_short = dict((v, k) for k, v in langs_short_2_long.items()) + +default_locale = 'pl_PL' + + +def raise_exception(msg) -> t.NoReturn: + raise Exception(msg) + + +@dc.dataclass(init=False) +class Website(flask.Flask): + def __init__(self) -> None: + super().__init__(__name__) + + self.jinja_options = { + **self.jinja_options, + 'loader': jinja2.PackageLoader(__package__), + 'autoescape': jinja2.select_autoescape(['.jinja']), + 'lstrip_blocks': True, + 'extensions': [ + *self.jinja_options.get('extensions', []), + 'jinja2.ext.i18n', + 'jinja2.ext.do' + ] + } + + self.jinja_env.globals['raise'] = raise_exception + + def koszko_install_translations(self, locale: str) -> None: + translations = gettext.translation( + 'messages', + localedir = here / 'locales', + languages = [locale] + ) + self.jinja_env.install_gettext_translations(translations) + + +website_app = Website() + + +def show_page(lang_short: str, page_path: str) -> str: + for segment in PurePosixPath(page_path).parts: + if segment.startswith('__'): + flask.abort(404) + + effective_page_path = '__index.html' if page_path == '' else page_path + + app = t.cast(Website, flask.current_app) + app.koszko_install_translations(langs_short_2_long[lang_short]) + + for prefix in [f'{lang_short}', '']: + pure_path = PurePosixPath(prefix) / f'{effective_page_path}.jinja' + footer_path = pure_path.parent / f'__footer_for_{pure_path.name}' + try: + html = flask.render_template( + str(pure_path), + lang_short = lang_short, + page_path = page_path, + dedicated_footer_path = str(footer_path) + ) + break + except jinja2.exceptions.TemplateNotFound: + if prefix == '': + raise + + return html + +for lang in langs_short_2_long.keys(): + def __show_page_in_lang(page_path: str, lang_short: str = lang) -> str: + return show_page(lang_short, page_path) + + website_app.add_url_rule( + f'/{lang}/<path:page_path>', + f'show_page_in_{lang}', + __show_page_in_lang, + methods=['GET'] + ) + + def __show_main_page_in_lang(lang_short: str = lang) -> str: + return show_page(lang_short, '') + + website_app.add_url_rule( + f'/{lang}/', + f'show_main_page_in_{lang}', + __show_main_page_in_lang, + methods=['GET'] + ) + +@website_app.route('/<path:page_path>', methods=['GET']) +def redirect_to_resource(page_path) -> werkzeug.Response: + if page_path != '': + path_start = PurePosixPath(page_path).parts[0] + + if path_start == 'sideload': + return flask.Response( + "Incomplete server configuration - requests for '/sideload/' not routed as they should be.", + 500 + ) + + # Route to other stuff that was on this domain before and has been moved + # under '/sideload/'. + for name in ['fraktal', 'mm']: + if path_start == name: + return flask.redirect(f'/sideload/{name}') + + # Make all resources from '/static' aliased under '/'. + if not page_path.endswith('.html'): + return flask.redirect(f'/static/{page_path}') + + # Redirect to the most suitable language version of a page. + chosen_locale = flask.request.accept_languages.best_match( + langs_short_2_long.values(), + default = default_locale + ) + if chosen_locale is None: + chosen_locale = default_locale + + lang_short = langs_long_2_short[chosen_locale] + + url = flask.url_for( + f'.show_page_in_{lang_short}', + page_path = page_path + ) + return flask.redirect(url) + +@website_app.route('/', methods=['GET']) +def redirect_to_lang_main_page() -> werkzeug.Response: + return redirect_to_resource('') |