summaryrefslogtreecommitdiff
path: root/talermerchantdemos/blog
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-04-21 21:07:18 +0200
committerFlorian Dold <florian@dold.me>2021-04-21 21:07:18 +0200
commit2b84f3bd5de3a500b63193924bea2f3dfd0c9afd (patch)
tree08d5a2bd354ab7849d731eabe7b132429b6d2225 /talermerchantdemos/blog
parent4356540b2eb12b2c307270566b0b4ce969e12c60 (diff)
downloadtaler-merchant-demos-2b84f3bd5de3a500b63193924bea2f3dfd0c9afd.tar.gz
taler-merchant-demos-2b84f3bd5de3a500b63193924bea2f3dfd0c9afd.tar.bz2
taler-merchant-demos-2b84f3bd5de3a500b63193924bea2f3dfd0c9afd.zip
common base template, new logo and lots of cleanup
Diffstat (limited to 'talermerchantdemos/blog')
-rw-r--r--talermerchantdemos/blog/blog.py190
-rw-r--r--talermerchantdemos/blog/content.py36
l---------talermerchantdemos/blog/static1
-rw-r--r--talermerchantdemos/blog/templates/article_frame.html.j215
-rw-r--r--talermerchantdemos/blog/templates/article_refunded.html.j218
-rw-r--r--talermerchantdemos/blog/templates/base.html.j252
-rw-r--r--talermerchantdemos/blog/templates/confirm_refund.html.j223
-rw-r--r--talermerchantdemos/blog/templates/error.html.j224
l---------talermerchantdemos/blog/templates/footer.html.j21
-rw-r--r--talermerchantdemos/blog/templates/index.html.j236
-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
14 files changed, 140 insertions, 290 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/article_frame.html.j2 b/talermerchantdemos/blog/templates/article_frame.html.j2
deleted file mode 100644
index 2690306..0000000
--- a/talermerchantdemos/blog/templates/article_frame.html.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "templates/base.html.j2" %}
-{% block main %}
-{% include "articles/" + lang + "/" + article_file %}
-
-{% if refundable %}
-<hr>
-<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>
-{% endif %}
-
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/article_refunded.html.j2 b/talermerchantdemos/blog/templates/article_refunded.html.j2
deleted file mode 100644
index d4e405c..0000000
--- a/talermerchantdemos/blog/templates/article_refunded.html.j2
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "templates/base.html.j2" %}
-{% block main %}
-
-<h2>{{ gettext("Refunded") }}</h2>
-
-<p>
-{{
- gettext("Your payment (order ID <tt>{order}<tt>) for the article "{article}" has been refunded.").format(order=order_id,article=article_name)
-}}
-</p>
-
-<p>
-{{
- gettext("You will not be able to read the article until you pay for it again.")
-}}
-</p>
-
-{% endblock main %}
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/confirm_refund.html.j2 b/talermerchantdemos/blog/templates/confirm_refund.html.j2
deleted file mode 100644
index a356ea1..0000000
--- a/talermerchantdemos/blog/templates/confirm_refund.html.j2
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "templates/base.html.j2" %}
-{% block main %}
- <h1>{{ gettext("Confirm refund request for article") }}</h1>
-
- <p>
- {{
- gettext("Do you want to get a refund for the article <em>{name}</em>?").format(name=article_name) + "<br/>" +
- gettext("In this demonstration, refunds will be automatically approved by the merchant.") + "<br/>" +
- gettext("After you have obtained a refund, you will not be able to read the article anymore.")
- }}
- </p>
-
- <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")}}">
- </form>
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/error.html.j2 b/talermerchantdemos/blog/templates/error.html.j2
deleted file mode 100644
index ffc2e1f..0000000
--- a/talermerchantdemos/blog/templates/error.html.j2
+++ /dev/null
@@ -1,24 +0,0 @@
-{% extends "templates/base.html.j2" %}
-{% block main %}
- <h1>{{ gettext("Error encountered") }}</h1>
-
- <p>{{ message }}</p>
-
- {% if status_code %}
- <p>
- {{ gettext ("The backend returned status code {code}.").format(code=status_code) }}.
- </p>
- {% endif %}
-
- {% if json %}
- <p>{{gettext("Backend response:")}}</p>
- <pre>{{ json }}</pre>
- {% endif %}
-
- {% if stack %}
- <p>{{gettext("Stack trace:")}}</p>
- <pre>
- {{ stack }}
- </pre>
- {% endif %}
-{% endblock main %}
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/index.html.j2 b/talermerchantdemos/blog/templates/index.html.j2
deleted file mode 100644
index f231e6f..0000000
--- a/talermerchantdemos/blog/templates/index.html.j2
+++ /dev/null
@@ -1,36 +0,0 @@
-{% extends "templates/base.html.j2" %}
-{% block main %}
- <h2>{{ gettext("Essay Shop: Free Software, Free Society") }} </h2>
- <div style="font-size: smaller;">
- <p>{{ gettext("This is the latest edition of <cite>Free Software, Free Society: Selected Essays of Richard M. Stallman.</cite>") }} <br>
- Free Software Foundation<br>
- 51 Franklin Street, Fifth Floor<br>
- Boston, MA 02110-1335
- <br>
- Copyright &copy; 2002, 2010 Free Software Foundation, Inc.
- </p>
-
- <p>{{ gettext("Verbatim copying and distribution of this entire book are permitted worldwide, without royalty, in any medium, provided this notice is preserved. Permission is granted to copy and distribute translations of this book from the original English into another language provided the translation has been approved by the Free Software Foundation and the copyright notice and this permission notice are preserved on all copies.") }}
- </p>
- <p>ISBN 978-0-9831592-0-9</p>
- </div>
-
- <h2>{{ gettext("Chapters") }} </h2>
- <div>
- {{
- gettext("Click on an individual chapter to to purchase it with GNU Taler.") +
- gettext('You can get free, virtual money to buy articles on this page at the <a href="{}")">bank</a>').format(env('TALER_ENV_URL_BANK'))
- }}
- </div>
-
- <div>
- {% for article in articles %}
- <div class="notice">
- <h3><a href="{{ url_for('article', lang=article.lang, article_name=article.slug) }}" class="articleTitle">{{article.title}}</a></h3>
- <p>{{ article.teaser|safe }} <a href="{{ url_for('article', lang=article.lang, article_name=article.slug) }}">({{ gettext('Pay to read more...') }})</a></p>
- </div>
- {% else %}
- <em>({{ gettext("No articles available in this language.") }})</em>
- {% endfor %}
- </div>
-{% endblock main %}
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