## # This file is part of GNU TALER. # Copyright (C) 2017, 2020 Taler Systems SA # # TALER is free software; you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free Software # Foundation; either version 2.1, or (at your option) any later version. # # TALER 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 Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License along with # GNU TALER; see the file COPYING. If not, see # # @author Christian Grothoff # @brief Minimal Website for the landing page. import os import re import datetime import base64 import logging import flask from flask import request from flask_babel import Babel from flask_babel import refresh from flask_babel import force_locale from flask_babel import gettext import traceback from taler.util.talerconfig import TalerConfig, ConfigurationError from ..httpcommon import backend_get, backend_post, self_localized import sys if not sys.version_info.major == 3 and sys.version_info.minor >= 6: print("Python 3.6 or higher is required.") print( "You are using Python {}.{}.".format( sys.version_info.major, sys.version_info.minor ) ) sys.exit(1) app = flask.Flask(__name__, template_folder="../templates", static_folder="../static") app.debug = True app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8") LOGGER = logging.getLogger(__name__) TC = TalerConfig.from_env() try: BACKEND_URL = TC["frontends"]["backend"].value_string(required=True) CURRENCY = TC["taler"]["currency"].value_string(required=True) APIKEY = TC["frontends"]["backend_apikey"].value_string(required=True) except ConfigurationError as ce: print(ce) exit(1) BABEL_TRANSLATION_DIRECTORIES = "../translations" app.config.from_object(__name__) babel = Babel(app) LOGGER.info("Using translations from:" + ":".join(list(babel.translation_directories))) translations = [str(translation) for translation in babel.list_translations()] if not "en" in translations: translations.append("en") LOGGER.info( "Operating with the following translations available: " + " ".join(translations) ) app.add_template_global(self_localized) @babel.localeselector def get_locale(): parts = request.path.split("/", 2) if 2 >= len(parts): # Totally unexpected path format, do not localize return "en" lang = parts[1] if lang in translations: return lang return "en" ## # Make the environment available into templates. # # @return the environment-reading function. @app.context_processor def utility_processor(): def env(name, default=None): return os.environ.get(name, default) def prettydate(talerdate): parsed_time = re.search(r"/Date\(([0-9]+)\)/", talerdate) if not parsed_time: return "malformed date given" parsed_time = int(parsed_time.group(1)) timestamp = datetime.datetime.fromtimestamp(parsed_time) # returns the YYYY-MM-DD date format. return timestamp.strftime("%Y-%b-%d") return dict(env=env, prettydate=prettydate) ## # Exception handler to capture all the unmanaged errors. # # @param e the Exception object, currently unused. # @return flask-native response object carrying the error message # (and execution stack!). @app.errorhandler(Exception) def internal_error(e): return flask.render_template( "landing-error.html.j2", message=gettext("Internal error"), stack=traceback.format_exc(), lang=get_locale(), ) ## # Serve the /favicon.ico requests. # # @return the favicon.ico file. @app.route("/favicon.ico") def favicon(): LOGGER.info("will look into: " + os.path.join(app.root_path, "static")) return flask.send_from_directory( os.path.join(app.root_path, "static"), "favicon.ico", mimetype="image/vnd.microsoft.ico", ) ## # Serve the main index page, redirecting to // # # @return response object of the index page. @app.route("/") def index(): default = "en" target = flask.request.accept_languages.best_match(translations, default) return flask.redirect("/" + target + "/", code=302) ## # Serve the internationalized main index page. # # @return response object of the index page. @app.route("//", methods=["GET"]) def start(lang): if x := os.environ.get("TALER_ENV_URL_BANK"): bank_register_url = "/".join([x.strip("/"), f"{lang}/register"]) else: bank_register_url = "#" if x := os.environ.get("TALER_ENV_URL_MERCHANT_BLOG"): merchant_blog_url = "/".join([x.strip("/"), lang]) else: merchant_blog_url = "#" if x := os.environ.get("TALER_ENV_URL_MERCHANT_DONATIONS"): merchant_donations_url = "/".join([x.strip("/"), lang]) else: merchant_donations_url = "#" if x := os.environ.get("TALER_ENV_URL_MERCHANT_SURVEY"): merchant_survey_url = "/".join([x.strip("/"), lang]) else: merchant_survey_url = "#" return flask.render_template( "landing-index.html.j2", merchant_currency=CURRENCY, lang=lang, bank_url=bank_register_url, merchant_blog_url=merchant_blog_url, merchant_donations_url=merchant_donations_url, merchant_survey_url=merchant_survey_url, ) @app.errorhandler(404) def handler_404(e): return flask.render_template( "landing-error.html.j2", message=gettext("Page not found"), lang=get_locale() ) @app.errorhandler(405) def handler_405(e): return flask.render_template( "landing-error.html.j2", message=gettext("HTTP method not allowed for this page"), lang=get_locale(), )