gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-playground] 01/01: initial commit


From: gnunet
Subject: [GNUnet-SVN] [taler-playground] 01/01: initial commit
Date: Tue, 27 Feb 2018 17:54:00 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository playground.

commit 0ad935178a3ab2700a5fda3702fd2c36bacd8a56
Author: Florian Dold <address@hidden>
AuthorDate: Thu Feb 22 18:18:00 2018 +0100

    initial commit
---
 .gitmodules                                        |   4 +
 ChangeLog                                          |   0
 Makefile.in                                        |  55 ++
 bootstrap                                          |  12 +
 configure.ac                                       |  66 +++
 m4/ax_compare_version.m4                           | 177 ++++++
 m4/python.m4                                       | 646 +++++++++++++++++++++
 playground.conf                                    |  12 +
 setup.py                                           |  22 +
 taler-playground.in                                |  85 +++
 taler-playground.wsgi.in                           |  21 +
 talerplayground/__init__.py                        |   0
 talerplayground/playground/__init__.py             |   3 +
 talerplayground/playground/playground.py           | 106 ++++
 talerplayground/playground/templates/base.html     |  54 ++
 talerplayground/playground/templates/index.html    |  57 ++
 .../playground/templates/javascript.html           |  22 +
 talerplayground/playground/web-common              |   1 +
 talerplayground/talerconfig.py                     | 342 +++++++++++
 19 files changed, 1685 insertions(+)

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..d0c1f7c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "talerplayground/playground/web-common"]
+       path = talerplayground/playground/web-common
+       url = git://taler.net/web-common
+       branch = master
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..c7b8750
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,55 @@
+INSTALL = install
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+prefix = @prefix@
+srcdir = @srcdir@
+
+script_templates = taler-merchant-donations frontend-donations.wsgi
+templates = Makefile $(script_templates)
+
+edit = sed -e 's|@address@hidden|$(prefix)|g'
+
+.PHONY: all
+all: $(templates)
+       cd talerdonations/donations/static/web-common && make && cd -
+
+Makefile: Makefile.in
+       ./config.status $@
+
+$(script_templates): %: Makefile %.in
+       rm -f $@ address@hidden
+       $(edit) '$(srcdir)/address@hidden' >address@hidden
+       mv address@hidden $@
+
+
+.PHONY: install-data
+install-data: $(templates)
+       @$(INSTALL_DATA) -Dt $(prefix)/share/taler/ frontend-donations.wsgi
+
+#      @test -n "$$(ls -A talerbank/app/static/web-common/)" || \
+#      (echo "please check out git submodules"; exit 1)
+
+
+
+# link package under prefix to source tree
+.PHONY: devinstall
+devinstall: $(templates) install-data
+       @pip3 install -e . --install-option="address@hidden@"
+
+
+# install into prefix
+.PHONY: install
+install: $(templates) install-data
+       @pip3 install . --install-option="address@hidden@"
+       @# force update when sources changed
+       @pip3 install . --install-option="address@hidden@" --upgrade --no-deps
+       cd talerdonations/donations/static/web-common && make install && cd -
+
+# run testcases
+.PHONY: check
+check:
+       @export address@hidden@/talerdonations/tests.conf; \
+        python3 setup.py test
+
+pylint:
+       @pylint talerdonations/
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..373e5f5
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if ! git --version >/dev/null; then
+  echo "git not installed"
+  exit 1
+fi
+
+echo "$0: Updating submodules"
+echo | git submodule update --init
+
+echo "$0: Running autoreconf"
+autoreconf -if
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..7685aa2
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,66 @@
+AC_INIT([talerplayground], [0.1.0], address@hidden)
+
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE
+AC_PROG_AWK
+AC_PROG_SED
+
+#
+# Check for Python
+#
+
+PC_INIT([3.4])
+pyheaders=0
+PC_PYTHON_CHECK_HEADERS([pyheaders=1])
+PC_PYTHON_CHECK_VERSION()
+
+#
+# Check for pip3
+#
+
+AC_MSG_CHECKING([pip3])
+pip3 --version >/dev/null
+if test $? -ne 0;
+  then
+  AC_MSG_ERROR([Please install pip3>=6.0])
+fi
+
+VERSION=$(pip3 --version | $AWK '{ print $2 }')
+
+AC_MSG_RESULT([$VERSION])
+
+AX_COMPARE_VERSION([$VERSION],[lt],[6.0], [AC_MSG_ERROR([Please install 
pip3>=6.0])])
+
+
+#
+# Check for tsc
+#
+AC_CHECK_PROG([tsc],[tsc],[yes],[no])
+AM_CONDITIONAL([HAVE_TSC], [test "x$tsc" = xyes])
+
+#
+# Check for minifier
+#
+AC_MSG_CHECKING([Checking for jsmin])
+python3 -m jsmin &> /dev/null
+if test $? -ne 0;
+  then
+  AC_MSG_ERROR([Please install Python3 module 'jsmin'])
+fi
+
+#
+# Report
+#
+if test x$pyheaders != x1; then
+  AC_MSG_WARN([Python headers not installed, might be required to build uwsgi])
+fi
+
+
+#
+# Finish
+#
+
+AC_CONFIG_FILES([Makefile
+  talerplayground/playground/static/web-common/Makefile])
+
+AC_OUTPUT
diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4
new file mode 100644
index 0000000..74dc0fd
--- /dev/null
+++ b/m4/ax_compare_version.m4
@@ -0,0 +1,177 @@
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_compare_version.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], 
[ACTION-IF-FALSE])
+#
+# DESCRIPTION
+#
+#   This macro compares two version strings. Due to the various number of
+#   minor-version numbers that can exist, and the fact that string
+#   comparisons are not compatible with numeric comparisons, this is not
+#   necessarily trivial to do in a autoconf script. This macro makes doing
+#   these comparisons easy.
+#
+#   The six basic comparisons are available, as well as checking equality
+#   limited to a certain number of minor-version levels.
+#
+#   The operator OP determines what type of comparison to do, and can be one
+#   of:
+#
+#    eq  - equal (test A == B)
+#    ne  - not equal (test A != B)
+#    le  - less than or equal (test A <= B)
+#    ge  - greater than or equal (test A >= B)
+#    lt  - less than (test A < B)
+#    gt  - greater than (test A > B)
+#
+#   Additionally, the eq and ne operator can have a number after it to limit
+#   the test to that number of minor versions.
+#
+#    eq0 - equal up to the length of the shorter version
+#    ne0 - not equal up to the length of the shorter version
+#    eqN - equal up to N sub-version levels
+#    neN - not equal up to N sub-version levels
+#
+#   When the condition is true, shell commands ACTION-IF-TRUE are run,
+#   otherwise shell commands ACTION-IF-FALSE are run. The environment
+#   variable 'ax_compare_version' is always set to either 'true' or 'false'
+#   as well.
+#
+#   Examples:
+#
+#     AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
+#     AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
+#
+#   would both be true.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
+#     AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
+#
+#   would both be false.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
+#
+#   would be true because it is only comparing two minor versions.
+#
+#     AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
+#
+#   would be true because it is only comparing the lesser number of minor
+#   versions of the two values.
+#
+#   Note: The characters that separate the version numbers do not matter. An
+#   empty string is the same as version 0. OP is evaluated by autoconf, not
+#   configure, so must be a string, not a variable.
+#
+#   The author would like to acknowledge Guido Draheim whose advice about
+#   the m4_case and m4_ifvaln functions make this macro only include the
+#   portions necessary to perform the specific comparison specified by the
+#   OP argument in the final configure script.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Tim Toolan <address@hidden>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 11
+
+dnl #########################################################################
+AC_DEFUN([AX_COMPARE_VERSION], [
+  AC_REQUIRE([AC_PROG_AWK])
+
+  # Used to indicate true or false condition
+  ax_compare_version=false
+
+  # Convert the two version strings to be compared into a format that
+  # allows a simple string comparison.  The end result is that a version
+  # string of the form 1.12.5-r617 will be converted to the form
+  # 0001001200050617.  In other words, each number is zero padded to four
+  # digits, and non digits are removed.
+  AS_VAR_PUSHDEF([A],[ax_compare_version_A])
+  A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  AS_VAR_PUSHDEF([B],[ax_compare_version_B])
+  B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
+  dnl # then the first line is used to determine if the condition is true.
+  dnl # The sed right after the echo is to remove any indented white space.
+  m4_case(m4_tolower($2),
+  [lt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [gt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [le],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],
+  [ge],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],[
+    dnl Split the operator from the subversion count if present.
+    m4_bmatch(m4_substr($2,2),
+    [0],[
+      # A count of zero means use the length of the shorter version.
+      # Determine the number of characters in A and B.
+      ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
+      ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
+
+      # Set A to no more than B's length and B to no more than A's length.
+      A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
+    ],
+    [[0-9]+],[
+      # A count greater than zero means use only that many subversions
+      A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+    ],
+    [.+],[
+      AC_WARNING(
+        [illegal OP numeric parameter: $2])
+    ],[])
+
+    # Pad zeros at end of numbers to make same length.
+    ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
+    B="$B`echo $A | sed 's/./0/g'`"
+    A="$ax_compare_version_tmp_A"
+
+    # Check for equality or inequality as necessary.
+    m4_case(m4_tolower(m4_substr($2,0,2)),
+    [eq],[
+      test "x$A" = "x$B" && ax_compare_version=true
+    ],
+    [ne],[
+      test "x$A" != "x$B" && ax_compare_version=true
+    ],[
+      AC_WARNING([illegal OP parameter: $2])
+    ])
+  ])
+
+  AS_VAR_POPDEF([A])dnl
+  AS_VAR_POPDEF([B])dnl
+
+  dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
+  if test "$ax_compare_version" = "true" ; then
+    m4_ifvaln([$4],[$4],[:])dnl
+    m4_ifvaln([$5],[else $5])dnl
+  fi
+]) dnl AX_COMPARE_VERSION
diff --git a/m4/python.m4 b/m4/python.m4
new file mode 100644
index 0000000..98a68b8
--- /dev/null
+++ b/m4/python.m4
@@ -0,0 +1,646 @@
+# Copyright 2012, 2013, 2014 Brandon Invergo <address@hidden>
+#
+# This file is part of pyconfigure.  This program is free
+# software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# Under Section 7 of GPL version 3, you are granted additional
+# permissions described in the Autoconf Configure Script Exception,
+# version 3.0, as published by the Free Software Foundation.
+#
+# You should have received a copy of the GNU General Public License
+# and a copy of the Autoconf Configure Script Exception along with
+# this program; see the files COPYINGv3 and COPYING.EXCEPTION
+# respectively.  If not, see <http://www.gnu.org/licenses/>.
+
+
+# Many of these macros were adapted from ones written by Andrew Dalke
+# and James Henstridge and are included with the Automake utility
+# under the following copyright terms:
+#
+# Copyright (C) 1999-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Table of Contents:
+#
+# 1. Language selection
+#    and routines to produce programs in a given language.
+#
+# 2. Producing programs in a given language.
+#
+# 3. Looking for a compiler
+#    And possibly the associated preprocessor.
+#
+# 4. Looking for specific libs & functionality
+
+
+## ----------------------- ##
+## 1. Language selection.  ##
+## ----------------------- ##
+
+
+# AC_LANG(Python)
+# ---------------
+AC_LANG_DEFINE([Python], [py], [PY], [PYTHON], [],
+[ac_ext=py
+ac_compile='chmod +x conftest.$ac_ext >&AS_MESSAGE_LOG_FD'
+ac_link='chmod +x conftest.$ac_ext && cp conftest.$ac_ext conftest 
>&AS_MESSAGE_LOG_FD'
+])
+
+
+# AC_LANG_PYTHON
+# --------------
+AU_DEFUN([AC_LANG_PYTHON], [AC_LANG(Python)])
+
+
+## ----------------------- ##
+## 2. Producing programs.  ##
+## ----------------------- ##
+
+
+# AC_LANG_PROGRAM(Python)([PROLOGUE], [BODY])
+# -------------------------------------------
+m4_define([AC_LANG_PROGRAM(Python)], [dnl
address@hidden:@!$PYTHON
+$1
+m4_if([$2], [], [], [dnl
+if __name__ == '__main__':
+$2])])
+
+
+# _AC_LANG_IO_PROGRAM(Python)
+# ---------------------------
+# Produce source that performs I/O.
+m4_define([_AC_LANG_IO_PROGRAM(Python)],
+[AC_LANG_PROGRAM([dnl
+import sys
+try:
+    h = open('conftest.out')
+except:
+    sys.exit(1)
+else:
+    close(h)
+    sys.exit(0)
+], [])])
+
+
+# _AC_LANG_CALL(Python)([PROLOGUE], [FUNCTION])
+# ---------------------
+# Produce source that calls FUNCTION
+m4_define([_AC_LANG_CALL(Python)],
+[AC_LANG_PROGRAM([$1], [$2])])
+
+
+## -------------------------------------------- ##
+## 3. Looking for Compilers and Interpreters.   ##
+## -------------------------------------------- ##
+
+
+AC_DEFUN([AC_LANG_COMPILER(Python)],
+[AC_REQUIRE([PC_PROG_PYTHON])])
+
+
+# PC_INIT([MIN-VERSION], [MAX-VERSION]) 
+# -----------------------------
+# Initialize pyconfigure, finding a Python interpreter with a given
+# minimum and/or maximum version. 
+AC_DEFUN([PC_INIT],
+[PC_PROG_PYTHON([], [$1], [$2])
+dnl If we found something, do a sanity check that the interpreter really
+dnl has the version its name would suggest.
+m4_ifval([PYTHON],
+        [PC_PYTHON_VERIFY_VERSION([>=], [pc_min_ver], [],
+                  [AC_MSG_FAILURE([No compatible Python interpreter found. If 
you're sure that you have one, try setting the PYTHON environment variable to 
the location of the interpreter.])])])
+m4_ifval([PYTHON],
+        [PC_PYTHON_VERIFY_VERSION([<=], [pc_max_ver], [],
+                  [AC_MSG_FAILURE([No compatible Python interpreter found. If 
you're sure that you have one, try setting the PYTHON environment variable to 
the location of the interpreter.])])])
+])# PC_INIT
+
+# PC_PROG_PYTHON([PROG-TO-CHECK-FOR], [MIN-VERSION], [MAX-VERSION])
+# ---------------------------------
+# Find a Python interpreter.  Python versions prior to 2.0 are not
+# supported. (2.0 was released on October 16, 2000).
+AC_DEFUN_ONCE([PC_PROG_PYTHON],
+[AC_ARG_VAR([PYTHON], [the Python interpreter])
+dnl The default minimum version is 2.0
+m4_define_default([pc_min_ver], m4_ifval([$2], [$2], [2.0]))
+dnl The default maximum version is 3.3
+m4_define_default([pc_max_ver], m4_ifval([$3], [$3], [4.0]))
+dnl Build up a list of possible interpreter names. 
+m4_define_default([_PC_PYTHON_INTERPRETER_LIST],
+    [dnl If we want some Python 3 versions (max version >= 3.0), 
+dnl also search for "python3"
+     m4_if(m4_version_compare(pc_max_ver, [2.9]), [1], [python3], []) \
+dnl If we want some Python 2 versions (min version <= 2.7),
+dnl also search for "python2".
+     m4_if(m4_version_compare(pc_min_ver, [2.8]), [-1], [python2], []) \
+dnl Construct a comma-separated list of interpreter names (python2.6, 
+dnl python2.7, etc). We only care about the first 3 characters of the
+dnl version strings (major-dot-minor; not 
+dnl major-dot-minor-dot-bugfix[-dot-whatever])
+     m4_foreach([pc_ver], 
+                    m4_esyscmd_s(seq -s[[", "]] -f["[[%.1f]]"] 
m4_substr(pc_max_ver, [0], [3]) -0.1 m4_substr(pc_min_ver, [0], [3])),
+dnl Remove python2.8 and python2.9 since they will never exist
+                    [m4_bmatch(pc_ver, [2.[89]], [], [python]pc_ver)]) \
+     [python]])
+dnl Do the actual search at last.
+m4_ifval([$1],
+       [AC_PATH_PROGS(PYTHON, [$1 _PC_PYTHON_INTERPRETER_LIST])],
+       [AC_PATH_PROGS(PYTHON, [_PC_PYTHON_INTERPRETER_LIST])])
+])# PC_PROG_PYTHON
+  
+
+# PC_PYTHON_PROG_PYTHON_CONFIG(PROG-TO-CHECK-FOR)
+# ----------------------------------------------
+# Find the python-config program
+AC_DEFUN([PC_PYTHON_PROG_PYTHON_CONFIG],
+[AC_REQUIRE([PC_PROG_PYTHON])[]dnl
+AC_ARG_VAR([PYTHON_CONFIG], [the Python-config program])
+dnl python-config's binary name is normally based on the Python interpreter's
+dnl binary name (i.e. python2.7 -> python2.7-config)
+m4_define([_PYTHON_BASENAME], [`basename $PYTHON`])
+m4_ifval([$1],
+       [AC_PATH_PROGS(PYTHON_CONFIG, [$1 _PYTHON_BASENAME-config])],
+       [AC_PATH_PROG(PYTHON_CONFIG, _PYTHON_BASENAME-config)])
+]) # PC_PYTHON_PROG_PYTHON_CONFIG
+
+
+# PC_PYTHON_VERIFY_VERSION([RELATION], [VERSION], [ACTION-IF-TRUE], 
[ACTION-IF-FALSE])
+# ---------------------------------------------------------------------------
+# Run ACTION-IF-TRUE if the Python interpreter PROG has version [RELATION] 
VERSION.
+# i.e if RELATION is "<", check if PROG has a version number less than VERSION.
+# Run ACTION-IF-FALSE otherwise.
+# Specify RELATION as any mathematical comparison "<", ">", "<=", ">=", "==" 
or "!="
+# This test uses sys.hexversion instead of the string equivalent (first
+# word of sys.version), in order to cope with versions such as 2.2c1.
+# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
+AC_DEFUN([PC_PYTHON_VERIFY_VERSION],
+[m4_define([pc_python_safe_ver], m4_bpatsubsts($2, [\.], [_]))
+AC_CACHE_CHECK([if Python $1 '$2'],
+    [[pc_cv_python_req_version_]pc_python_safe_ver],
+    [AC_LANG_PUSH(Python)[]dnl
+     AC_RUN_IFELSE(
+        [AC_LANG_PROGRAM([dnl
+import sys
+], [dnl
+    # split strings by '.' and convert to numeric.  Append some zeros
+    # because we need at least 4 digits for the hex conversion.
+    # map returns an iterator in Python 3.0 and a list in 2.x
+    reqver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
+    reqverhex = 0
+    # xrange is not present in Python 3.0 and range returns an iterator
+    for i in list(range(4)):
+        reqverhex = (reqverhex << 8) + reqver[[i]]
+    # the final 8 bits are "0xf0" for final versions, which are all
+    # we'll test against, since it's doubtful that a released software
+    # will depend on an alpha- or beta-state Python.
+    reqverhex += 0xf0
+    if sys.hexversion $1 reqverhex:
+        sys.exit()
+    else:
+        sys.exit(1)
+])], 
+         [[pc_cv_python_req_version_]pc_python_safe_ver=yes], 
+         [[pc_cv_python_req_version_]pc_python_safe_ver=no])
+     AC_LANG_POP(Python)[]dnl
+    ])
+AS_IF([test "$[pc_cv_python_req_version_]pc_python_safe_ver" = "no"], [$4], 
[$3])
+])# PC_PYTHON_VERIFY_VERSION
+
+
+# PC_PYTHON_CHECK_VERSION
+# -----------------------
+# Query Python for its version number.  Getting [:3] seems to be
+# the best way to do this; it's what "site.py" does in the standard
+# library.
+AC_DEFUN([PC_PYTHON_CHECK_VERSION],
+[AC_REQUIRE([PC_PROG_PYTHON])[]dnl
+AC_CACHE_CHECK([for $1 version], 
+    [pc_cv_python_version],
+    [AC_LANG_PUSH(Python)[]dnl
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+], [dnl
+    sys.stdout.write(sys.version[[:3]])
+])],
+                   [pc_cv_python_version=`./conftest`],
+                   [AC_MSG_FAILURE([failed to run Python program])])
+     AC_LANG_POP(Python)[]dnl
+    ])
+AC_SUBST([PYTHON_VERSION], [$pc_cv_python_version])
+])# PC_PYTHON_CHECK_VERSION
+
+
+# PC_PYTHON_CHECK_PREFIX
+# ----------------------
+# Use the value of $prefix for the corresponding value of
+# PYTHON_PREFIX. This is made a distinct variable so it can be
+# overridden if need be.  However, general consensus is that you
+# shouldn't need this ability. 
+AC_DEFUN([PC_PYTHON_CHECK_PREFIX],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to get it with python-config otherwise do it from within Python
+AC_CACHE_CHECK([for Python prefix], [pc_cv_python_prefix],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_prefix=`$PYTHON_CONFIG --prefix 2>&AS_MESSAGE_LOG_FD`
+else
+    AC_LANG_PUSH(Python)[]dnl
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+], [dnl
+    sys.stdout.write(sys.prefix)
+])], [pc_cv_python_prefix=`./conftest`;
+      if test $? != 0; then
+         AC_MSG_FAILURE([could not determine Python prefix])
+      fi],
+      [AC_MSG_FAILURE([failed to run Python program])])
+    AC_LANG_POP(Python)[]dnl
+fi])
+AC_SUBST([PYTHON_PREFIX], [$pc_cv_python_prefix])])
+
+
+# PC_PYTHON_CHECK_EXEC_PREFIX
+# --------------------------
+# Like above, but for $exec_prefix
+AC_DEFUN([PC_PYTHON_CHECK_EXEC_PREFIX],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to get it with python-config otherwise do it from within Python
+AC_CACHE_CHECK([for Python exec-prefix], [pc_cv_python_exec_prefix],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_exec_prefix=`$PYTHON_CONFIG --exec-prefix 
2>&AS_MESSAGE_LOG_FD`
+else
+    AC_LANG_PUSH(Python)[]dnl
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+], [dnl
+    sys.stdout.write(sys.exec_prefix)
+])],
+        [pc_cv_python_exec_prefix=`./conftest`;
+         if test $? != 0; then
+            AC_MSG_FAILURE([could not determine Python exec_prefix])
+         fi],
+         [AC_MSG_FAILURE([failed to run Python program])])
+    AC_LANG_POP(Python)[]dnl
+fi
+])
+AC_SUBST([PYTHON_EXEC_PREFIX], [$pc_cv_python_exec_prefix])])
+
+
+# PC_PYTHON_CHECK_INCLUDES
+# ------------------------
+# Find the Python header file include flags (ie
+# '-I/usr/include/python')
+AC_DEFUN([PC_PYTHON_CHECK_INCLUDES],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the headers location with python-config otherwise guess
+AC_CACHE_CHECK([for Python includes], [pc_cv_python_includes],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_includes=`$PYTHON_CONFIG --includes 2>&AS_MESSAGE_LOG_FD`
+else
+    
pc_cv_python_includes="[-I$includedir/$_PYTHON_BASENAME]m4_ifdef(PYTHON_ABI_FLAGS,
+    PYTHON_ABI_FLAGS,)"
+fi
+])
+AC_SUBST([PYTHON_INCLUDES], [$pc_cv_python_includes])])
+
+
+# PC_PYTHON_CHECK_HEADERS([ACTION-IF-PRESENT], [ACTION-IF-ABSENT])
+# -----------------------
+# Check for the presence and usability of Python.h
+AC_DEFUN([PC_PYTHON_CHECK_HEADERS],
+[AC_REQUIRE([PC_PYTHON_CHECK_INCLUDES])[]dnl
+pc_cflags_store=$CPPFLAGS
+CPPFLAGS="$CFLAGS $PYTHON_INCLUDES"
+AC_CHECK_HEADER([Python.h], [$1], [$2])
+CPPFLAGS=$pc_cflags_store
+])
+
+
+# PC_PYTHON_CHECK_LIBS
+# --------------------
+# Find the Python lib flags (ie '-lpython')
+AC_DEFUN([PC_PYTHON_CHECK_LIBS],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the lib flags with python-config otherwise guess
+AC_CACHE_CHECK([for Python libs], [pc_cv_python_libs],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_libs=`$PYTHON_CONFIG --libs 2>&AS_MESSAGE_LOG_FD`
+else
+    pc_cv_python_libs="[-l$_PYTHON_BASENAME]m4_ifdef(PYTHON_ABI_FLAGS, 
PYTHON_ABI_FLAGS,)"
+fi
+])
+AC_SUBST([PYTHON_LIBS], [$pc_cv_python_libs])])
+
+
+# PC_PYTHON_TEST_LIBS(LIBRARY-FUNCTION, [ACTION-IF-PRESENT], 
[ACTION-IF-ABSENT])
+# -------------------
+# Verify that the Python libs can be loaded
+AC_DEFUN([PC_PYTHON_TEST_LIBS],
+[AC_REQUIRE([PC_PYTHON_CHECK_LIBS])[]dnl
+pc_libflags_store=$LIBS
+for lflag in $PYTHON_LIBS; do
+    case $lflag in
+        -lpython*@:}@
+               LIBS="$LIBS $lflag"
+               pc_libpython=`echo $lflag | sed -e 's/^-l//'`
+               ;;
+         *@:}@;;
+    esac
+done
+AC_CHECK_LIB([$pc_libpython], [$1], [$2], [$3])])
+
+
+# PC_PYTHON_CHECK_CFLAGS
+# ----------------------
+# Find the Python CFLAGS
+AC_DEFUN([PC_PYTHON_CHECK_CFLAGS],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the CFLAGS with python-config otherwise give up
+AC_CACHE_CHECK([for Python CFLAGS], [pc_cv_python_cflags],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_cflags=`$PYTHON_CONFIG --cflags 2>&AS_MESSAGE_LOG_FD`
+else
+    pc_cv_python_cflags=
+fi
+])
+AC_SUBST([PYTHON_CFLAGS], [$pc_cv_python_cflags])])
+
+
+# PC_PYTHON_CHECK_LDFLAGS
+# -----------------------
+# Find the Python LDFLAGS
+AC_DEFUN([PC_PYTHON_CHECK_LDFLAGS],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the LDFLAGS with python-config otherwise give up
+AC_CACHE_CHECK([for Python LDFLAGS], [pc_cv_python_ldflags],
+[if test -x "$PYTHON_CONFIG"; then
+    pc_cv_python_ldflags=`$PYTHON_CONFIG --ldflags 2>&AS_MESSAGE_LOG_FD`
+else
+    pc_cv_python_ldflags=
+fi
+])
+AC_SUBST([PYTHON_LDFLAGS], [$pc_cv_python_ldflags])])
+
+
+# PC_PYTHON_CHECK_EXTENSION_SUFFIX
+# --------------------------------
+# Find the Python extension suffix (i.e. '.cpython-32.so')
+AC_DEFUN([PC_PYTHON_CHECK_EXTENSION_SUFFIX],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the suffix with python-config otherwise give up
+AC_CACHE_CHECK([for Python extension suffix], [pc_cv_python_extension_suffix],
+[if test -x "$PYTHON_CONFIG"; then
+     pc_cv_python_extension_suffix=`$PYTHON_CONFIG --extension-suffix 
2>&AS_MESSAGE_LOG_FD`
+else
+    pc_cv_python_extension_suffix=
+fi
+])
+AC_SUBST([PYTHON_EXTENSION_SUFFIX], [$pc_cv_python_extension_suffix])])
+
+
+# PC_PYTHON_CHECK_ABI_FLAGS
+# -------------------------
+# Find the Python ABI flags
+AC_DEFUN([PC_PYTHON_CHECK_ABI_FLAGS],
+[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl
+dnl Try to find the ABI flags with python-config otherwise give up
+AC_CACHE_CHECK([for Python ABI flags], [pc_cv_python_abi_flags],
+[if test -x "$PYTHON_CONFIG"; then
+     pc_cv_python_abi_flags=`$PYTHON_CONFIG --abiflags 2>&AS_MESSAGE_LOG_FD`
+else
+    pc_cv_python_abi_flags=
+fi
+])
+AC_SUBST([PYTHON_ABI_FLAGS], [$pc_cv_python_abi_flags])])
+
+
+# PC_PYTHON_CHECK_PLATFORM
+# ------------------------
+# At times (like when building shared libraries) you may want
+# to know which OS platform Python thinks this is.
+AC_DEFUN([PC_PYTHON_CHECK_PLATFORM],
+[AC_REQUIRE([PC_PROG_PYTHON])[]dnl
+dnl Get the platform from within Python (sys.platform)
+AC_CACHE_CHECK([for Python platform], [pc_cv_python_platform],
+    [AC_LANG_PUSH(Python)[]dnl
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+], [dnl
+    sys.stdout.write(sys.platform)
+])], [pc_cv_python_platform=`./conftest`;
+     if test $? != 0; then
+        AC_MSG_FAILURE([could not determine Python platform])
+     fi],
+     [AC_MSG_FAILURE([failed to run Python program])])
+    AC_LANG_POP(Python)[]dnl
+   ])
+AC_SUBST([PYTHON_PLATFORM], [$pc_cv_python_platform])
+])
+
+
+# PC_PYTHON_CHECK_SITE_DIR
+# ---------------------
+# The directory to which new libraries are installed (i.e. the
+# "site-packages" directory.
+AC_DEFUN([PC_PYTHON_CHECK_SITE_DIR],
+[AC_REQUIRE([PC_PROG_PYTHON])AC_REQUIRE([PC_PYTHON_CHECK_PREFIX])[]dnl
+AC_CACHE_CHECK([for Python site-packages directory],
+    [pc_cv_python_site_dir],
+    [AC_LANG_PUSH(Python)[]dnl
+    if test "x$prefix" = xNONE
+     then
+       pc_py_prefix=$ac_default_prefix
+     else
+       pc_py_prefix=$prefix
+     fi
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+from platform import python_implementation
+# sysconfig in CPython 2.7 doesn't work in virtualenv
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+    import sysconfig
+except:
+    can_use_sysconfig = False
+else:
+    can_use_sysconfig = True
+if can_use_sysconfig:
+    if python_implementation() == "CPython" and sys.version[[:3]] == '2.7':
+        can_use_sysconfig = False
+if not can_use_sysconfig:        
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(False, False, prefix='$pc_py_prefix')
+else:
+    sitedir = sysconfig.get_path('purelib', vars={'base':'$pc_py_prefix'})
+], [dnl
+    sys.stdout.write(sitedir)
+])], [pc_cv_python_site_dir=`./conftest`],
+     [AC_MSG_FAILURE([failed to run Python program])])
+     AC_LANG_POP(Python)[]dnl
+     case $pc_cv_python_site_dir in
+     $pc_py_prefix*)
+       pc__strip_prefix=`echo "$pc_py_prefix" | sed 's|.|.|g'`
+       pc_cv_python_site_dir=`echo "$pc_cv_python_site_dir" | sed 
"s,^$pc__strip_prefix/,,"`
+       ;;
+     *)
+       case $pc_py_prefix in
+         /usr|/System*) ;;
+         *)
+         pc_cv_python_site_dir=lib/python$PYTHON_VERSION/site-packages
+         ;;
+       esac
+       ;;
+     esac
+     ])
+AC_SUBST([pythondir], [\${prefix}/$pc_cv_python_site_dir])])# 
PC_PYTHON_CHECK_SITE_DIR
+
+# PC_PYTHON_SITE_PACKAGE_DIR
+# --------------------------
+# $PACKAGE directory under PYTHON_SITE_DIR
+AC_DEFUN([PC_PYTHON_SITE_PACKAGE_DIR],
+[AC_REQUIRE([PC_PYTHON_CHECK_SITE_DIR])[]dnl
+AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE_NAME])])
+
+
+# PC_PYTHON_CHECK_EXEC_DIR
+# ------------------------
+# directory for installing python extension modules (shared libraries)
+AC_DEFUN([PC_PYTHON_CHECK_EXEC_DIR],
+[AC_REQUIRE([PC_PROG_PYTHON])AC_REQUIRE([PC_PYTHON_CHECK_EXEC_PREFIX])[]dnl
+  AC_CACHE_CHECK([for Python extension module directory],
+    [pc_cv_python_exec_dir],
+    [AC_LANG_PUSH(Python)[]dnl
+    if test "x$pc_cv_python_exec_prefix" = xNONE
+     then
+       pc_py_exec_prefix=$pc_cv_python_prefix
+     else
+       pc_py_exec_prefix=$pc_cv_python_exec_prefix
+     fi
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+import sys
+from platform import python_implementation
+# sysconfig in CPython 2.7 doesn't work in virtualenv
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+    import sysconfig
+except:
+    can_use_sysconfig = False
+else:
+    can_use_sysconfig = True
+if can_use_sysconfig:
+    if python_implementation() == "CPython" and sys.version[[:3]] == '2.7':
+        can_use_sysconfig = False
+if not can_use_sysconfig:        
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(False, False, 
prefix='$pc_py__exec_prefix')
+else:
+    sitedir = sysconfig.get_path('purelib', 
vars={'platbase':'$pc_py_exec_prefix'})
+], [dnl
+    sys.stdout.write(sitedir)
+])], [pc_cv_python_exec_dir=`./conftest`],
+     [AC_MSG_FAILURE([failed to run Python program])])
+     AC_LANG_POP(Python)[]dnl
+     case $pc_cv_python_exec_dir in
+     $pc_py_exec_prefix*)
+       pc__strip_prefix=`echo "$pc_py_exec_prefix" | sed 's|.|.|g'`
+       pc_cv_python_exec_dir=`echo "$pc_cv_python_exec_dir" | sed 
"s,^$pc__strip_prefix/,,"`
+       ;;
+     *)
+       case $pc_py_exec_prefix in
+         /usr|/System*) ;;
+         *)
+          pc_cv_python_exec_dir=lib/python$PYTHON_VERSION/site-packages
+          ;;
+       esac
+       ;;
+     esac
+    ])
+AC_SUBST([pyexecdir], [\${exec_prefix}/$pc_cv_python_pyexecdir])]) 
#PY_PYTHON_CHECK_EXEC_LIB_DIR
+
+
+# PC_PYTHON_EXEC_PACKAGE_DIR
+# --------------------------
+# $PACKAGE directory under PYTHON_SITE_DIR
+AC_DEFUN([PC_PYTHON_EXEC_PACKAGE_DIR],
+[AC_REQUIRE([PC_PYTHON_CHECK_EXEC_DIR])[]dnl
+AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE_NAME])])
+
+
+## -------------------------------------------- ##
+## 4. Looking for specific libs & functionality ##
+## -------------------------------------------- ##
+
+
+# PC_PYTHON_CHECK_MODULE(LIBRARY, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# ----------------------------------------------------------------------
+# Macro for checking if a Python library is installed
+AC_DEFUN([PC_PYTHON_CHECK_MODULE],
+[AC_REQUIRE([PC_PROG_PYTHON])[]dnl
+m4_define([pc_python_safe_mod], m4_bpatsubsts($1, [\.], [_]))
+AC_CACHE_CHECK([for Python '$1' library],
+    [[pc_cv_python_module_]pc_python_safe_mod],
+    [AC_LANG_PUSH(Python)[]dnl
+     AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM([dnl
+import sys
+try:
+    import $1
+except:
+    sys.exit(1)
+else:
+    sys.exit(0)
+], [])],
+       [[pc_cv_python_module_]pc_python_safe_mod="yes"],
+       [[pc_cv_python_module_]pc_python_safe_mod="no"])
+     AC_LANG_POP(Python)[]dnl
+    ])
+AS_IF([test "$[pc_cv_python_module_]pc_python_safe_mod" = "no"], [$3], [$2])
+])# PC_PYTHON_CHECK_MODULE
+
+
+# PC_PYTHON_CHECK_FUNC([LIBRARY], FUNCTION, ARGS, [ACTION-IF-FOUND], 
[ACTION-IF-NOT-FOUND])
+# 
---------------------------------------------------------------------------------------
+# Check to see if a given function call, optionally from a module, can
+# be successfully called
+AC_DEFUN([PC_PYTHON_CHECK_FUNC],
+[AC_REQUIRE([PC_PROG_PYTHON])[]dnl
+m4_define([pc_python_safe_mod], m4_bpatsubsts($1, [\.], [_]))
+AC_CACHE_CHECK([for Python m4_ifnblank($1, '$1.$2()', '$2()') function],
+    [[pc_cv_python_func_]pc_python_safe_mod[_$2]],
+    [AC_LANG_PUSH(Python)[]dnl
+     AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM([dnl
+import sys
+m4_ifnblank([$1], [dnl
+try:
+    import $1
+except:
+    sys.exit(1)
+], [])], 
+[
+m4_ifnblank([$1], [
+    try:
+        $1.$2($3)], [
+    try:
+        $2($3)])
+    except:
+        sys.exit(1)
+    else:
+        sys.exit(0)
+])],
+       [[pc_cv_python_func_]pc_python_safe_mod[_$2]="yes"],
+       [[pc_cv_python_func_]pc_python_safe_mod[_$2]="no"])
+     AC_LANG_POP(Python)[]dnl
+    ])
+AS_IF([test "$[pc_cv_python_func_]pc_python_safe_mod[_$2]" = "no"], [$5], [$4])
+])# PC_PYTHON_CHECK_FUNC
diff --git a/playground.conf b/playground.conf
new file mode 100644
index 0000000..27e0900
--- /dev/null
+++ b/playground.conf
@@ -0,0 +1,12 @@
+[taler]
+currency = TESTKUDOS
+
+[frontends]
+BACKEND = http://backend.test.taler.net/
+
+[playground]
+UWSGI_UNIXPATH_MODE = 660
+instance = default
+UWSGI_SERVE = unix
+UWSGI_UNIXPATH = $HOME/sockets/playground.uwsgi
+UWSGI_UNIXMODE = 666
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..cc4e36d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(name='talerplayground',
+      version='0.0',
+      description='Various testing sites',
+      url='git://taler.net/playground',
+      author='Florian Dold',
+      author_email='address@hidden',
+      license='GPL',
+      packages=find_packages(),
+      install_requires=["Flask>=0.10"],
+      package_data={
+          '':[
+              "playground/templates/*.html",
+              "playground/static/*.svg",
+              "playground/static/*.css",
+              "playground/static/*.js",
+              "playground/static/*.js.tar.gz",
+      ]
+      },
+      scripts=['taler-merchant-playground'],
+      zip_safe=False)
diff --git a/taler-playground.in b/taler-playground.in
new file mode 100644
index 0000000..d9174c2
--- /dev/null
+++ b/taler-playground.in
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+
+"""
+Stand-alone script to manage the GNU Taler playground frontend.
+"""
+
+import argparse
+import sys
+import os
+import site
+import logging
+from talerdonations.talerconfig import TalerConfig
+
+
+os.environ.setdefault("TALER_PREFIX", "@prefix@")
+site.addsitedir("%s/lib/python%d.%d/site-packages" % (
+    "@prefix@",
+    sys.version_info.major,
+    sys.version_info.minor))
+
+TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
+
+# No perfect match to our logging format, but good enough ...
+UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)"
+
+def handle_serve_http(args):
+    port = args.port
+    if port is None:
+        port = TC["playground"]["http_port"].value_int(required=True)
+    spec = ":%d" % (port,)
+    os.execlp("uwsgi", "uwsgi",
+              "--master",
+              "--die-on-term",
+              "--log-format", UWSGI_LOGFMT,
+              "--http", spec,
+              "--wsgi-file", "@prefix@/share/taler/frontend-playground.wsgi")
+
+def handle_serve_uwsgi(args):
+    del args # pacify PEP checkers
+    serve_uwsgi = 
TC["playground"]["uwsgi_serve"].value_string(required=True).lower()
+    params = ["uwsgi", "uwsgi",
+              "--master",
+              "--die-on-term",
+              "--log-format", UWSGI_LOGFMT,
+              "--wsgi-file", "@prefix@/share/taler/frontend-playground.wsgi"]
+    if serve_uwsgi == "tcp":
+        port = TC["playground"]["uwsgi_port"].value_int(required=True)
+        spec = ":%d" % (port,)
+        params.extend(["--socket", spec])
+    elif serve_uwsgi == "unix":
+        spec = TC["playground"]["uwsgi_unixpath"].value_filename(required=True)
+        mode = 
TC["playground"]["uwsgi_unixpath_mode"].value_filename(required=True)
+        params.extend(["--socket", spec])
+        params.extend(["--chmod-socket="+mode])
+        os.makedirs(os.path.dirname(spec), exist_ok=True)
+    logging.info("launching uwsgi with argv %s", params[1:])
+    os.execlp(*params)
+
+
+
+PARSER = argparse.ArgumentParser()
+PARSER.set_defaults(func=None)
+PARSER.add_argument('--config', '-c',
+                    help="configuration file to use",
+                    metavar="CONFIG", type=str,
+                    dest="config", default=None)
+SUB = PARSER.add_subparsers()
+
+P = SUB.add_parser('serve-http', help="Serve over HTTP")
+P.add_argument("--port", "-p", dest="port",
+               type=int, default=None, metavar="PORT")
+P.set_defaults(func=handle_serve_http)
+
+P = SUB.add_parser('serve-uwsgi', help="Serve over UWSGI")
+P.set_defaults(func=handle_serve_uwsgi)
+
+ARGS = PARSER.parse_args()
+if getattr(ARGS, 'func', None) is None:
+    PARSER.print_help()
+    sys.exit(1)
+
+if ARGS.config is not None:
+    os.environ["TALER_CONFIG_FILE"] = ARGS.config
+
+ARGS.func(ARGS)
diff --git a/taler-playground.wsgi.in b/taler-playground.wsgi.in
new file mode 100644
index 0000000..565ffd4
--- /dev/null
+++ b/taler-playground.wsgi.in
@@ -0,0 +1,21 @@
+import sys
+
+if sys.version_info.major < 3:
+    print("The taler playground needs to run with Python>=3.4")
+    sys.exit(1)
+
+import site
+import os
+import logging
+
+logging.basicConfig(level=logging.INFO)
+
+os.environ.setdefault("TALER_PREFIX", "@prefix@")
+site.addsitedir("%s/lib/python%d.%d/site-packages" % (
+    "@prefix@", 
+    sys.version_info.major,
+    sys.version_info.minor))
+
+import talerplayground.playground
+
+application = talerplayground.playground.app
diff --git a/talerplayground/__init__.py b/talerplayground/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/talerplayground/playground/__init__.py 
b/talerplayground/playground/__init__.py
new file mode 100644
index 0000000..7788acc
--- /dev/null
+++ b/talerplayground/playground/__init__.py
@@ -0,0 +1,3 @@
+from talerplayground.playground.playground import app
+
+__all__ = ["app"]
diff --git a/talerplayground/playground/playground.py 
b/talerplayground/playground/playground.py
new file mode 100644
index 0000000..759bd7a
--- /dev/null
+++ b/talerplayground/playground/playground.py
@@ -0,0 +1,106 @@
+# This file is part of GNU TALER.
+# Copyright (C) 2014-2016 INRIA
+#
+# TALER is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free 
Software
+# Foundation; either version 2.1, or (at your option) any later version.
+#
+# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
+#
+# You should have received a copy of the GNU Lesser General Public License 
along with
+# GNU TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+#
+# @author Florian Dold
+# @author Marcello Stanisci
+
+from urllib.parse import urljoin, parse_qsl
+import logging
+import os
+import uuid
+import base64
+import random
+import requests
+import flask
+import traceback
+from ..talerconfig import TalerConfig
+
+LOGGER = logging.getLogger(__name__)
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+app = flask.Flask(__name__, template_folder=BASE_DIR)
+app.debug = True
+app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8')
+
+TC = TalerConfig.from_env()
+BACKEND_URL = TC["frontends"]["backend"].value_string(required=True)
+CURRENCY = TC["taler"]["currency"].value_string(required=True)
+APIKEY = TC["frontends"]["backend_apikey"].value_string(required=True)
+
+app.config.from_object(__name__)
+
address@hidden
+def utility_processor():
+    def env(name, default=None):
+        return os.environ.get(name, default)
+    return dict(env=env)
+
+
+def err_abort(abort_status_code, **params):
+    t = flask.render_template("templates/error.html", **params)
+    flask.abort(flask.make_response(t, abort_status_code))
+
+
+def backend_get(endpoint, params):
+    headers = {"Authorization": "ApiKey " + APIKEY}
+    try:
+        resp = requests.get(urljoin(BACKEND_URL, endpoint), params=params, 
headers=headers)
+    except requests.ConnectionError:
+        err_abort(500, message="Could not establish connection to backend")
+    try:
+        response_json = resp.json()
+    except ValueError:
+        err_abort(500, message="Could not parse response from backend")
+    if resp.status_code != 200:
+        err_abort(500, message="Backend returned error status",
+                  json=response_json, status_code=resp.status_code)
+    return response_json
+
+
+def backend_post(endpoint, json):
+    headers = {"Authorization": "ApiKey " + APIKEY}
+    try:
+        resp = requests.post(urljoin(BACKEND_URL, endpoint), json=json, 
headers=headers)
+    except requests.ConnectionError:
+        err_abort(500, message="Could not establish connection to backend")
+    try:
+        response_json = resp.json()
+    except ValueError:
+        err_abort(500, message="Could not parse response from backend",
+                  status_code=resp.status_code)
+    if resp.status_code != 200:
+        err_abort(500, message="Backend returned error status",
+                  json=response_json, status_code=resp.status_code)
+    return response_json
+
+
+def expect_parameter(name):
+    val = flask.request.args.get(name)
+    if not val:
+        return err_abort(400, "parameter '{}' required".format(name))
+    return val
+
+
address@hidden(Exception)
+def internal_error(e):
+    return flask.render_template("templates/error.html",
+                                 message="Internal error",
+                                 stack=traceback.format_exc())
+
+
address@hidden("/")
+def index():
+    return flask.render_template("templates/index.html", 
merchant_currency=CURRENCY)
+
diff --git a/talerplayground/playground/templates/base.html 
b/talerplayground/playground/templates/base.html
new file mode 100644
index 0000000..60f17da
--- /dev/null
+++ b/talerplayground/playground/templates/base.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- 
+  This file is part of GNU TALER.
+  Copyright (C) 2014, 2015, 2016 INRIA
+
+  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>Taler Donation Demo</title>
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='web-common/pure.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='web-common/demo.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='web-common/taler-fallback.css') }}" id="taler-presence-stylesheet" />
+  {% block styles %}{% endblock %}
+  {% block scripts %}{% endblock %}
+</head>
+
+<body>
+  <div class="demobar">
+    <h1><span class="tt adorn-brackets">Taler Demo</span></h1>
+    <h1><span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS') 
}}">Donations</a></span></h1>
+    <p>This is the donation page, you can make donations (with an imaginary 
currency for now) to free software projects.</p>
+    <ul>
+      <li><a href="{{ env('TALER_ENV_URL_INTRO', '#') }}">Introduction</a></li>
+      <li><a href="{{ env('TALER_ENV_URL_BANK', '#') }}">Bank</a></li>
+      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG', '#') }}">Essay 
Shop</a></li>
+      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS', '#') 
}}">Donations</a></li>
+      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') 
}}">Tipping/Survey</a></li>
+    </ul>
+    <p>You can learn more about Taler on our main <a 
href="https://taler.net";>website</a>.</p>
+  </div>
+
+  <section id="main" class="content">
+    {% block main %}
+      This is the main content of the page.
+    {% endblock %}
+    <hr />
+    <div class="copyright">
+      <p>Copyright &copy; 2014&mdash;2017 INRIA</p>
+      <a href="/javascript" data-jslicense="1" 
class="jslicenseinfo">JavaScript license information</a>
+    </div>
+  </section>
+</body>
+</html>
diff --git a/talerplayground/playground/templates/index.html 
b/talerplayground/playground/templates/index.html
new file mode 100644
index 0000000..b384362
--- /dev/null
+++ b/talerplayground/playground/templates/index.html
@@ -0,0 +1,57 @@
+{% extends "templates/base.html" %}
+
+{% block main %}
+<h1 lang="en">Welcome to the Taler Donation "Shop" Demo</h1>
+
+<p>This toy donations website shows the user experience for donations with 
Taler.
+You are paying with an imaginary currency ({{ merchant_currency }}).
+</p>
+
+<div class="taler-installed-hide">
+  <h2>Installing the Taler wallet</h2>
+  First, you need to install the Taler wallet browser extension.
+  Install the wallet
+  <span id="install-done" style="visibility: hidden">(done)</span>
+  <ul>
+    <li>from the app store for <a 
href="https://chrome.google.com/webstore/detail/gnu-taler-wallet/millncjiddlpgdmkklmhfadpacifaonc";>Google
+        Chrome and Chromium</a>
+    </li>
+    <li>By visiting our <a href="https://taler.net/wallet";>installation 
page</a> for other platforms.
+    </li>
+  </ul>
+  Wallets for other browsers will be provided in the near future.
+</div>
+
+<div class="taler-installed-show">
+  <p>Please select a project, the amount (*) of {{ merchant_currency }} you
+    wish to donate, and enter the name that will appear on your receipt:</p>
+
+  <form name="tform" action="checkout" method="GET" class="pure-form">
+    <div class="participation" id="fake-shop">
+      <select name="donation_receiver">
+        <option value="GNUnet">GNUnet</option>
+        <option value="Taler">GNU Taler</option>
+        <option value="Tor">Tor</option>
+      </select>
+      <select id="taler-donation" name="donation_amount">
+        <option value="{{ merchant_currency }}:0.1">0.1 {{ merchant_currency 
}}</option>
+        <option value="{{ merchant_currency }}:1">1 {{ merchant_currency 
}}</option>
+        <option value="{{ merchant_currency }}:6">5 {{ merchant_currency 
}}</option>
+        <option value="{{ merchant_currency }}:10">10 {{ merchant_currency 
}}</option>
+      </select>
+      <input type="text" name="donation_donor" value="Anonymous Donor" />
+      <input type="submit" class="pure-button pure-button-primary" 
value="Donate!" />
+    </div>
+  </form>
+  <p>
+    (*) To make it a bit more fun, the 5 {{ merchant_currency }} option is
+    deliberately implemented with a fault: the merchant will try to make you
+    donate 6 {{ merchant_currency }} instead of the 5 {{ merchant_currency }} 
you
+    got to see.  But do not worry, you will be given the opportunity to review
+    the final offer from the merchant in a window secured by the Taler
+    extension.  That way, you can spot the error before committing to an
+    incorrect contract.
+  </p>
+</div>
+
+{% endblock %}
diff --git a/talerplayground/playground/templates/javascript.html 
b/talerplayground/playground/templates/javascript.html
new file mode 100644
index 0000000..fa94218
--- /dev/null
+++ b/talerplayground/playground/templates/javascript.html
@@ -0,0 +1,22 @@
+<!-- This file is in the public domain -->
+<html>
+<body>
+<table id="jslicense-labels1">
+<tr>
+  <td><a 
href="/static/web-common/taler-wallet-lib.js">taler-wallet-lib.js</a></td>
+  <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html";>LGPL</a></td>
+  <td><a 
href="/static/web-common/taler-wallet-lib.js.tar.gz">taler-wallet-lib.js.tar.gz</a></td>
+</tr>
+<tr>
+  <td><a 
href="/static/web-common/dropdown-navbar_script.js">dropdown-navbar_script.js</a></td>
+  <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html";>LGPL</a></td>
+  <td><a 
href="/static/web-common/dropdown-navbar_script.js">dropdown-navbar_script.js</a></td>
+</tr>
+<tr>
+  <td><a href="/static/web-common/index.js">dropdown-navbar_script.js</a></td>
+  <td><a href="https://www.gnu.org/licenses/lgpl-2.1.html";>LGPL</a></td>
+  <td><a href="/static/web-common/index.js">dropdown-navbar_script.js</a></td>
+</tr>
+</table>
+</body>
+</html>
diff --git a/talerplayground/playground/web-common 
b/talerplayground/playground/web-common
new file mode 160000
index 0000000..0a65d59
--- /dev/null
+++ b/talerplayground/playground/web-common
@@ -0,0 +1 @@
+Subproject commit 0a65d5985b0474b4597dc2936f5791559c2a8a5a
diff --git a/talerplayground/talerconfig.py b/talerplayground/talerconfig.py
new file mode 100644
index 0000000..a7ca065
--- /dev/null
+++ b/talerplayground/talerconfig.py
@@ -0,0 +1,342 @@
+#  This file is part of TALER
+#  (C) 2016 INRIA
+#
+#  TALER is free software; you can redistribute it and/or modify it under the
+#  terms of the GNU Affero General Public License as published by the Free 
Software
+#  Foundation; either version 3, 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 General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License along with
+#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+#
+#  @author Florian Dold
+
+"""
+Parse GNUnet-style configurations in pure Python
+"""
+
+import logging
+import collections
+import os
+import weakref
+import sys
+import re
+
+LOGGER = logging.getLogger(__name__)
+
+__all__ = ["TalerConfig"]
+
+TALER_DATADIR = None
+try:
+    # not clear if this is a good idea ...
+    from talerpaths import TALER_DATADIR as t
+    TALER_DATADIR = t
+except ImportError:
+    pass
+
+class ConfigurationError(Exception):
+    pass
+
+class ExpansionSyntaxError(Exception):
+    pass
+
+
+def expand(var, getter):
+    """
+    Do shell-style parameter expansion.
+    Supported syntax:
+    - ${X}
+    - ${X:-Y}
+    - $X
+    """
+    pos = 0
+    result = ""
+    while pos != -1:
+        start = var.find("$", pos)
+        if start == -1:
+            break
+        if var[start:].startswith("${"):
+            balance = 1
+            end = start + 2
+            while balance > 0 and end < len(var):
+                balance += {"{": 1, "}": -1}.get(var[end], 0)
+                end += 1
+            if balance != 0:
+                raise ExpansionSyntaxError("unbalanced parentheses")
+            piece = var[start+2:end-1]
+            if piece.find(":-") > 0:
+                varname, alt = piece.split(":-", 1)
+                replace = getter(varname)
+                if replace is None:
+                    replace = expand(alt, getter)
+            else:
+                varname = piece
+                replace = getter(varname)
+                if replace is None:
+                    replace = var[start:end]
+        else:
+            end = start + 2
+            while end < len(var) and var[start+1:end+1].isalnum():
+                end += 1
+            varname = var[start+1:end]
+            replace = getter(varname)
+            if replace is None:
+                replace = var[start:end]
+        result = result + replace
+        pos = end
+
+
+    return result + var[pos:]
+
+
+class OptionDict(collections.defaultdict):
+    def __init__(self, config, section_name):
+        self.config = weakref.ref(config)
+        self.section_name = section_name
+        super().__init__()
+    def __missing__(self, key):
+        entry = Entry(self.config(), self.section_name, key)
+        self[key] = entry
+        return entry
+    def __getitem__(self, chunk):
+        return super().__getitem__(chunk.lower())
+    def __setitem__(self, chunk, value):
+        super().__setitem__(chunk.lower(), value)
+
+
+class SectionDict(collections.defaultdict):
+    def __missing__(self, key):
+        value = OptionDict(self, key)
+        self[key] = value
+        return value
+    def __getitem__(self, chunk):
+        return super().__getitem__(chunk.lower())
+    def __setitem__(self, chunk, value):
+        super().__setitem__(chunk.lower(), value)
+
+
+class Entry:
+    def __init__(self, config, section, option, **kwargs):
+        self.value = kwargs.get("value")
+        self.filename = kwargs.get("filename")
+        self.lineno = kwargs.get("lineno")
+        self.section = section
+        self.option = option
+        self.config = weakref.ref(config)
+
+    def __repr__(self):
+        return "<Entry section=%s, option=%s, value=%s>" \
+               % (self.section, self.option, repr(self.value),)
+
+    def __str__(self):
+        return self.value
+
+    def value_string(self, default=None, required=False, warn=False):
+        if required and self.value is None:
+            raise ConfigurationError("Missing required option '%s' in section 
'%s'" \
+                                     % (self.option.upper(), 
self.section.upper()))
+        if self.value is None:
+            if warn:
+                if default is not None:
+                    LOGGER.warning("Configuration is missing option '%s' in 
section '%s',\
+                                   falling back to '%s'", self.option, 
self.section, default)
+                else:
+                    LOGGER.warning("Configuration ** is missing option '%s' in 
section '%s'",
+                                   self.option.upper(), self.section.upper())
+            return default
+        return self.value
+
+    def value_int(self, default=None, required=False, warn=False):
+        value = self.value_string(default, warn, required)
+        if value is None:
+            return None
+        try:
+            return int(value)
+        except ValueError:
+            raise ConfigurationError("Expected number for option '%s' in 
section '%s'" \
+                                     % (self.option.upper(), 
self.section.upper()))
+
+    def _getsubst(self, key):
+        value = self.config()["paths"][key].value
+        if value is not None:
+            return value
+        value = os.environ.get(key)
+        if value is not None:
+            return value
+        return None
+
+    def value_filename(self, default=None, required=False, warn=False):
+        value = self.value_string(default, required, warn)
+        if value is None:
+            return None
+        return expand(value, self._getsubst)
+
+    def location(self):
+        if self.filename is None or self.lineno is None:
+            return "<unknown>"
+        return "%s:%s" % (self.filename, self.lineno)
+
+
+class TalerConfig:
+    """
+    One loaded taler configuration, including base configuration
+    files and included files.
+    """
+    def __init__(self):
+        """
+        Initialize an empty configuration
+        """
+        self.sections = SectionDict()
+
+    # defaults != config file: the first is the 'base'
+    # whereas the second overrides things from the first.
+    @staticmethod
+    def from_file(filename=None, load_defaults=True):
+        cfg = TalerConfig()
+        if filename is None:
+            xdg = os.environ.get("XDG_CONFIG_HOME")
+            if xdg:
+                filename = os.path.join(xdg, "taler.conf")
+            else:
+                filename = os.path.expanduser("~/.config/taler.conf")
+        if load_defaults:
+            cfg.load_defaults()
+        cfg.load_file(filename)
+        return cfg
+
+    def value_string(self, section, option, **kwargs):
+        return self.sections[section][option].value_string(
+            kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
+
+    def value_filename(self, section, option, **kwargs):
+        return self.sections[section][option].value_filename(
+            kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
+
+    def value_int(self, section, option, **kwargs):
+        return self.sections[section][option].value_int(
+            kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
+
+    def load_defaults(self):
+        base_dir = os.environ.get("TALER_BASE_CONFIG")
+        if base_dir:
+            self.load_dir(base_dir)
+            return
+        prefix = os.environ.get("TALER_PREFIX")
+        if prefix:
+            tmp = os.path.split(os.path.normpath(prefix))
+            if re.match("lib", tmp[1]):
+                prefix = tmp[0]
+            self.load_dir(os.path.join(prefix, "share/taler/config.d"))
+            return
+        if TALER_DATADIR:
+            self.load_dir(os.path.join(TALER_DATADIR, "share/taler/config.d"))
+            return
+        LOGGER.warning("no base directory found")
+
+    @staticmethod
+    def from_env(*args, **kwargs):
+        """
+        Load configuration from environment variable TALER_CONFIG_FILE
+        or from default location if the variable is not set.
+        """
+        filename = os.environ.get("TALER_CONFIG_FILE")
+        return TalerConfig.from_file(filename, *args, **kwargs)
+
+    def load_dir(self, dirname):
+        try:
+            files = os.listdir(dirname)
+        except FileNotFoundError:
+            LOGGER.warning("can't read config directory '%s'", dirname)
+            return
+        for file in files:
+            if not file.endswith(".conf"):
+                continue
+            self.load_file(os.path.join(dirname, file))
+
+    def load_file(self, filename):
+        sections = self.sections
+        try:
+            with open(filename, "r") as file:
+                lineno = 0
+                current_section = None
+                for line in file:
+                    lineno += 1
+                    line = line.strip()
+                    if line == "":
+                        # empty line
+                        continue
+                    if line.startswith("#"):
+                        # comment
+                        continue
+                    if line.startswith("["):
+                        if not line.endswith("]"):
+                            LOGGER.error("invalid section header in line %s: 
%s",
+                                         lineno, repr(line))
+                        section_name = line.strip("[]").strip().strip('"')
+                        current_section = section_name
+                        continue
+                    if current_section is None:
+                        LOGGER.error("option outside of section in line %s: 
%s", lineno, repr(line))
+                        continue
+                    pair = line.split("=", 1)
+                    if len(pair) != 2:
+                        LOGGER.error("invalid option in line %s: %s", lineno, 
repr(line))
+                    key = pair[0].strip()
+                    value = pair[1].strip()
+                    if value.startswith('"'):
+                        value = value[1:]
+                        if not value.endswith('"'):
+                            LOGGER.error("mismatched quotes in line %s: %s", 
lineno, repr(line))
+                        else:
+                            value = value[:-1]
+                    entry = Entry(self.sections, current_section, key,
+                                  value=value, filename=filename, 
lineno=lineno)
+                    sections[current_section][key] = entry
+        except FileNotFoundError:
+            LOGGER.error("Configuration file (%s) not found", filename)
+            sys.exit(3)
+
+
+    def dump(self):
+        for kv_section in self.sections.items():
+            print("[%s]" % (kv_section[1].section_name,))
+            for kv_option in kv_section[1].items():
+                print("%s = %s # %s" % \
+                      (kv_option[1].option,
+                       kv_option[1].value,
+                       kv_option[1].location()))
+
+    def __getitem__(self, chunk):
+        if isinstance(chunk, str):
+            return self.sections[chunk]
+        raise TypeError("index must be string")
+
+
+if __name__ == "__main__":
+    import argparse
+
+    PARSER = argparse.ArgumentParser()
+    PARSER.add_argument("--section", "-s", dest="section",
+                        default=None, metavar="SECTION")
+    PARSER.add_argument("--option", "-o", dest="option",
+                        default=None, metavar="OPTION")
+    PARSER.add_argument("--config", "-c", dest="config",
+                        default=None, metavar="FILE")
+    PARSER.add_argument("--filename", "-f", dest="expand_filename",
+                        default=False, action='store_true')
+    ARGS = PARSER.parse_args()
+
+    TC = TalerConfig.from_file(ARGS.config)
+
+    if ARGS.section is not None and ARGS.option is not None:
+        if ARGS.expand_filename:
+            X = TC.value_filename(ARGS.section, ARGS.option)
+        else:
+            X = TC.value_string(ARGS.section, ARGS.option)
+        if X is not None:
+            print(X)
+    else:
+        TC.dump()

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]