summaryrefslogtreecommitdiff
path: root/talermerchantdemos
diff options
context:
space:
mode:
Diffstat (limited to 'talermerchantdemos')
-rw-r--r--talermerchantdemos/blog/blog.py190
-rw-r--r--talermerchantdemos/blog/content.py36
l---------talermerchantdemos/blog/static1
-rw-r--r--talermerchantdemos/blog/templates/base.html.j252
l---------talermerchantdemos/blog/templates/footer.html.j21
-rw-r--r--talermerchantdemos/blog/templates/javascript.html31
l---------talermerchantdemos/blog/templates/language-switcher.html.j21
l---------talermerchantdemos/blog/templates/menu.html.j21
l---------talermerchantdemos/blog/translations1
-rw-r--r--talermerchantdemos/donations/donations.py97
l---------talermerchantdemos/donations/static1
-rw-r--r--talermerchantdemos/donations/templates/base.html.j248
l---------talermerchantdemos/donations/templates/footer.html.j21
-rw-r--r--talermerchantdemos/donations/templates/javascript.html31
l---------talermerchantdemos/donations/templates/language-switcher.html.j21
l---------talermerchantdemos/donations/templates/menu.html.j21
l---------talermerchantdemos/donations/translations1
-rw-r--r--talermerchantdemos/httpcommon/__init__.py65
-rw-r--r--talermerchantdemos/landing/landing.py16
l---------talermerchantdemos/landing/static1
l---------talermerchantdemos/landing/templates/footer.html.j21
l---------talermerchantdemos/landing/templates/language-switcher.html.j21
l---------talermerchantdemos/landing/templates/menu.html.j21
l---------talermerchantdemos/landing/translations1
l---------talermerchantdemos/survey/static1
-rw-r--r--talermerchantdemos/survey/survey.py80
-rw-r--r--talermerchantdemos/survey/templates/base.html.j248
l---------talermerchantdemos/survey/templates/footer.html.j21
l---------talermerchantdemos/survey/templates/language-switcher.html.j21
l---------talermerchantdemos/survey/templates/menu.html.j21
l---------talermerchantdemos/survey/translations1
-rw-r--r--talermerchantdemos/templates/blog-article-frame.html.j2 (renamed from talermerchantdemos/blog/templates/article_frame.html.j2)11
-rw-r--r--talermerchantdemos/templates/blog-article-refunded.html.j2 (renamed from talermerchantdemos/blog/templates/article_refunded.html.j2)2
-rw-r--r--talermerchantdemos/templates/blog-base.html.j213
-rw-r--r--talermerchantdemos/templates/blog-confirm-refund.html.j2 (renamed from talermerchantdemos/blog/templates/confirm_refund.html.j2)6
-rw-r--r--talermerchantdemos/templates/blog-error.html.j2 (renamed from talermerchantdemos/donations/templates/error.html.j2)2
-rw-r--r--talermerchantdemos/templates/blog-index.html.j2 (renamed from talermerchantdemos/blog/templates/index.html.j2)2
-rw-r--r--talermerchantdemos/templates/common-base.html.j2 (renamed from talermerchantdemos/landing/templates/base.html.j2)18
-rw-r--r--talermerchantdemos/templates/donations-base.html.j213
-rw-r--r--talermerchantdemos/templates/donations-checkout.html.j2 (renamed from talermerchantdemos/donations/templates/checkout.html.j2)2
-rw-r--r--talermerchantdemos/templates/donations-error.html.j2 (renamed from talermerchantdemos/blog/templates/error.html.j2)2
-rw-r--r--talermerchantdemos/templates/donations-fulfillment.html.j2 (renamed from talermerchantdemos/donations/templates/fulfillment.html.j2)2
-rw-r--r--talermerchantdemos/templates/donations-index.html.j2 (renamed from talermerchantdemos/donations/templates/index.html.j2)2
-rw-r--r--talermerchantdemos/templates/donations-provider-not-supported.html.j2 (renamed from talermerchantdemos/donations/templates/provider-not-supported.html.j2)2
-rw-r--r--talermerchantdemos/templates/landing-base.html.j212
-rw-r--r--talermerchantdemos/templates/landing-error.html.j2 (renamed from talermerchantdemos/landing/templates/error.html.j2)2
-rw-r--r--talermerchantdemos/templates/landing-index.html.j2 (renamed from talermerchantdemos/landing/templates/index.html.j2)2
-rw-r--r--talermerchantdemos/templates/menu.html.j25
-rw-r--r--talermerchantdemos/templates/survey-base.html.j213
-rw-r--r--talermerchantdemos/templates/survey-error.html.j2 (renamed from talermerchantdemos/survey/templates/error.html.j2)2
-rw-r--r--talermerchantdemos/templates/survey-index.html.j2 (renamed from talermerchantdemos/survey/templates/index.html.j2)2
51 files changed, 360 insertions, 469 deletions
diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py
index be9b0a2..e8cce32 100644
--- a/talermerchantdemos/blog/blog.py
+++ b/talermerchantdemos/blog/blog.py
@@ -34,20 +34,38 @@ import sys
from urllib.parse import urljoin, urlencode, urlparse
from taler.util.talerconfig import TalerConfig, ConfigurationError
from ..blog.content import ARTICLES, get_article_file, get_image_file
-from talermerchantdemos.httpcommon import backend_get, backend_post, self_localized, err_abort, Deadline
+from talermerchantdemos.httpcommon import (
+ backend_get,
+ backend_post,
+ self_localized,
+ Deadline,
+ BackendException,
+)
+
+
+def err_abort(abort_status_code, **params):
+ """
+ Return a error response to the client.
+
+ @param abort_status_code status code to return along the response.
+ @param params _kw_ arguments to passed verbatim to the templating engine.
+ """
+ t = flask.render_template("blog-error.html.j2", lang=get_locale(), **params)
+ flask.abort(flask.make_response(t, abort_status_code))
def refundable(pay_status):
refunded = pay_status.get("refunded")
refund_deadline = pay_status.get("contract_terms", {}).get("refund_deadline")
- assert(refunded != None and refund_deadline)
+ assert refunded != None and refund_deadline
t_ms = refund_deadline.get("t_ms")
- assert(t_ms)
+ assert t_ms
rd = Deadline(t_ms)
if not refunded and not rd.isExpired():
return True
return False
+
if not sys.version_info.major == 3 and sys.version_info.minor >= 6:
print("Python 3.6 or higher is required.")
print(
@@ -57,10 +75,7 @@ if not sys.version_info.major == 3 and sys.version_info.minor >= 6:
)
sys.exit(1)
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-app = flask.Flask(
- __name__, template_folder=BASE_DIR, static_folder=BASE_DIR + "/../static/"
-)
+app = flask.Flask(__name__, template_folder="../templates", static_folder="../static")
app.debug = True
app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
@@ -73,19 +88,24 @@ try:
except ConfigurationError as ce:
print(ce)
exit(1)
+
ARTICLE_AMOUNT = CURRENCY + ":0.5"
BACKEND_URL = urljoin(BACKEND_BASE_URL, "instances/blog/")
+BABEL_TRANSLATION_DIRECTORIES = "../translations"
+
app.config.from_object(__name__)
babel = Babel(app)
-LOGGER.info("Using translations from:" + ':'.join(list(babel.translation_directories)))
+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))
+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)
+app.add_template_global(self_localized)
##
@@ -98,6 +118,7 @@ def utility_processor():
# These helpers will be available in templates
def env(name, default=None):
return os.environ.get(name, default)
+
return dict(env=env)
@@ -110,9 +131,10 @@ def utility_processor():
@app.errorhandler(Exception)
def internal_error(e):
return flask.render_template(
- "templates/error.html.j2",
+ "blog-error.html.j2",
message=gettext("Internal error"),
- stack=traceback.format_exc()
+ stack=traceback.format_exc(),
+ lang=get_locale(),
)
@@ -122,27 +144,29 @@ def internal_error(e):
# @return response object of the index page.
@app.route("/")
def index():
- default = 'en'
+ default = "en"
target = flask.request.accept_languages.best_match(translations, default)
return flask.redirect("/" + target + "/", code=302)
+
##
# 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'))
+ LOGGER.info("will look into: " + os.path.join(app.root_path, "static"))
return flask.send_from_directory(
- os.path.join(app.root_path, 'static'),
+ os.path.join(app.root_path, "static"),
"favicon.ico",
- mimetype="image/vnd.microsoft.ico"
+ mimetype="image/vnd.microsoft.ico",
)
+
@babel.localeselector
def get_locale():
- parts = request.path.split('/', 2)
- if (2 >= len(parts)):
+ parts = request.path.split("/", 2)
+ if 2 >= len(parts):
# Totally unexpected path format, do not localize
return "en"
lang = parts[1]
@@ -150,6 +174,7 @@ def get_locale():
return lang
return "en"
+
##
# Serve the main index page for a particular language.
#
@@ -157,14 +182,14 @@ def get_locale():
@app.route("/<lang>/")
def start(lang):
if lang in ARTICLES:
- translated=ARTICLES[lang]
+ translated = ARTICLES[lang]
else:
- translated={}
+ translated = {}
return flask.render_template(
- "templates/index.html.j2",
+ "blog-index.html.j2",
lang=lang,
merchant_currency=CURRENCY,
- articles=translated.values()
+ articles=translated.values(),
)
@@ -172,21 +197,30 @@ def start(lang):
def confirm_refund(lang, order_id):
session_id = flask.session.get("session_id", "")
pay_status = backend_get(
- BACKEND_URL, f"private/orders/{order_id}", params=dict(session_id=session_id)
+ BACKEND_URL,
+ f"private/orders/{order_id}",
+ params=dict(session_id=session_id),
+ auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
if order_status != "paid":
err_abort(
- 400, message=gettext("Cannot refund unpaid article"),
+ 400,
+ message=gettext("Cannot refund unpaid article"),
)
article_name = pay_status["contract_terms"]["extra"]["article_name"]
if not refundable(pay_status):
return flask.render_template(
- "templates/error.html.j2", message=gettext("Article is not anymore refundable")
+ "blog-error.html.j2",
+ message=gettext("Article is not anymore refundable"),
+ lang=get_locale(),
)
return flask.render_template(
- "templates/confirm_refund.html.j2", article_name=article_name, order_id=order_id
+ "blog-confirm-refund.html.j2",
+ article_name=article_name,
+ order_id=order_id,
+ lang=get_locale(),
)
@@ -207,7 +241,10 @@ def refund(order_id):
return flask.jsonify(dict(error="Aborting refund: order unknown")), 400
session_id = flask.session.get("session_id", "")
pay_status = backend_get(
- BACKEND_URL, f"private/orders/{order_id}", params=dict(session_id=session_id)
+ BACKEND_URL,
+ f"private/orders/{order_id}",
+ params=dict(session_id=session_id),
+ auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
@@ -215,16 +252,16 @@ def refund(order_id):
err_abort(
402,
message=gettext("You did not pay for this article (nice try!)"),
- json=pay_status
+ json=pay_status,
)
if not refundable(pay_status):
err_abort(
- 403,
- message=gettext("Item not refundable (anymore)"),
- json=pay_status
+ 403, message=gettext("Item not refundable (anymore)"), json=pay_status
)
refund_spec = dict(reason="Demo reimbursement", refund=ARTICLE_AMOUNT)
- resp = backend_post(BACKEND_URL, f"private/orders/{order_id}/refund", refund_spec)
+ backend_post(
+ BACKEND_URL, f"private/orders/{order_id}/refund", refund_spec, auth_token=APIKEY
+ )
return flask.redirect(pay_status["order_status_url"])
@@ -245,7 +282,9 @@ def refund(order_id):
def render_article(article_name, lang, data, order_id, refundable):
article_info = ARTICLES[lang].get(article_name)
if article_info is None:
- m = gettext("Internal error: Files for article ({}) not found.").format(article_name)
+ m = gettext("Internal error: Files for article ({}) not found.").format(
+ article_name
+ )
err_abort(500, message=m)
if data is not None:
if data in article_info.extra_files:
@@ -255,25 +294,27 @@ def render_article(article_name, lang, data, order_id, refundable):
)
err_abort(404, message=m)
# the order_id is needed for refunds
+ article_contents = open(get_article_file(article_info)).read()
return flask.render_template(
- "templates/article_frame.html.j2",
- article_file=get_article_file(article_info),
+ "blog-article-frame.html.j2",
+ article_contents=article_contents,
article_name=article_name,
order_id=order_id,
lang=lang,
- refundable=refundable
+ refundable=refundable,
)
+
##
# Setup a fresh order with the backend.
#
# @param article_name which article the order is for
# @param lang which language to use
#
-def post_order(article_name,lang):
+def post_order(article_name, lang):
order = dict(
amount=ARTICLE_AMOUNT,
- extra=dict(article_name=article_name,lang=lang),
+ extra=dict(article_name=article_name, lang=lang),
fulfillment_url=flask.request.base_url,
summary="Essay: " + article_name.replace("_", " "),
# FIXME: add support for i18n of summary!
@@ -283,7 +324,9 @@ def post_order(article_name,lang):
order_resp = backend_post(
BACKEND_URL,
"private/orders",
- dict(order=order, refund_delay=dict(d_ms=1000 * 120)))
+ dict(order=order, refund_delay=dict(d_ms=1000 * 120)),
+ auth_token=APIKEY,
+ )
return order_resp
@@ -324,26 +367,28 @@ def article(article_name, lang=None, data=None):
if not order_id:
if not lang:
err_abort(403, message=gettext("Direct access forbidden"))
- order_resp = post_order(article_name,lang)
+ order_resp = post_order(article_name, lang)
order_id = order_resp["order_id"]
# Ask the backend for the status of the payment
pay_status = backend_get(
BACKEND_URL,
f"private/orders/{order_id}",
- params=dict(session_id=session_id)
+ params=dict(session_id=session_id),
+ auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
if order_status == "claimed":
if not lang:
err_abort(403, message=gettext("Direct access forbidden"))
# Order already claimed, must setup fresh order
- order_resp = post_order(article_name,lang)
+ order_resp = post_order(article_name, lang)
order_id = order_resp["order_id"]
pay_status = backend_get(
BACKEND_URL,
f"private/orders/{order_id}",
- params=dict(session_id=session_id)
+ params=dict(session_id=session_id),
+ auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
# This really must be 'unpaid' now...
@@ -351,17 +396,14 @@ def article(article_name, lang=None, data=None):
if order_status == "paid":
refunded = pay_status["refunded"]
if refunded:
- return flask.render_template(
- "templates/article_refunded.html.j2",
+ return flask.render_template(
+ "blog-article-refunded.html.j2",
article_name=article_name,
order_id=order_id,
+ lang=lang,
)
response = render_article(
- article_name,
- lang,
- data,
- order_id,
- refundable(pay_status)
+ article_name, lang, data, order_id, refundable(pay_status)
)
return response
@@ -372,14 +414,10 @@ def article(article_name, lang=None, data=None):
if ai is not None and au is not None:
response = flask.redirect(au)
response.set_cookie(
- "order_id",
- ai,
- path=urllib.parse.quote(f"/essay/{article_name}")
+ "order_id", ai, path=urllib.parse.quote(f"/essay/{article_name}")
)
response.set_cookie(
- "order_id",
- ai,
- path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
+ "order_id", ai, path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
)
return response
@@ -387,27 +425,39 @@ def article(article_name, lang=None, data=None):
# run the payment protocol.
response = flask.redirect(pay_status["order_status_url"])
response.set_cookie(
- "order_id",
- order_id,
- path=urllib.parse.quote(f"/essay/{article_name}")
+ "order_id", order_id, path=urllib.parse.quote(f"/essay/{article_name}")
)
response.set_cookie(
- "order_id",
- order_id,
- path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
+ "order_id", order_id, path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
)
return response
+
@app.errorhandler(500)
-def handler(e):
+def handler_500(e):
return flask.render_template(
- "templates/error.html.j2",
- message=gettext("Internal server error")
+ "blog-error.html.j2",
+ message=gettext("Internal server error"),
+ lang=get_locale(),
)
+
@app.errorhandler(404)
-def handler(e):
+def handler_404(e):
return flask.render_template(
- "templates/error.html.j2",
- message=gettext("Page not found")
+ "blog-error.html.j2",
+ message=gettext("Page not found"),
+ lang=get_locale(),
+ )
+
+
+@app.errorhandler(BackendException)
+def handler_backend_exception(e):
+ t = flask.render_template(
+ "survey-error.html.j2",
+ lang=get_locale(),
+ message=e.args[0],
+ json=e.backend_json,
+ status_code=e.backend_status,
)
+ return flask.make_response(t, 500)
diff --git a/talermerchantdemos/blog/content.py b/talermerchantdemos/blog/content.py
index 00064df..ba2fcbe 100644
--- a/talermerchantdemos/blog/content.py
+++ b/talermerchantdemos/blog/content.py
@@ -49,8 +49,8 @@ ARTICLES = {}
# @param extra_file collection of extra files associated with the
# article, like images and sounds.
# @param lang language of the arcile
-def add_article(slug, title, teaser, main_file, extra_files, lang='en'):
- if (not (lang in ARTICLES)):
+def add_article(slug, title, teaser, main_file, extra_files, lang="en"):
+ if not (lang in ARTICLES):
ARTICLES[lang] = OrderedDict()
ARTICLES[lang][slug] = Article(slug, title, teaser, main_file, extra_files, lang)
@@ -71,8 +71,10 @@ def get_image_file(image):
# @param article the article filename.
# @return the path to the article HTML file.
def get_article_file(article):
- filex = resource_filename("talermerchantdemos", article.main_file)
- return os.path.basename(filex)
+ filex = resource_filename(
+ "talermerchantdemos", article.main_file,
+ )
+ return os.path.abspath(filex)
##
@@ -88,7 +90,7 @@ def get_article_file(article):
# specified.
def add_from_html(resource_name, lang):
res = resource_stream("talermerchantdemos", resource_name)
- soup = BeautifulSoup(res, 'html.parser')
+ soup = BeautifulSoup(res, "html.parser")
res.close()
title_el = soup.find("h2")
if title_el is None:
@@ -103,9 +105,9 @@ def add_from_html(resource_name, lang):
paragraphs = soup.find_all("p")
if len(paragraphs) > 0:
teaser = paragraphs[0].get_text()
- if (len(paragraphs) > 1) and (len (teaser) < 100):
+ if (len(paragraphs) > 1) and (len(teaser) < 100):
teaser2 = paragraphs[1].get_text()
- if (len(teaser2) > len(teaser)):
+ if len(teaser2) > len(teaser):
teaser = teaser2
else:
LOGGER.warning("Cannot extract teaser from '%s'", resource_name)
@@ -119,21 +121,23 @@ def add_from_html(resource_name, lang):
# We require that any image whose access is regulated is src'd
# as "<slug>/data/img.png". We also need to check if the <slug>
# component actually matches the article's slug
- if re_proc.match(img['src']):
- if img['src'].split(os.sep)[2] == slug:
+ if re_proc.match(img["src"]):
+ if img["src"].split(os.sep)[2] == slug:
LOGGER.info(
- "extra file for %s is %s" %
- (slug, os.path.basename(img['src']))
+ "extra file for %s is %s" % (slug, os.path.basename(img["src"]))
)
- extra_files.append(os.path.basename(img['src']))
+ extra_files.append(os.path.basename(img["src"]))
else:
- LOGGER.warning("Image src and slug don't match: '%s' != '%s'" \
- % (img['src'].split(os.sep)[2], slug))
+ LOGGER.warning(
+ "Image src and slug don't match: '%s' != '%s'"
+ % (img["src"].split(os.sep)[2], slug)
+ )
add_article(slug, title, teaser, resource_name, extra_files, lang)
+
for l in listdir(resource_filename("talermerchantdemos", "blog/articles/")):
# Filter by active languages, otherwise this takes quite a while to load...
- if l in { "en", "de", "sv", "es" }:
+ if l in {"en", "de", "sv", "es"}:
LOGGER.info("importing %s" % l)
- for a in listdir(resource_filename ("talermerchantdemos", "blog/articles/" + l)):
+ for a in listdir(resource_filename("talermerchantdemos", "blog/articles/" + l)):
add_from_html("blog/articles/" + l + "/" + a, l)
diff --git a/talermerchantdemos/blog/static b/talermerchantdemos/blog/static
deleted file mode 120000
index d9bc54d..0000000
--- a/talermerchantdemos/blog/static
+++ /dev/null
@@ -1 +0,0 @@
-../static/ \ No newline at end of file
diff --git a/talermerchantdemos/blog/templates/base.html.j2 b/talermerchantdemos/blog/templates/base.html.j2
deleted file mode 100644
index c4935f1..0000000
--- a/talermerchantdemos/blog/templates/base.html.j2
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<!--
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015, 2016, 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
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--->
-
-<html>
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- {% block meta %}{% endblock %}
- <title>{{ gettext("GNU Taler Demo: Essay Shop") }}</title>
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='pure.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='demo.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='navbar.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='torsten.css') }}" />
- {% block styles %}{% endblock %}
- {% block scripts %}{% endblock %}
-</head>
-
-<body>
- <header class="demobar" style="display: flex; flex-direction: column;">
- <h1><span class="tt adorn-brackets">{{ gettext("Taler Demo")}}</span><br/>
- <span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG') }}">{{ gettext("Essay shop") }}</a></span></h1>
- <p>{{
- gettext("On this page you can buy articles using an imaginary currency.") + "<br>" +
- gettext("The articles are chapters from Richard Stallman's book &quot;Free Software, Free Society&quot;.") + "<br>" +
- gettext('The book is <a href="{shop}">published by the FSF</a> and available gratis at <a href="{gnu}">gnu.org</a>.').format(shop="https://shop.fsf.org/product/free-software-free-society-2", gnu="https://www.gnu.org")
- }}
- </p>
- </header>
- {% from 'templates/menu.html.j2' import menu with context %} {{ menu('blog') }}
-
- <section id="main" class="content">
- {% block main %}
- This is the main content of the page.
- {% endblock %}
- {% include 'templates/footer.html.j2' %}
- </section>
-</body>
-</html>
diff --git a/talermerchantdemos/blog/templates/footer.html.j2 b/talermerchantdemos/blog/templates/footer.html.j2
deleted file mode 120000
index 028b093..0000000
--- a/talermerchantdemos/blog/templates/footer.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/footer.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/blog/templates/javascript.html b/talermerchantdemos/blog/templates/javascript.html
deleted file mode 100644
index 436c186..0000000
--- a/talermerchantdemos/blog/templates/javascript.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- This file is in the public domain -->
-
-<html>
- <head>
- <title>JavaScript disclaimer.</title>
- </head>
- <body>
- <h3>This site does not use JavaScript.</h3>
- </body>
-</html>
-
-<!-- The following lines are kept as a template for future inclusion of JavaScript files. -->
-
-<!--
- <html>
- <body>
- <table id="jslicense-labels1">
- <tr>
- <td><a href="/static/path/to/example0.js">example0.js</a></td>
- <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html">LGPL</a></td>
- <td><a href="/static/path/to/example0.js.tar.gz">example0.js.tar.gz</a></td>
- </tr>
- <tr>
- <td><a href="/static/path/to/example1.js">example1.js</a></td>
- <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html">LGPL</a></td>
- <td><a href="/static/path/to/example1.js.tar.gz">example1.js.tar.gz</a></td>
- </tr>
- </table>
- </body>
- </html>
--->
diff --git a/talermerchantdemos/blog/templates/language-switcher.html.j2 b/talermerchantdemos/blog/templates/language-switcher.html.j2
deleted file mode 120000
index 6423c5c..0000000
--- a/talermerchantdemos/blog/templates/language-switcher.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/language-switcher.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/blog/templates/menu.html.j2 b/talermerchantdemos/blog/templates/menu.html.j2
deleted file mode 120000
index b9575ea..0000000
--- a/talermerchantdemos/blog/templates/menu.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/menu.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/blog/translations b/talermerchantdemos/blog/translations
deleted file mode 120000
index 0a951f7..0000000
--- a/talermerchantdemos/blog/translations
+++ /dev/null
@@ -1 +0,0 @@
-../../translations/ \ No newline at end of file
diff --git a/talermerchantdemos/donations/donations.py b/talermerchantdemos/donations/donations.py
index ea9612c..19f1372 100644
--- a/talermerchantdemos/donations/donations.py
+++ b/talermerchantdemos/donations/donations.py
@@ -31,19 +31,23 @@ import traceback
import urllib
from taler.util.talerconfig import TalerConfig, ConfigurationError
from urllib.parse import urljoin
-from ..httpcommon import backend_post, backend_get, fallback_404, self_localized
+from ..httpcommon import backend_post, backend_get, 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))
+ print(
+ "You are using Python {}.{}.".format(
+ sys.version_info.major, sys.version_info.minor
+ )
+ )
sys.exit(1)
LOGGER = logging.getLogger(__name__)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-app = flask.Flask(__name__, template_folder=BASE_DIR)
+app = flask.Flask(__name__, template_folder="../templates", static_folder="../static")
app.debug = True
app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
@@ -56,23 +60,27 @@ 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)))
+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))
+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)
+app.add_template_global(self_localized)
@babel.localeselector
def get_locale():
- parts = request.path.split('/', 2)
- if (2 >= len(parts)):
+ parts = request.path.split("/", 2)
+ if 2 >= len(parts):
# Totally unexpected path format, do not localize
return "en"
lang = parts[1]
@@ -100,10 +108,7 @@ def utility_processor():
# @param abort_status_code status code to return along the response.
# @param params _kw_ arguments to passed verbatim to the templating engine.
def err_abort(abort_status_code, **params):
- t = flask.render_template(
- "templates/error.html.j2",
- lang=get_locale(),
- **params)
+ t = flask.render_template("donations-error.html.j2", lang=get_locale(), **params)
flask.abort(flask.make_response(t, abort_status_code))
@@ -116,7 +121,7 @@ def err_abort(abort_status_code, **params):
# if something unexpected happens.
def backend_instanced_get(instance, endpoint, params):
backend_url = urljoin(BACKEND_BASE_URL, f"instances/{instance}/")
- return backend_get(backend_url, endpoint, params)
+ return backend_get(backend_url, endpoint, params, auth_token=APIKEY)
##
@@ -129,7 +134,7 @@ def backend_instanced_get(instance, endpoint, params):
# @return the backend response (JSON format).
def backend_instanced_post(instance, endpoint, json):
backend_url = urljoin(BACKEND_BASE_URL, f"instances/{instance}/")
- return backend_post(backend_url, endpoint, json)
+ return backend_post(backend_url, endpoint, json, auth_token=APIKEY)
##
@@ -153,32 +158,34 @@ def expect_parameter(name):
@app.errorhandler(Exception)
def internal_error(e):
return flask.render_template(
- "templates/error.html.j2",
+ "donations-error.html.j2",
message=gettext("Internal error"),
lang=get_locale(),
- stack=traceback.format_exc()
+ stack=traceback.format_exc(),
)
+
##
# 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'))
+ LOGGER.info("will look into: " + os.path.join(app.root_path, "static"))
return flask.send_from_directory(
- os.path.join(app.root_path, 'static'),
+ os.path.join(app.root_path, "static"),
"favicon.ico",
- mimetype="image/vnd.microsoft.ico"
+ mimetype="image/vnd.microsoft.ico",
)
+
##
# Serve the main index page, redirecting to /<lang>/
#
# @return response object of the index page.
@app.route("/")
def index():
- default = 'en'
+ default = "en"
target = flask.request.accept_languages.best_match(translations, default)
return flask.redirect("/" + target + "/", code=302)
@@ -190,22 +197,11 @@ def index():
@app.route("/<lang>/")
def start(lang):
return flask.render_template(
- "templates/index.html.j2",
- lang=lang,
- merchant_currency=CURRENCY
+ "donations-index.html.j2", lang=lang, merchant_currency=CURRENCY
)
##
-# Serve the "/javascript" page.
-#
-# @return response object for the /javascript page.
-@app.route("/javascript")
-def javascript_licensing():
- return flask.render_template("templates/javascript.html")
-
-
-##
# Serve the "/checkout" page. This page lets the
# user pick the payment method they want to use,
# and finally confirm the donation.
@@ -217,7 +213,7 @@ def checkout(lang):
donation_receiver = expect_parameter("donation_receiver")
donation_donor = expect_parameter("donation_donor")
return flask.render_template(
- "templates/checkout.html.j2",
+ "donations-checkout.html.j2",
donation_amount=amount,
donation_receiver=donation_receiver,
donation_donor=donation_donor,
@@ -232,11 +228,8 @@ def checkout(lang):
#
# @return response object about the mentioned impossibility.
@app.route("/<lang>/provider-not-supported")
-def provider_not_supported():
- return flask.render_template(
- "templates/provider-not-supported.html.j2",
- lang=lang
- )
+def provider_not_supported(lang):
+ return flask.render_template("donations-provider-not-supported.html.j2", lang=lang)
##
@@ -253,8 +246,7 @@ def donate(lang):
donation_donor = expect_parameter("donation_donor")
payment_system = expect_parameter("payment_system")
if payment_system != "taler":
- return flask.redirect(flask.url_for("provider_not_supported",
- lang=lang))
+ return flask.redirect(flask.url_for("provider_not_supported", lang=lang))
fulfillment_url = flask.url_for(
"fulfillment",
timestamp=str(time.time()),
@@ -266,9 +258,7 @@ def donate(lang):
order = dict(
amount=donation_amount,
extra=dict(
- donor=donation_donor,
- receiver=donation_receiver,
- amount=donation_amount
+ donor=donation_donor, receiver=donation_receiver, amount=donation_amount
),
fulfillment_url=fulfillment_url,
summary="Donation to {}".format(donation_receiver),
@@ -279,10 +269,9 @@ def donate(lang):
)
order_id = order_resp["order_id"]
return flask.redirect(
- flask.url_for("fulfillment",
- receiver=donation_receiver,
- lang=lang,
- order_id=order_id)
+ flask.url_for(
+ "fulfillment", receiver=donation_receiver, lang=lang, order_id=order_id
+ )
)
@@ -295,9 +284,8 @@ def donate(lang):
# page is returned; otherwise, the browser will be redirected
# to a page that accepts the payment.
@app.route("/<lang>/donation/<receiver>")
-def fulfillment(lang,receiver):
+def fulfillment(lang, receiver):
order_id = expect_parameter("order_id")
- pay_params = dict(order_id=order_id)
pay_status = backend_instanced_get(
receiver, f"private/orders/{order_id}", params=dict()
)
@@ -305,7 +293,7 @@ def fulfillment(lang,receiver):
if order_status == "paid":
extra = pay_status["contract_terms"]["extra"]
return flask.render_template(
- "templates/fulfillment.html.j2",
+ "donations-fulfillment.html.j2",
donation_receiver=extra["receiver"],
donation_amount=extra["amount"],
donation_donor=extra["donor"],
@@ -315,10 +303,9 @@ def fulfillment(lang,receiver):
)
return flask.redirect(pay_status["order_status_url"])
+
@app.errorhandler(404)
def handler(e):
return flask.render_template(
- "templates/error.html.j2",
- lang=get_locale(),
- message=gettext("Page not found")
+ "donations-error.html.j2", lang=get_locale(), message=gettext("Page not found")
)
diff --git a/talermerchantdemos/donations/static b/talermerchantdemos/donations/static
deleted file mode 120000
index d9bc54d..0000000
--- a/talermerchantdemos/donations/static
+++ /dev/null
@@ -1 +0,0 @@
-../static/ \ No newline at end of file
diff --git a/talermerchantdemos/donations/templates/base.html.j2 b/talermerchantdemos/donations/templates/base.html.j2
deleted file mode 100644
index 307705f..0000000
--- a/talermerchantdemos/donations/templates/base.html.j2
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!--
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015, 2016, 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
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--->
-
-<html data-taler-nojs="true">
-<head>
- <title>{{ gettext("GNU Taler Demo: Donations") }}</title>
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='pure.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='demo.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='navbar.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='torsten.css') }}" />
- {% block styles %}{% endblock %}
- {% block scripts %}{% endblock %}
-</head>
-
-<body>
- <header class="demobar" style="display: flex; flex-direction: column;">
- <h1><span class="tt adorn-brackets">{{ gettext("Taler Demo") }}</span><br/>
- <span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS') }}">{{gettext("Donations")}}</a></span></h1>
- <p>{{
- gettext ("This is the donation page.") + "<br>" +
- gettext ("Using this page you can make donations in {currency} to Free Software projects.").format(currency=merchant_currency)
- }}
- </p>
- </header>
- {% from 'templates/menu.html.j2' import menu with context %} {{ menu('donations') }}
-
- <section id="main" class="content">
- {% block main %}
- This is the main content of the page.
- {% endblock %}
- {% include 'templates/footer.html.j2' %}
- </section>
-</body>
-</html>
diff --git a/talermerchantdemos/donations/templates/footer.html.j2 b/talermerchantdemos/donations/templates/footer.html.j2
deleted file mode 120000
index 028b093..0000000
--- a/talermerchantdemos/donations/templates/footer.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/footer.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/donations/templates/javascript.html b/talermerchantdemos/donations/templates/javascript.html
deleted file mode 100644
index 436c186..0000000
--- a/talermerchantdemos/donations/templates/javascript.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- This file is in the public domain -->
-
-<html>
- <head>
- <title>JavaScript disclaimer.</title>
- </head>
- <body>
- <h3>This site does not use JavaScript.</h3>
- </body>
-</html>
-
-<!-- The following lines are kept as a template for future inclusion of JavaScript files. -->
-
-<!--
- <html>
- <body>
- <table id="jslicense-labels1">
- <tr>
- <td><a href="/static/path/to/example0.js">example0.js</a></td>
- <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html">LGPL</a></td>
- <td><a href="/static/path/to/example0.js.tar.gz">example0.js.tar.gz</a></td>
- </tr>
- <tr>
- <td><a href="/static/path/to/example1.js">example1.js</a></td>
- <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html">LGPL</a></td>
- <td><a href="/static/path/to/example1.js.tar.gz">example1.js.tar.gz</a></td>
- </tr>
- </table>
- </body>
- </html>
--->
diff --git a/talermerchantdemos/donations/templates/language-switcher.html.j2 b/talermerchantdemos/donations/templates/language-switcher.html.j2
deleted file mode 120000
index 6423c5c..0000000
--- a/talermerchantdemos/donations/templates/language-switcher.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/language-switcher.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/donations/templates/menu.html.j2 b/talermerchantdemos/donations/templates/menu.html.j2
deleted file mode 120000
index b9575ea..0000000
--- a/talermerchantdemos/donations/templates/menu.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/menu.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/donations/translations b/talermerchantdemos/donations/translations
deleted file mode 120000
index 0a951f7..0000000
--- a/talermerchantdemos/donations/translations
+++ /dev/null
@@ -1 +0,0 @@
-../../translations/ \ No newline at end of file
diff --git a/talermerchantdemos/httpcommon/__init__.py b/talermerchantdemos/httpcommon/__init__.py
index 443160b..39cd696 100644
--- a/talermerchantdemos/httpcommon/__init__.py
+++ b/talermerchantdemos/httpcommon/__init__.py
@@ -6,6 +6,16 @@ from datetime import datetime
import time
from flask_babel import gettext
+
+class BackendException(Exception):
+ """Exception for failed communication with the Taler merchant backend"""
+
+ def __init__(self, message, backend_status=None, backend_json=None):
+ super().__init__(message)
+ self.backend_status = backend_status
+ self.backend_json = backend_json
+
+
##
# POST a request to the backend, and return a error
# response if any error occurs.
@@ -14,28 +24,30 @@ from flask_babel import gettext
# this request.
# @param json the POST's body.
# @return the backend response (JSON format).
-def backend_post(backend_url, endpoint, json):
- headers = {"Authorization": "ApiKey sandbox"}
+def backend_post(backend_url, endpoint, json, auth_token=None):
+ headers = dict()
+ if auth_token is not None:
+ headers["Authorization"] = "Bearer " + auth_token
final_url = urljoin(backend_url, endpoint)
print("POSTing to: " + final_url)
try:
resp = requests.post(final_url, json=json, headers=headers)
except requests.ConnectionError:
- err_abort(500, message=gettext("Could not establish connection to backend"))
+ raise BackendException(
+ message=gettext("Could not establish connection to backend")
+ )
try:
response_json = resp.json()
except ValueError:
- err_abort(
- 500,
+ raise BackendException(
message=gettext("Could not parse response from backend"),
- status_code=resp.status_code,
+ backend_status=resp.status_code,
)
if resp.status_code != 200:
- err_abort(
- 500,
+ raise BackendException(
message=gettext("Backend returned error status"),
- json=response_json,
- status_code=resp.status_code,
+ backend_status=resp.status_code,
+ backend_json=response_json,
)
print("Backend responds to {}: {}".format(final_url, str(response_json)))
return response_json
@@ -48,24 +60,27 @@ def backend_post(backend_url, endpoint, json):
# @param params (dict type of) URL parameters to append to the request.
# @return the JSON response from the backend, or a error response
# if something unexpected happens.
-def backend_get(backend_url, endpoint, params):
- headers = {"Authorization": "ApiKey sandbox"}
+def backend_get(backend_url, endpoint, params, auth_token=None):
+ headers = dict()
+ if auth_token is not None:
+ headers["Authorization"] = "Bearer " + auth_token
final_url = urljoin(backend_url, endpoint)
print("GETting: " + final_url + " with params: " + str(params))
try:
resp = requests.get(final_url, params=params, headers=headers)
except requests.ConnectionError:
- err_abort(500, message=gettext("Could not establish connection to backend"))
+ raise BackendException(
+ message=gettext("Could not establish connection to backend")
+ )
try:
response_json = resp.json()
except ValueError:
- err_abort(500, message=gettext("Could not parse response from backend"))
+ raise BackendException(message=gettext("Could not parse response from backend"))
if resp.status_code != 200:
- err_abort(
- 500,
+ raise BackendException(
message=gettext("Backend returned error status"),
- json=response_json,
- status_code=resp.status_code,
+ backend_status=resp.status_code,
+ backend_json=response_json,
)
print("Backend responds to {}: {}".format(final_url, str(response_json)))
return response_json
@@ -98,20 +113,6 @@ def self_localized(lang):
return "/" + lang + "/" + parts[2]
-##
-# Return a error response to the client.
-#
-# @param abort_status_code status code to return along the response.
-# @param params _kw_ arguments to passed verbatim to the templating engine.
-def err_abort(abort_status_code, **params):
- t = flask.render_template("templates/error.html.j2", lang=get_locale(), **params)
- flask.abort(flask.make_response(t, abort_status_code))
-
-
-def fallback_404(error):
- return "Page not found"
-
-
class Deadline:
def __init__(self, value):
self.value = value
diff --git a/talermerchantdemos/landing/landing.py b/talermerchantdemos/landing/landing.py
index ffbedd7..270e205 100644
--- a/talermerchantdemos/landing/landing.py
+++ b/talermerchantdemos/landing/landing.py
@@ -41,8 +41,7 @@ if not sys.version_info.major == 3 and sys.version_info.minor >= 6:
)
sys.exit(1)
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-app = flask.Flask(__name__, template_folder=BASE_DIR)
+app = flask.Flask(__name__, template_folder="../templates", static_folder="../static")
app.debug = True
app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
@@ -56,6 +55,8 @@ except ConfigurationError as ce:
print(ce)
exit(1)
+BABEL_TRANSLATION_DIRECTORIES = "../translations"
+
app.config.from_object(__name__)
babel = Babel(app)
@@ -67,8 +68,7 @@ LOGGER.info(
"Operating with the following translations available: " + " ".join(translations)
)
-# pylint: disable=no-member
-app.jinja_env.globals.update(self_localized=self_localized)
+app.add_template_global(self_localized)
@babel.localeselector
@@ -113,7 +113,7 @@ def utility_processor():
@app.errorhandler(Exception)
def internal_error(e):
return flask.render_template(
- "templates/error.html.j2",
+ "landing-error.html.j2",
message=gettext("Internal error"),
stack=traceback.format_exc(),
lang=get_locale(),
@@ -173,7 +173,7 @@ def start(lang):
merchant_survey_url = "#"
return flask.render_template(
- "templates/index.html.j2",
+ "landing-index.html.j2",
merchant_currency=CURRENCY,
lang=lang,
bank_url=bank_register_url,
@@ -186,14 +186,14 @@ def start(lang):
@app.errorhandler(404)
def handler_404(e):
return flask.render_template(
- "templates/error.html.j2", message=gettext("Page not found"), lang=get_locale()
+ "landing-error.html.j2", message=gettext("Page not found"), lang=get_locale()
)
@app.errorhandler(405)
def handler_405(e):
return flask.render_template(
- "templates/error.html.j2",
+ "landing-error.html.j2",
message=gettext("HTTP method not allowed for this page"),
lang=get_locale(),
)
diff --git a/talermerchantdemos/landing/static b/talermerchantdemos/landing/static
deleted file mode 120000
index d9bc54d..0000000
--- a/talermerchantdemos/landing/static
+++ /dev/null
@@ -1 +0,0 @@
-../static/ \ No newline at end of file
diff --git a/talermerchantdemos/landing/templates/footer.html.j2 b/talermerchantdemos/landing/templates/footer.html.j2
deleted file mode 120000
index 028b093..0000000
--- a/talermerchantdemos/landing/templates/footer.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/footer.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/landing/templates/language-switcher.html.j2 b/talermerchantdemos/landing/templates/language-switcher.html.j2
deleted file mode 120000
index 6423c5c..0000000
--- a/talermerchantdemos/landing/templates/language-switcher.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/language-switcher.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/landing/templates/menu.html.j2 b/talermerchantdemos/landing/templates/menu.html.j2
deleted file mode 120000
index b9575ea..0000000
--- a/talermerchantdemos/landing/templates/menu.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/menu.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/landing/translations b/talermerchantdemos/landing/translations
deleted file mode 120000
index 0a951f7..0000000
--- a/talermerchantdemos/landing/translations
+++ /dev/null
@@ -1 +0,0 @@
-../../translations/ \ No newline at end of file
diff --git a/talermerchantdemos/survey/static b/talermerchantdemos/survey/static
deleted file mode 120000
index d9bc54d..0000000
--- a/talermerchantdemos/survey/static
+++ /dev/null
@@ -1 +0,0 @@
-../static/ \ No newline at end of file
diff --git a/talermerchantdemos/survey/survey.py b/talermerchantdemos/survey/survey.py
index 871b417..4813477 100644
--- a/talermerchantdemos/survey/survey.py
+++ b/talermerchantdemos/survey/survey.py
@@ -30,18 +30,21 @@ 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
+from ..httpcommon import backend_get, backend_post, self_localized, BackendException
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))
+ 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 = flask.Flask(__name__, template_folder="../templates", static_folder="../static")
app.debug = True
-app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8')
+app.secret_key = base64.b64encode(os.urandom(64)).decode("utf-8")
LOGGER = logging.getLogger(__name__)
TC = TalerConfig.from_env()
@@ -53,22 +56,27 @@ except ConfigurationError as ce:
print(ce)
exit(1)
+BABEL_TRANSLATION_DIRECTORIES = "../translations"
+
app.config.from_object(__name__)
babel = Babel(app)
INSTANCED_URL = urljoin(BACKEND_URL, f"instances/survey/")
-LOGGER.info("Using translations from:" + ':'.join(list(babel.translation_directories)))
+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))
+if not "en" in translations:
+ translations.append("en")
+LOGGER.info(
+ "Operating with the following translations available: " + " ".join(translations)
+)
+
+app.add_template_global(self_localized)
-app.jinja_env.globals.update(self_localized=self_localized)
@babel.localeselector
def get_locale():
- parts = request.path.split('/', 2)
- if (2 >= len(parts)):
+ parts = request.path.split("/", 2)
+ if 2 >= len(parts):
# Totally unexpected path format, do not localize
return "en"
lang = parts[1]
@@ -76,6 +84,7 @@ def get_locale():
return lang
return "en"
+
##
# Make the environment available into templates.
#
@@ -106,23 +115,24 @@ def utility_processor():
@app.errorhandler(Exception)
def internal_error(e):
return flask.render_template(
- "templates/error.html.j2",
+ "survey-error.html.j2",
message=gettext("Internal error"),
stack=traceback.format_exc(),
- lang=get_locale()
+ 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'))
+ LOGGER.info("will look into: " + os.path.join(app.root_path, "static"))
return flask.send_from_directory(
- os.path.join(app.root_path, 'static'),
+ os.path.join(app.root_path, "static"),
"favicon.ico",
- mimetype="image/vnd.microsoft.ico"
+ mimetype="image/vnd.microsoft.ico",
)
@@ -139,21 +149,23 @@ def submit_survey(lang):
tip_spec = dict(
amount=CURRENCY + ":1.0",
next_url=os.environ.get("TALER_ENV_URL_INTRO", "https://taler.net/"),
- justification="Payment methods survey"
+ justification="Payment methods survey",
)
- backend_resp = backend_post(INSTANCED_URL, "private/tips", tip_spec)
+ backend_resp = backend_post(INSTANCED_URL, "private/tips", tip_spec, auth_token=APIKEY)
return flask.redirect(backend_resp["tip_status_url"])
+
##
# Serve the main index page, redirecting to /<lang>/
#
# @return response object of the index page.
@app.route("/")
def index():
- default = 'en'
+ default = "en"
target = flask.request.accept_languages.best_match(translations, default)
return flask.redirect("/" + target + "/", code=302)
+
##
# Serve the internationalized main index page.
#
@@ -161,21 +173,33 @@ def index():
@app.route("/<lang>/", methods=["GET"])
def start(lang):
return flask.render_template(
- "templates/index.html.j2", merchant_currency=CURRENCY, lang=lang
+ "survey-index.html.j2", merchant_currency=CURRENCY, lang=lang
)
+
@app.errorhandler(404)
-def handler(e):
+def handler_404(e):
return flask.render_template(
- "templates/error.html.j2",
- message=gettext("Page not found"),
- lang=get_locale()
+ "survey-error.html.j2", message=gettext("Page not found"), lang=get_locale()
)
+
@app.errorhandler(405)
-def handler(e):
+def handler_405(e):
return flask.render_template(
- "templates/error.html.j2",
+ "survey-error.html.j2",
message=gettext("HTTP method not allowed for this page"),
- lang=get_locale()
+ lang=get_locale(),
+ )
+
+
+@app.errorhandler(BackendException)
+def handler_backend_exception(e):
+ t = flask.render_template(
+ "survey-error.html.j2",
+ lang=get_locale(),
+ message=e.args[0],
+ json=e.backend_json,
+ status_code=e.backend_status,
)
+ return flask.make_response(t, 500) \ No newline at end of file
diff --git a/talermerchantdemos/survey/templates/base.html.j2 b/talermerchantdemos/survey/templates/base.html.j2
deleted file mode 100644
index 734da6f..0000000
--- a/talermerchantdemos/survey/templates/base.html.j2
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!--
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015, 2016, 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
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--->
-
-<html data-taler-nojs="true">
-<head>
- <title>{{ gettext("GNU Taler Demo: Survey") }}</title>
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='pure.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='demo.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='navbar.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='torsten.css') }}" />
- {% block styles %}{% endblock %}
- {% block scripts %}{% endblock %}
-</head>
-
-<body>
- <header class="demobar" style="display: flex; flex-direction: column;">
- <h1><span class="tt adorn-brackets">{{ gettext("Taler Demo") }}</span><br/>
- <span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY') }}">{{ gettext("Survey") }}</a></span></h1>
- <p>{{
- gettext("This page demonstrates how to tip visitors for completing small tasks.") + "<br>" +
- gettext("Tipping is a way for offer cash rewards that go directly into a user's wallet.")
- }}
- </p>
- </header>
- {% from 'templates/menu.html.j2' import menu with context %} {{ menu('survey') }}
-
- <section id="main" class="content">
- {% block main %}
- This is the main content of the page.
- {% endblock %}
- {% include 'templates/footer.html.j2' %}
- </section>
-</body>
-</html>
diff --git a/talermerchantdemos/survey/templates/footer.html.j2 b/talermerchantdemos/survey/templates/footer.html.j2
deleted file mode 120000
index 028b093..0000000
--- a/talermerchantdemos/survey/templates/footer.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/footer.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/survey/templates/language-switcher.html.j2 b/talermerchantdemos/survey/templates/language-switcher.html.j2
deleted file mode 120000
index 6423c5c..0000000
--- a/talermerchantdemos/survey/templates/language-switcher.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/language-switcher.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/survey/templates/menu.html.j2 b/talermerchantdemos/survey/templates/menu.html.j2
deleted file mode 120000
index b9575ea..0000000
--- a/talermerchantdemos/survey/templates/menu.html.j2
+++ /dev/null
@@ -1 +0,0 @@
-../../templates/menu.html.j2 \ No newline at end of file
diff --git a/talermerchantdemos/survey/translations b/talermerchantdemos/survey/translations
deleted file mode 120000
index 0a951f7..0000000
--- a/talermerchantdemos/survey/translations
+++ /dev/null
@@ -1 +0,0 @@
-../../translations/ \ No newline at end of file
diff --git a/talermerchantdemos/blog/templates/article_frame.html.j2 b/talermerchantdemos/templates/blog-article-frame.html.j2
index 2690306..1b51c9d 100644
--- a/talermerchantdemos/blog/templates/article_frame.html.j2
+++ b/talermerchantdemos/templates/blog-article-frame.html.j2
@@ -1,15 +1,20 @@
-{% extends "templates/base.html.j2" %}
+{% extends "blog-base.html.j2" %}
{% block main %}
-{% include "articles/" + lang + "/" + article_file %}
-{% if refundable %}
+{{ article_contents | safe }}
+
<hr>
+
+{% if refundable %}
<p class="graybox">
{{
gettext("Taler allows merchants to offer refunds to customers.") +
gettext('You can <a href="{url}">request a refund</a> within the first hour after buying this article.').format(url=url_for('confirm_refund', lang='en', order_id=order_id))
}}
</p>
+{% else %}
+{{ gettext("Taler allows merchants to offer refunds to customers.") }}
+{{ gettext("This article can't be refunded anymore.") }}
{% endif %}
{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/article_refunded.html.j2 b/talermerchantdemos/templates/blog-article-refunded.html.j2
index d4e405c..6a2e139 100644
--- a/talermerchantdemos/blog/templates/article_refunded.html.j2
+++ b/talermerchantdemos/templates/blog-article-refunded.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "blog-base.html.j2" %}
{% block main %}
<h2>{{ gettext("Refunded") }}</h2>
diff --git a/talermerchantdemos/templates/blog-base.html.j2 b/talermerchantdemos/templates/blog-base.html.j2
new file mode 100644
index 0000000..e533bd4
--- /dev/null
+++ b/talermerchantdemos/templates/blog-base.html.j2
@@ -0,0 +1,13 @@
+{% extends "common-base.html.j2" %}
+
+{% block header_content %}
+
+<h1><span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG') }}">{{ gettext("Essay shop") }}</a></span></h1>
+<p>{{
+ gettext("On this page you can buy articles using an imaginary currency.") + "<br>" +
+ gettext("The articles are chapters from Richard Stallman's book &quot;Free Software, Free Society&quot;.") + "<br>" +
+ gettext('The book is <a href="{shop}">published by the FSF</a> and available gratis at <a href="{gnu}">gnu.org</a>.').format(shop="https://shop.fsf.org/product/free-software-free-society-2", gnu="https://www.gnu.org")
+ }}
+</p>
+
+{% endblock %}
diff --git a/talermerchantdemos/blog/templates/confirm_refund.html.j2 b/talermerchantdemos/templates/blog-confirm-refund.html.j2
index a356ea1..ab5dfcd 100644
--- a/talermerchantdemos/blog/templates/confirm_refund.html.j2
+++ b/talermerchantdemos/templates/blog-confirm-refund.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "blog-base.html.j2" %}
{% block main %}
<h1>{{ gettext("Confirm refund request for article") }}</h1>
@@ -13,11 +13,11 @@
<p>
{{
gettext ("You will only be able to receive the refund on the same wallet that you have used to pay for this article originally.")
- }}
+ }}
</p>
<form action="{{ url_for('refund', order_id=order_id) }}" method="POST">
<input type="text" name="article_name" value={{ article_name}} hidden>
- <input type="submit" value="{{gettext("Request refund")}}">
+ <input type="submit" value="{{ gettext('Request refund') }}">
</form>
{% endblock main %}
diff --git a/talermerchantdemos/donations/templates/error.html.j2 b/talermerchantdemos/templates/blog-error.html.j2
index ffc2e1f..5b2a381 100644
--- a/talermerchantdemos/donations/templates/error.html.j2
+++ b/talermerchantdemos/templates/blog-error.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "blog-base.html.j2" %}
{% block main %}
<h1>{{ gettext("Error encountered") }}</h1>
diff --git a/talermerchantdemos/blog/templates/index.html.j2 b/talermerchantdemos/templates/blog-index.html.j2
index f231e6f..dd1bced 100644
--- a/talermerchantdemos/blog/templates/index.html.j2
+++ b/talermerchantdemos/templates/blog-index.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "blog-base.html.j2" %}
{% block main %}
<h2>{{ gettext("Essay Shop: Free Software, Free Society") }} </h2>
<div style="font-size: smaller;">
diff --git a/talermerchantdemos/landing/templates/base.html.j2 b/talermerchantdemos/templates/common-base.html.j2
index 97a91a5..ff8d655 100644
--- a/talermerchantdemos/landing/templates/base.html.j2
+++ b/talermerchantdemos/templates/common-base.html.j2
@@ -28,21 +28,23 @@
<body>
<header class="demobar" style="display: flex; flex-direction: column;">
- <h1><img src="{{ url_for('static', filename='logo-white.svg') }}" height="100"><br/>
- <span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY') }}">{{ gettext("Introduction") }}</a></span></h1>
- <p>{{
- gettext("This is the GNU Taler demo.") + "<br>" +
- gettext("Here you can try out the GNU Taler payment system using a toy currency.")
- }}
+ <p>
+ <img src="{{ url_for('static', filename='logo-white.svg') }}" height="100"><br/>
</p>
+
+ <div>
+ {% block header_content %}
+ <p>This is the header content.</p>
+ {% endblock %}
+ </div>
</header>
- {% from 'templates/menu.html.j2' import menu with context %} {{ menu('landing') }}
+ {% from 'menu.html.j2' import menu with context %} {{ menu('landing') }}
<section id="main" class="content">
{% block main %}
This is the main content of the page.
{% endblock %}
- {% include 'templates/footer.html.j2' %}
+ {% include 'footer.html.j2' %}
</section>
</body>
</html>
diff --git a/talermerchantdemos/templates/donations-base.html.j2 b/talermerchantdemos/templates/donations-base.html.j2
new file mode 100644
index 0000000..b214eac
--- /dev/null
+++ b/talermerchantdemos/templates/donations-base.html.j2
@@ -0,0 +1,13 @@
+{% extends "common-base.html.j2" %}
+
+{% block header_content %}
+
+<h1>
+<span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS') }}">{{gettext("Donations")}}</a></span></h1>
+<p>{{
+gettext ("This is the donation page.") + "<br>" +
+gettext ("Using this page you can make donations in {currency} to Free Software projects.").format(currency=merchant_currency)
+}}
+</p>
+
+{% endblock %}
diff --git a/talermerchantdemos/donations/templates/checkout.html.j2 b/talermerchantdemos/templates/donations-checkout.html.j2
index 5b6900f..ec63d9f 100644
--- a/talermerchantdemos/donations/templates/checkout.html.j2
+++ b/talermerchantdemos/templates/donations-checkout.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "donations-base.html.j2" %}
{% block main %}
<article>
diff --git a/talermerchantdemos/blog/templates/error.html.j2 b/talermerchantdemos/templates/donations-error.html.j2
index ffc2e1f..8c48335 100644
--- a/talermerchantdemos/blog/templates/error.html.j2
+++ b/talermerchantdemos/templates/donations-error.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "donations-base.html.j2" %}
{% block main %}
<h1>{{ gettext("Error encountered") }}</h1>
diff --git a/talermerchantdemos/donations/templates/fulfillment.html.j2 b/talermerchantdemos/templates/donations-fulfillment.html.j2
index 5fd799b..b49b7dc 100644
--- a/talermerchantdemos/donations/templates/fulfillment.html.j2
+++ b/talermerchantdemos/templates/donations-fulfillment.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "donations-base.html.j2" %}
{% block main %}
<h2>{{ gettext("Donation Receipt") }}</h2>
diff --git a/talermerchantdemos/donations/templates/index.html.j2 b/talermerchantdemos/templates/donations-index.html.j2
index 87bce85..25a6768 100644
--- a/talermerchantdemos/donations/templates/index.html.j2
+++ b/talermerchantdemos/templates/donations-index.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "donations-base.html.j2" %}
{% block main %}
<h2>{{ gettext("Donate to Free Software projects") }}</h2>
diff --git a/talermerchantdemos/donations/templates/provider-not-supported.html.j2 b/talermerchantdemos/templates/donations-provider-not-supported.html.j2
index a4d5d80..a92af77 100644
--- a/talermerchantdemos/donations/templates/provider-not-supported.html.j2
+++ b/talermerchantdemos/templates/donations-provider-not-supported.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html" %}
+{% extends "donations-base.html.j2" %}
{% block main %}
<h2>{{ gettext("Payment Provider Not Supported") }}</h2>
diff --git a/talermerchantdemos/templates/landing-base.html.j2 b/talermerchantdemos/templates/landing-base.html.j2
new file mode 100644
index 0000000..e279767
--- /dev/null
+++ b/talermerchantdemos/templates/landing-base.html.j2
@@ -0,0 +1,12 @@
+{% extends "common-base.html.j2" %}
+
+{% block header_content %}
+
+<h1><span class="it"><a href="{{ env('TALER_ENV_URL_LANDING') }}">{{ gettext("Introduction") }}</a></span></h1>
+<p>{{
+ gettext("This is the GNU Taler demo.") + "<br>" +
+ gettext("Here you can try out the GNU Taler payment system using a toy currency.")
+ }}
+</p>
+
+{% endblock %}
diff --git a/talermerchantdemos/landing/templates/error.html.j2 b/talermerchantdemos/templates/landing-error.html.j2
index 009947c..7909656 100644
--- a/talermerchantdemos/landing/templates/error.html.j2
+++ b/talermerchantdemos/templates/landing-error.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "landing-base.html.j2" %}
{% block main %}
<h1>{{ gettext("Error encountered") }}</h1>
diff --git a/talermerchantdemos/landing/templates/index.html.j2 b/talermerchantdemos/templates/landing-index.html.j2
index 9beb7c0..16145e3 100644
--- a/talermerchantdemos/landing/templates/index.html.j2
+++ b/talermerchantdemos/templates/landing-index.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "landing-base.html.j2" %}
{% block main %}
<article>
<h2>{{ gettext("Step 1: Install the Taler wallet") }}</h2>
diff --git a/talermerchantdemos/templates/menu.html.j2 b/talermerchantdemos/templates/menu.html.j2
index c364a2c..937030d 100644
--- a/talermerchantdemos/templates/menu.html.j2
+++ b/talermerchantdemos/templates/menu.html.j2
@@ -16,10 +16,7 @@
<a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') + lang + '/' }}"
{% if active == 'survey' %} class="active" {% endif %}
>{{gettext("Tipping/Survey")}}</a>
- <!-- a href="{{ env('TALER_ENV_URL_BACKOFFICE', '#') + lang + '/' }}"
- {% if active == 'backoffice' %} class="active" {% endif %}
- >{{gettext("Back-office")}}</a -->
- {% include 'templates/language-switcher.html.j2' %}
+ {% include 'language-switcher.html.j2' %}
</nav>
</div>
{%- endmacro %}
diff --git a/talermerchantdemos/templates/survey-base.html.j2 b/talermerchantdemos/templates/survey-base.html.j2
new file mode 100644
index 0000000..f35f514
--- /dev/null
+++ b/talermerchantdemos/templates/survey-base.html.j2
@@ -0,0 +1,13 @@
+{% extends "common-base.html.j2" %}
+
+{% block header_content %}
+
+<h1>
+<span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY') }}">{{ gettext("Survey") }}</a></span></h1>
+<p>{{
+gettext("This page demonstrates how to tip visitors for completing small tasks.") + "<br>" +
+gettext("Tipping is a way for offer cash rewards that go directly into a user's wallet.")
+}}
+</p>
+
+{% endblock %}
diff --git a/talermerchantdemos/survey/templates/error.html.j2 b/talermerchantdemos/templates/survey-error.html.j2
index 844da08..b2f84f9 100644
--- a/talermerchantdemos/survey/templates/error.html.j2
+++ b/talermerchantdemos/templates/survey-error.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "survey-base.html.j2" %}
{% block main %}
<h1>{{ gettext("Error encountered") }}</h1>
diff --git a/talermerchantdemos/survey/templates/index.html.j2 b/talermerchantdemos/templates/survey-index.html.j2
index 7c449da..e7d323b 100644
--- a/talermerchantdemos/survey/templates/index.html.j2
+++ b/talermerchantdemos/templates/survey-index.html.j2
@@ -1,4 +1,4 @@
-{% extends "templates/base.html.j2" %}
+{% extends "survey-base.html.j2" %}
{% block main %}
<div>
<p>