diff options
Diffstat (limited to 'talermerchantdemos')
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 "Free Software, Free Society".") + "<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 "Free Software, Free Society".") + "<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> |