##
# 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
from urllib.parse import urljoin
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)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
app = flask.Flask(__name__, template_folder=BASE_DIR)
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)
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.jinja_env.globals.update(self_localized=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(
"templates/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):
return flask.render_template(
"templates/index.html.j2",
merchant_currency=CURRENCY,
lang=lang
)
@app.errorhandler(404)
def handler(e):
return flask.render_template(
"templates/error.html.j2",
message=gettext("Page not found"),
lang=get_locale()
)
@app.errorhandler(405)
def handler(e):
return flask.render_template(
"templates/error.html.j2",
message=gettext("HTTP method not allowed for this page"),
lang=get_locale()
)