Fork the GNU Taler sources

This commit is contained in:
Ghost-ate-your-brains 2021-10-28 20:23:29 +03:00
parent ed877a4f93
commit 36e102131a
76 changed files with 12420 additions and 2 deletions

3
AUTHORS Normal file
View File

@ -0,0 +1,3 @@
Marcello Stanisci
Florian Dold
Christian Grothoff

661
COPYING Normal file
View File

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

21
ChangeLog Normal file
View File

@ -0,0 +1,21 @@
Sun 08 Aug 2021 08:44:35 PM CEST
Releasing taler-bank 0.8.2. -CG
Tue Apr 10 02:39:19 CEST 2018
Releasing taler-bank-0.5.1 -FD
Tue Feb 13 10:05:06 CET 2018
Introducing the /reject REST API, internal wire
transfers between users, and a middleware-based
exceptions management.
Thu Jul 13 11:43:05 CEST 2017
Testing against ill-formed config file
Tue Jun 6 14:39:45 CEST 2017
Implementing /history API where any authenticated user
can check his financial situation. Removing the "admin"
interface. Implementing debt limiting. -MS
Wed Jun 1 17:27:36 CEST 2016
Releasing taler-bank-0.0.0. -CG

368
INSTALL Normal file
View File

@ -0,0 +1,368 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
Foundation, Inc.
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 warranty of any kind.
Basic Installation
==================
Briefly, the shell command './configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the 'README' file for
instructions specific to this package. Some packages provide this
'INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The 'configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a 'Makefile' in each directory of the package.
It may also create one or more '.h' files containing system-dependent
definitions. Finally, it creates a shell script 'config.status' that
you can run in the future to recreate the current configuration, and a
file 'config.log' containing compiler output (useful mainly for
debugging 'configure').
It can also use an optional file (typically called 'config.cache' and
enabled with '--cache-file=config.cache' or simply '-C') that saves the
results of its tests to speed up reconfiguring. Caching is disabled by
default to prevent problems with accidental use of stale cache files.
If you need to do unusual things to compile the package, please try
to figure out how 'configure' could check whether to do them, and mail
diffs or instructions to the address given in the 'README' so they can
be considered for the next release. If you are using the cache, and at
some point 'config.cache' contains results you don't want to keep, you
may remove or edit it.
The file 'configure.ac' (or 'configure.in') is used to create
'configure' by a program called 'autoconf'. You need 'configure.ac' if
you want to change it or regenerate 'configure' using a newer version of
'autoconf'.
The simplest way to compile this package is:
1. 'cd' to the directory containing the package's source code and type
'./configure' to configure the package for your system.
Running 'configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type 'make' to compile the package.
3. Optionally, type 'make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type 'make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the 'make install' phase executed with root
privileges.
5. Optionally, type 'make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior 'make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing 'make clean'. To also remove the
files that 'configure' created (so you can compile the package for
a different kind of computer), type 'make distclean'. There is
also a 'make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type 'make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide 'make
distcheck', which can by used by developers to test that all other
targets like 'make install' and 'make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the 'configure' script does not know about. Run './configure --help'
for details on some of the pertinent environment variables.
You can give 'configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here is
an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU 'make'. 'cd' to the
directory where you want the object files and executables to go and run
the 'configure' script. 'configure' automatically checks for the source
code in the directory that 'configure' is in and in '..'. This is known
as a "VPATH" build.
With a non-GNU 'make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use 'make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple '-arch' options to the
compiler but only a single '-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the 'lipo' tool if you have problems.
Installation Names
==================
By default, 'make install' installs the package's commands under
'/usr/local/bin', include files under '/usr/local/include', etc. You
can specify an installation prefix other than '/usr/local' by giving
'configure' the option '--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like '--bindir=DIR' to specify different values for particular
kinds of files. Run 'configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the default
for these options is expressed in terms of '${prefix}', so that
specifying just '--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to 'configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
'make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, 'make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
'${prefix}'. Any directories that were specified during 'configure',
but not in terms of '${prefix}', must each be overridden at install time
for the entire installation to be relocated. The approach of makefile
variable overrides for each directory variable is required by the GNU
Coding Standards, and ideally causes no recompilation. However, some
platforms have known limitations with the semantics of shared libraries
that end up requiring recompilation when using this method, particularly
noticeable in packages that use GNU Libtool.
The second method involves providing the 'DESTDIR' variable. For
example, 'make install DESTDIR=/alternate/directory' will prepend
'/alternate/directory' before all installation names. The approach of
'DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of '${prefix}'
at 'configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving 'configure' the
option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
Some packages pay attention to '--enable-FEATURE' options to
'configure', where FEATURE indicates an optional part of the package.
They may also pay attention to '--with-PACKAGE' options, where PACKAGE
is something like 'gnu-as' or 'x' (for the X Window System). The
'README' should mention any '--enable-' and '--with-' options that the
package recognizes.
For packages that use the X Window System, 'configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the 'configure' options '--x-includes=DIR' and
'--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of 'make' will be. For these packages, running './configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with 'make V=1'; while running './configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with 'make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX 'make' updates targets which have the same time stamps as their
prerequisites, which makes it generally unusable when shipped generated
files such as 'configure' are involved. Use GNU 'make' instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
workaround. If GNU CC is not installed, it is therefore recommended to
try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
in your 'PATH', put it _after_ '/usr/bin'.
On Haiku, software installed for all users goes in '/boot/common',
not '/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features 'configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, 'configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
'--build=TYPE' option. TYPE can either be a short name for the system
type, such as 'sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file 'config.sub' for the possible values of each field. If
'config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option '--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with '--host=TYPE'.
Sharing Defaults
================
If you want to set default values for 'configure' scripts to share,
you can create a site shell script called 'config.site' that gives
default values for variables like 'CC', 'cache_file', and 'prefix'.
'configure' looks for 'PREFIX/share/config.site' if it exists, then
'PREFIX/etc/config.site' if it exists. Or, you can set the
'CONFIG_SITE' environment variable to the location of the site script.
A warning: not all 'configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to 'configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the 'configure' command line, using 'VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified 'gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
Autoconf limitation. Until the limitation is lifted, you can use this
workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
'configure' Invocation
======================
'configure' recognizes the following options to control how it
operates.
'--help'
'-h'
Print a summary of all of the options to 'configure', and exit.
'--help=short'
'--help=recursive'
Print a summary of the options unique to this package's
'configure', and exit. The 'short' variant lists options used only
in the top level, while the 'recursive' variant lists options also
present in any nested packages.
'--version'
'-V'
Print the version of Autoconf used to generate the 'configure'
script, and exit.
'--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally 'config.cache'. FILE defaults to '/dev/null' to
disable caching.
'--config-cache'
'-C'
Alias for '--cache-file=config.cache'.
'--quiet'
'--silent'
'-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to '/dev/null' (any error
messages will still be shown).
'--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
'configure' can determine that directory automatically.
'--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names:: for
more details, including other options available for fine-tuning the
installation locations.
'--no-create'
'-n'
Run the configure checks, but stop before creating any output
files.
'configure' also accepts some other, not widely useful, options. Run
'configure --help' for more details.

0
NEWS Normal file
View File

122
README.md
View File

@ -1,3 +1,121 @@
# bank
# GNU Taler Demo Bank
The base bank repository, based on GNU Taler
This code implements a bank Web portal that tightly integrates
with the GNU Taler payment system. The bank it primarily meant
be used as part of a demonstrator for the Taler system, but
can also be used in standalone deployments for a regional/local
currency.
## Deprecation Notice
This project is being replaced with https://git.taler.net/libeufin.git
No more feature will be worked in this project so hack at your own risk.
## Installation
### PyPI
To install the bank without building it from source, run
```
$ pip3 install --user talerbank
```
to get the latest version published in the Python Package Index (PyPI).
### Prerequisites
To build the bank, you need:
* ``python>=3.8``
* ``pip3``
* ``poetry``
* Either install with ``pip3 install --user poetry``, your distribution's package manager,
or as recommended in the [poetry documentation](https://python-poetry.org/docs/#installation).
### GNU-Style Installation
If you are building from the git repository, first run ``./boostrap``.
Then run
```
$ ./configure
$ make install
```
to install the bank for the current user.
### Custom Installation
To install with custom options, first build the wheel for the bank
and then install via ``pip3``:
```
$ ./configure
$ make wheel
$ pip3 $CUSTOM_OPTIONS ./dist/talerbank-$VERSION-py3-none-any.whl
```
## Internationalization
After new strings to translate enter the bank's text, run the
following command in order to expand the current PO files:
```
$ make i18n-strings
```
It is then required to put the extended PO files under versioning.
The following command creates *new* languages to be translated. In
particular, it produces the PO file for the language being added.
```
$ python3 manage.py makemessages -l $LANG
```
However, you should probably use https://weblate.taler.net/ to add
new languages instead.
## Configuring the Bank
The bank obeys to the INI syntax for configuration files.
When launched, the bank will by default look for a configuration
file located at ~/.config/taler.conf. To override this behaviour,
give the -c option when launching the bank.
The following configuration instance makes the bank serve over
HTTP, at port 5882.
```
[taler]
currency = LOCALKUDOS
[bank]
serve = http
http_port = 5882
database = postgres:///talerlocal
max_debt = LOCALKUDOS:500.0
max_debt_bank = LOCALKUDOS:1000000000.0
allow_registrations = YES
base_url = http://localhost:5882/
suggested_exchange = http://localhost:5884/
```
## Launching the Bank
```
$ taler-bank-manage serve
```
## Running Tests
From the repository's top directory:
$ make check
## How to Force Migrations
https://simpleisbetterthancomplex.com/tutorial/2016/07/26/how-to-reset-migrations.html

10
bank.conf Normal file
View File

@ -0,0 +1,10 @@
[bank]
# Which database should we use?
DATABASE = postgres:///talerbank
# FIXME
MAX_DEBT = KUDOS:50.0
# FIXME
MAX_DEBT_BANK = KUDOS:0.0

19
bootstrap Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
# Bootstrap the repository. Used when the repository is checked out from git.
# When using the source tarball, running this script is not necessary.
set -eu
# Skip git-commands when installing from a TGZ package.
if [ -d .git ] ; then
if ! git --version >/dev/null; then
echo "git not installed"
exit 1
fi
git submodule sync
git submodule update --init
fi
rm -f ./configure
cp build-system/taler-build-scripts/configure ./configure

60
build-system/Makefile Normal file
View File

@ -0,0 +1,60 @@
# This Makefile is in the public domain
version := $(shell poetry version | awk '{ print $$2 }')
.PHONY: all
all: wheel
.PHONY: poetry-install
poetry-install:
poetry install
.PHONY: wheel
wheel: poetry-install
poetry run python3 manage.py compilemessages
poetry build -f wheel
.PHONY: install
install: wheel
pip3 install --upgrade --ignore-installed --user "dist/talerbank-$(version)-py3-none-any.whl"
.PHONY: uninstall
uninstall:
pip3 uninstall talerbank
.PHONY: i18n-strings
i18n-strings: poetry-install
poetry run python3 manage.py makemessages -l it
poetry run python3 manage.py makemessages -l de
# Also update template for new languages
poetry run python3 manage.py makemessages -l en
mv talerbank/app/locale/en/LC_MESSAGES/django.po talerbank/app/locale/django.pot
rm -r talerbank/app/locale/en/
# See bug #5850 for some test cases that are currently disabled.
.PHONY: check
check: poetry-install
poetry run ./run-tests.sh
.PHONY: clean
clean:
rm -rf dist
configure: build-system/taler-build-scripts/
./bootstrap
.PHONY: dist
dist: configure
./build-system/taler-build-scripts/archive-with-submodules/git_archive_all.py --include ./bootstrap --include ./configure taler-bank-$(version).tar.gz
.PHONY: pretty
pretty: poetry-install
poetry run black talerbank/
Makefile: build-system/Makefile configure
@echo updating makefile
cp build-system/Makefile .

26
build-system/configure.py Normal file
View File

@ -0,0 +1,26 @@
# This configure.py file is places in the public domain.
# Configure the build directory.
# This file is invoked by './configure' and should usually not be invoked
# manually.
import talerbuildconfig as tbc
import sys
import shutil
if getattr(tbc, "serialversion", 0) < 2:
print("talerbuildconfig outdated, please update the build-common submodule and/or bootstrap")
sys.exit(1)
b = tbc.BuildConfig()
# Declare dependencies
b.add_tool(tbc.PosixTool("find"))
b.add_tool(tbc.PosixTool("awk"))
b.add_tool(tbc.GenericTool("pip3", version_arg="--version"))
b.add_tool(tbc.GenericTool("poetry", version_arg="-V"))
b.run()
print("copying Makefile")
shutil.copyfile("build-system/Makefile", "Makefile")

331
contrib/Doxyfile Normal file
View File

@ -0,0 +1,331 @@
# Doxyfile 1.8.13
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "taler-demobank"
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = doxygen-doc/
CREATE_SUBDIRS = yes
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING = in=Python
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 0
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = NO
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT =
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.py *.in
RECURSIVE = YES
EXCLUDE = Makefile.in
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

35
contrib/django_wsgi.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
# This file is in the public domain
# Launch uwsgi: i.e. connects django to nginx
print_usage () {
echo Usage ./django_wsgi.sh virtualenv_dir/
exit
}
PROJ_DIR=""
ENV_DIR=""
#if test -d "$1";
# then PROJ_DIR=$1
#else echo "Please give a valid project directory"
# print_usage
#fi
if test -d "$1";
then ENV_DIR=$1
else echo "Please give a valid virtualenv's directory"
print_usage
fi
set -e
pids=$(pidof uwsgi || true)
if [[ ! -z "$pids" ]]; then
kill $pids || true
sleep 1
kill -9 $pids || true
fi
echo "Killed previous instance"
source $ENV_DIR/bin/activate
exec uwsgi --master --die-on-term --socket :8000 --module bank_project.wsgi

View File

@ -0,0 +1,21 @@
# this config example is in the public domain
# the upstream component nginx needs to connect to
upstream django {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 80;
# the domain name it will serve for
server_name localdjango; # substitute your machine's IP address or FQDN
charset utf-8;
location / {
uwsgi_pass django;
include /home/marcello/static_test/uwsgi_params;
}
}

View File

@ -0,0 +1,16 @@
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;

49
doc/taler-bank-manage.1 Normal file
View File

@ -0,0 +1,49 @@
.Dd May 13, 2016
.Dt TALER-BANK-MANAGE 1
.Os
.Sh NAME
.Nm taler-bank-manage
.Nd Manager script for the Taler bank
.Sh SYNOPSIS
.Nm
.Op Fl -django
.Op Fl -serve-http
.Op Fl -serve-uwsgi
.Op Fl -sampledata
.Op Fl -config
.Ao Ar options Ac
.Sh DESCRIPTION
.Nm
is a command line tool to manage the Taler bank demonstrator.
It is mandatory to provide a switch.
Each switch has its own set of options.
Give
.Fl -help
to each switch in order to get the list of supported options.
.Ss SWITCHES
.Bl -tag -width Ds
.It Fl -django
This switch is a mere wrapper for the django native tool
.Xr django-admin 1 ,
therefore it takes the same options.
Refer to the official django-admin documentation.
.It Fl -serve-http
Launches the bank Web service at the port given in the
.Fl -port
option.
.It Fl -serve-uwsgi
Launches the bank over UWSGI.
Typically used in conjunction with a Web server which acts like
a reverse proxy.
.It Fl -sampledata
Populates the bank's database with sample wire transfers.
.It Fl -config
Shows all of Taler's configuration.
it just retrieves values from default configuration files.
.El
.\".Sh EXAMPLES
.Sh SEE ALSO
.Xr django-admin 1
.\".Sh HISTORY
.\".Sh AUTHORS
.\".Sh BUGS

20
manage.py Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
if "compilemessages" not in sys.argv and "makemessages" not in sys.argv:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'talerbank.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

687
poetry.lock generated Normal file
View File

@ -0,0 +1,687 @@
[[package]]
name = "appdirs"
version = "1.4.4"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "asgiref"
version = "3.3.4"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
[[package]]
name = "astroid"
version = "2.5.6"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
python-versions = "~=3.6"
[package.dependencies]
lazy-object-proxy = ">=1.4.0"
wrapt = ">=1.11,<1.13"
[[package]]
name = "babel"
version = "2.9.1"
description = "Internationalization utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
pytz = ">=2015.7"
[[package]]
name = "black"
version = "20.8b1"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
appdirs = "*"
click = ">=7.1.2"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.6,<1"
regex = ">=2020.1.8"
toml = ">=0.10.1"
typed-ast = ">=1.4.0"
typing-extensions = ">=3.7.4"
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
[[package]]
name = "certifi"
version = "2020.12.5"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "chardet"
version = "4.0.0"
description = "Universal encoding detector for Python 2 and 3"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "click"
version = "7.1.2"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "django"
version = "3.2.3"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
asgiref = ">=3.3.2,<4"
pytz = "*"
sqlparse = ">=0.2.2"
[package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
name = "idna"
version = "2.10"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "isort"
version = "5.8.0"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
[package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
[[package]]
name = "jinja2"
version = "3.0.0"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
MarkupSafe = ">=2.0.0rc2"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "lazy-object-proxy"
version = "1.6.0"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "lxml"
version = "4.6.3"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
[package.extras]
cssselect = ["cssselect (>=0.7)"]
html5 = ["html5lib"]
htmlsoup = ["beautifulsoup4"]
source = ["Cython (>=0.29.7)"]
[[package]]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "mock"
version = "4.0.3"
description = "Rolling backport of unittest.mock for all Pythons"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
build = ["twine", "wheel", "blurb"]
docs = ["sphinx"]
test = ["pytest (<5.4)", "pytest-cov"]
[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "pathspec"
version = "0.8.1"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "psycopg2"
version = "2.8.6"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
[[package]]
name = "pylint"
version = "2.8.2"
description = "python code static checker"
category = "dev"
optional = false
python-versions = "~=3.6"
[package.dependencies]
astroid = ">=2.5.6,<2.7"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
isort = ">=4.2.5,<6"
mccabe = ">=0.6,<0.7"
toml = ">=0.7.1"
[[package]]
name = "pytz"
version = "2021.1"
description = "World timezone definitions, modern and historical"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "qrcode"
version = "6.1"
description = "QR Code image generator"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
six = "*"
[package.extras]
dev = ["tox", "pytest", "mock"]
maintainer = ["zest.releaser"]
pil = ["pillow"]
test = ["pytest", "pytest-cov", "mock"]
[[package]]
name = "regex"
version = "2021.4.4"
description = "Alternative regular expression module, to replace re."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "requests"
version = "2.25.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
certifi = ">=2017.4.17"
chardet = ">=3.0.2,<5"
idna = ">=2.5,<3"
urllib3 = ">=1.21.1,<1.27"
[package.extras]
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sqlparse"
version = "0.4.1"
description = "A non-validating SQL parser."
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "taler-util"
version = "0.8.3"
description = "Util library for GNU Taler"
category = "main"
optional = false
python-versions = ">=3.7"
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "typed-ast"
version = "1.4.3"
description = "a fork of Python 2 and 3 ast modules with type comment support"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "typing-extensions"
version = "3.10.0.0"
description = "Backported and Experimental Type Hints for Python 3.5+"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "urllib3"
version = "1.26.4"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
[package.extras]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
brotli = ["brotlipy (>=0.6.0)"]
[[package]]
name = "uwsgi"
version = "2.0.19.1"
description = "The uWSGI server"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "wrapt"
version = "1.12.1"
description = "Module for decorators, wrappers and monkey patching."
category = "dev"
optional = false
python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "9774137a0dd606c6a3608f64563d91bbbfc000695d8a13d397e4b57318a204c6"
[metadata.files]
appdirs = [
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
]
asgiref = [
{file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"},
{file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"},
]
astroid = [
{file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"},
{file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"},
]
babel = [
{file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
{file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
]
black = [
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
]
certifi = [
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
]
chardet = [
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
]
click = [
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
django = [
{file = "Django-3.2.3-py3-none-any.whl", hash = "sha256:7e0a1393d18c16b503663752a8b6790880c5084412618990ce8a81cc908b4962"},
{file = "Django-3.2.3.tar.gz", hash = "sha256:13ac78dbfd189532cad8f383a27e58e18b3d33f80009ceb476d7fcbfc5dcebd8"},
]
idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
isort = [
{file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"},
{file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"},
]
jinja2 = [
{file = "Jinja2-3.0.0-py3-none-any.whl", hash = "sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6"},
{file = "Jinja2-3.0.0.tar.gz", hash = "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
]
lxml = [
{file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"},
{file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"},
{file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"},
{file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"},
{file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"},
{file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"},
{file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16"},
{file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"},
{file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"},
{file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617"},
{file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"},
{file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"},
{file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92"},
{file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"},
{file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"},
{file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"},
{file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"},
{file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae"},
{file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"},
{file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"},
{file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"},
{file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"},
{file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a"},
{file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"},
{file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"},
{file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"},
]
markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
mock = [
{file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"},
{file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"},
]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
pathspec = [
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
{file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
]
psycopg2 = [
{file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"},
{file = "psycopg2-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5"},
{file = "psycopg2-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad"},
{file = "psycopg2-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3"},
{file = "psycopg2-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821"},
{file = "psycopg2-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301"},
{file = "psycopg2-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a"},
{file = "psycopg2-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d"},
{file = "psycopg2-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84"},
{file = "psycopg2-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5"},
{file = "psycopg2-2.8.6-cp38-cp38-win32.whl", hash = "sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e"},
{file = "psycopg2-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051"},
{file = "psycopg2-2.8.6-cp39-cp39-win32.whl", hash = "sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3"},
{file = "psycopg2-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7"},
{file = "psycopg2-2.8.6.tar.gz", hash = "sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543"},
]
pylint = [
{file = "pylint-2.8.2-py3-none-any.whl", hash = "sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b"},
{file = "pylint-2.8.2.tar.gz", hash = "sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217"},
]
pytz = [
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
]
qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
]
regex = [
{file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"},
{file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"},
{file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"},
{file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"},
{file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"},
{file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"},
{file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"},
{file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"},
{file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"},
{file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"},
{file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"},
{file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"},
{file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"},
{file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"},
{file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"},
{file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"},
{file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"},
{file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"},
{file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"},
{file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"},
{file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"},
{file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"},
{file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"},
{file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"},
{file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"},
{file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"},
{file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"},
{file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"},
{file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"},
]
requests = [
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sqlparse = [
{file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"},
{file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"},
]
taler-util = [
{file = "taler-util-0.8.3.tar.gz", hash = "sha256:3ff81faf6dff494ae1727d47cec9d853d8719f9d5325a1e7775fa3cc9ded88c1"},
{file = "taler_util-0.8.3-py3-none-any.whl", hash = "sha256:b00105961024ce73f3142aaa6379e17251815ce7bc4b3a472d91efd9089a5003"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
typed-ast = [
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
{file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
{file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
{file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
{file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
{file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
{file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
{file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
{file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
{file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
{file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
{file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
{file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
{file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
{file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
{file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
]
typing-extensions = [
{file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
{file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
{file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
]
urllib3 = [
{file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
{file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
]
uwsgi = [
{file = "uWSGI-2.0.19.1.tar.gz", hash = "sha256:faa85e053c0b1be4d5585b0858d3a511d2cd10201802e8676060fd0a109e5869"},
]
wrapt = [
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
]

34
pyproject.toml Normal file
View File

@ -0,0 +1,34 @@
[tool.poetry]
name = "talerbank"
version = "0.8.2"
description = "Taler demo bank"
authors = ["Marcello Stanisci <ms@taler.net>", "Florian Dold <dold@taler.net"]
license = "GPL-3.0-or-later"
include = ["talerbank/app/locale/*/LC_MESSAGES/django.mo"]
exclude = ["talerbank/app/locale/*/LC_MESSAGES/django.po"]
[tool.poetry.dependencies]
python = "^3.8"
django = "^3.1.3"
taler-util = "^0.8.3"
lxml = "^4.6.1"
psycopg2 = "^2.8.6"
Jinja2 = "^3.0.0"
qrcode = "^6.1"
uWSGI = "^2.0.19"
requests = "^2.24.0"
click = "^7.1.2"
Babel = "^2.8.0"
[tool.poetry.dev-dependencies]
pylint = "^2.6.0"
django = "^3.1.3"
black = "^20.8b1"
mock = "^4.0.2"
[tool.poetry.scripts]
taler-bank-manage = 'talerbank.cli:run'
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

11
run-one-test.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# See Bug #5850 for some tests that are currently skipped
if [[ -z $1 ]]; then
echo "Usage: ./run-one-test.sh 'TestClassName'"
exit 0
fi
export TALER_CONFIG_FILE="talerbank/app/testconfigs/bank-check.conf"
exec ./manage.py test --no-input talerbank.app.tests.$1

6
run-tests.sh Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
# See Bug #5850 for some tests that are currently skipped
export TALER_CONFIG_FILE="talerbank/app/testconfigs/bank-check.conf"
exec ./manage.py test --no-input talerbank.app.tests "$@"

4
talerbank/__init__.py Normal file
View File

@ -0,0 +1,4 @@
import logging
FMT = "%(asctime)-15s %(module)s %(levelname)s %(message)s"
logging.basicConfig(format=FMT, level=logging.WARNING)

View File

7
talerbank/app/admin.py Normal file
View File

@ -0,0 +1,7 @@
# This file is in the public domain
from django.contrib import admin
from .models import BankAccount, BankTransaction
admin.site.register(BankAccount)
admin.site.register(BankTransaction)

View File

@ -0,0 +1,321 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 07:51+0000\n"
"PO-Revision-Date: 2021-01-30 10:01+0000\n"
"Last-Translator: Weblate Admin <admin@example.com>\n"
"Language-Team: German <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Fehler"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Die Seite wurde nicht gefunden!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Bank - Taler Demo"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Die Demo-Seiten der Bank veranschaulichen, wie sich die Buchungen mit GNU "
"Taler auf verschiedenen Bankkonten auswirken. Durch Klicken auf die "
"Tabulatoren erhält man eine Liste der Buchungen in einzelnen <a href=\""
"{public_accounts}\">öffentlich sichtbaren Konten</a>: Für den Exchange, die "
"Bank, den Umfrage-Anbieter (Tipping/Survey-Beispiel) und andere Bankkunden. "
"Die Zahlungsströme sind leicht nachvollziehbar, wenn man diese Buchungen mit "
"den Buchungen auf dem eigenen Konto, von dem die Geldwerte abgebucht wurden, "
"in Beziehung setzt."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Einführung"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Bank"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Aufsatzsammlung"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Spenden"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Tipping/Umfrage"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Alles Wissenswerte zu GNU Taler gibt es auf unseren <a "
"href={taler_site}>Webseiten</a>."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Willkommen bei der {currency} Bank!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Bitte melden Sie sich an!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "Nutzername / Passwort abgelehnt. Bitte nochmals versuchen."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"Ihr Konto hat keinen Zugang zu dieser Seite. Bitte melden Sie sich mit einem "
"Konto an, das zum Zugang berechtigt ist."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Bitte melden Sie sich an."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Wenn Sie noch kein Bankkonto haben, registrieren Sie sich bitte unter <a "
"href=\"{register_link}\">Registrierung</a>. Die Eröffnung eines Konto "
"geschieht einfach und schnell und belohnt Sie mit einem Startguthaben von "
"100 {currency}."
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr ""
"Die Registrierungsdaten sind nichtöffentlich und auch nicht von Dritten "
"einsehbar."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Bitte betrachten Sie die <a href=\"{public_accounts}\">öffentlich sichtbaren "
"Konten</a>, um die Überweisungen vom Treuhandkonto des Exchange an die "
"Bankkonten der Zahlungsempfänger zu sehen."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Ausloggen"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Überweisung"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr ""
"Geld überweisen mit dem payto-Schema (Uniform Resource Identifier gemäß RFC "
"8905):"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "payto-Adresse"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Bestätigen"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Willkommen <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Kontostand"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Geld in ein Taler-Wallet abheben"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Abzuhebender Betrag"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Abhebevorgang auslösen"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Geld überweisen"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Transaktionen für"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Datum"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Betrag"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Empfänger"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Verwendungszweck"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "abgebrochen"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Keine Transaktionen zu / von diesem Konto"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Buchungen auf öffentlich sichtbaren Konten"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "Konto"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr ""
"Keine Bewegungen für Konto Nr. {account_number} ({account_name}) anzuzeigen."
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Bitte eröffnen Sie ein Konto bei der {currency}-Bank!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Zurück"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Diese Nutzerkennung ist nicht mehr verfügbar, bitte wählen Sie eine andere!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Abhebung bestätigen"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"Die {currency}-Bank bittet Sie um Bestätigung, dass Sie den Betrag von "
"<b>{amount}</b> des Exchange <b>{exchange}</b> in Ihr Wallet abheben wollen. "
"Um zu beweisen, dass das Bankkonto Ihnen gehört, beantworten Sie bitte "
"folgende Sicherheitsfrage (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Wie viel ist {question}?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"Eine echte Bank würde an dieser Stelle die PIN der Konteninhaber abfragen, "
"eine TAN an ein Mobilfunkgerät senden und diese TAN abfragen oder einen TAN-"
"Generator verwenden lassen. Stattdessen gibt es hier diese kleine "
"Rechenaufgabe."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "In ein Taler-Wallet abheben"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Es sieht so aus, als würde Ihr Browser Zahlungen mit GNU Taler nicht "
"unterstützen. Sie können jedoch alternativ eine <a href=\"{wallet_link"
"}\">Browser-Erweiterung</a> installieren (wallet browser extension)."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr ""
"Sie können diesen QR-Code verwenden, um in Ihr mobiles Wallet abzuheben:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Klicken Sie <a href=\"{taler_withdraw_uri}\">diesen Link</a>, um das Wallet "
"auf Ihrem System zu öffnen."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr ""
"Es wurden fehlerhafte Daten oder ein unpassendes Datenformat übermittelt!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Überweisung erfolgreich ausgeführt!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Registrierung erfolgreich!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "Die Captcha-Frage wurde leider nicht richtig beantwortet."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Abheben ins Wallet erfolgreich!"

View File

@ -0,0 +1,279 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr ""
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr ""
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr ""
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr ""
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr ""
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr ""
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr ""
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr ""
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr ""
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr ""
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr ""
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr ""
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr ""
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr ""
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr ""
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr ""
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr ""
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr ""
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr ""
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr ""
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr ""
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr ""
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr ""
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr ""
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr ""
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr ""
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr ""
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr ""
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr ""
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr ""
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr ""
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr ""
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr ""
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr ""
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr ""
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr ""
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr ""
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr ""
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr ""
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr ""
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr ""
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr ""
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr ""
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr ""
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr ""

View File

@ -0,0 +1,306 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-02-23 11:28+0000\n"
"Last-Translator: Stefan <eintritt@hotmail.com>\n"
"Language-Team: Spanish <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/es/>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Error"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Página no encontrada!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Banco - Taler Demo"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Esta parte de la demo muestra como un banco que soporta Taler funcionaría "
"directamente . Además usando tu propia cuenta bancaria, puedes ver el "
"historial de transacciones de algunas <a href=\"{public_accounts}\">cuentas "
"Públicas</a>."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Introducción"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Banco"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Tienda de pruebas"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Donaciones"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Consejos/Encuestas"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Puedes aprender más sobre Taler en nuestra <a href={taler_site}>página Web</"
"a> principal."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Bienvenido al banco de {currency}!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Por favor inicia sesión!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "Tu usuario y contraseña no coinciden. Por favor prueba de nuevo."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"Tu cuenta no tiene acceso a esta página. Para proceder, por favor inicia "
"sesión con una cuenta que tenga acceso."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Por favor inicia sesión para ver esta página."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Si tu eres un nuevo cliente por favor <a href=\"{register_link}\""
">regístrate</a>. El registro es rápido y gratuito, y te da un bono de "
"registro de 100 {currency}"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "Las inscripciones no están abiertas al público."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Para ver transacciones de cuentas públicas, por favor <a href=\""
"{public_accounts}\">haz click aquí</a>."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Cierre de sesión"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Transferencia bancaria"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Transferir dinero a través del sistema payto:"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "dirección payto"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Confirmar"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Bienvenido <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Balance de cuenta bancaria"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Retirar dinero hacia una cartera Taler"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Cantidad a retirar"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Comenzar la retirada"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Transferir dinero"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Transacciones para"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Fecha"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Cantidad"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Contraparte"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Asunto"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "cancelada"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Ninguna transacción realizada a/desde esta cuenta"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Historial de cuentas públicas"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "cuenta"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "Sin historia para la cuenta #{account_number} ({account_name}) todavía"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Registro en el banco de {currency} !"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Atrás"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Disculpa, el nombre de usuario ya no está disponible. Por favor elige otro!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Confirmar retirada"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"{currency} El banco necesita verificar que tú intentas retirar la cantidad "
"de <b>{amount}</b> desde <b>{exchange}</b>.Para comprobar que tú eres el "
"propietario de la cuenta , por favor contesta la siguiente &quot;pregunta de "
"seguridad &quot; (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Qué es {question}?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"Un banco real debe solicitar un PIN/TAN en vez de un simple cálculo. Por "
"ejemplo mandando una contraseña de un solo uso al teléfono móvil del cliente "
"o proveyéndole de un generador de contraseñas aleatorio."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Retirar hacia la cartera Taler"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Parece ser que tu navegador no soporta los pagos GNU Taler. Puedes probar a "
"instalar <a href=\"{wallet_link}\">un complemento para navegador</a>."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "Puedes usar este código QR para retirar a la cartera de tu móvil:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Haz clic en <a href=\"{taler_withdraw_uri}\">este enlace</a> para abrir la "
"cartera Taler de tu sistema si esta existe."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "Formulario erróneo enviado!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Transferencia bancaria realizada con éxito!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Registro realizado con éxito!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "Respuesta CAPTCHA errónea."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Retirada realizada con éxito!"

View File

@ -0,0 +1,311 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-03-17 18:31+0000\n"
"Last-Translator: Marianne Le Guennec <marianne.leguennec@gmail.com>\n"
"Language-Team: French <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/fr/>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Erreur"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Page introuvable !"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Banque - Taler Démo"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Cette section de démo vous montre comment fonctionnerait une banque qui "
"soutiendrait directement Taler. En plus de passer par votre propre compte "
"bancaire, vous avez accès à l'historique des transactions de certains <a "
"href=\"{public_accounts}\">Comptes Publiques</a>."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Introduction"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Banque"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Boutique de test"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Dons"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Conseils/Sondage"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Pour en savoir plus sur Taler, vous pouvez consulter notre <a "
"href={taler_site}>site</a> principal."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Bienvenue dans votre Banque {currency} !"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Veuillez vous identifier !"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Une erreur s'est produite avec votre nom d'utilisateur ou votre mot de "
"passe. Veuillez réessayer."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"Vous n'avez pas accès à cette page depuis votre compte. Afin de poursuivre, "
"veuillez vous connecter depuis un compte y permettant l'accès."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Veuillez vous identifier pour accéder à cette page."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Si vous êtes un nouvel utilisateur, veuillez vous <a href=\"{register_link}\""
">inscrire</a>. L'inscription est rapide et gratuite. Vous bénéficiez d'un "
"bonus de 100 {currency} à l'inscription."
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "Les inscriptions ne sont pas ouvertes au public."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Pour accéder aux transactions de comptes publics, <a href=\"{public_accounts}"
"\">cliquez ici</a>."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Déconnexion"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Virement bancaire"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Virer de l'argent via le système payto :"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "Adresse payto"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Confirmer"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Bienvenue <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Solde du compte bancaire"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Retirer de l'argent dans un portefeuille Taler"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Montant à retirer"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Retrait initial"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Virement bancaire"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Transactions pour"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Date"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Montant"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Contrepartie"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Sujet"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "Annulé"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Aucune transaction effectuée vers/depuis ce compte"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Historique de comptes publiques"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "compte"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr ""
"Aucun historique pour le compte #{account_number} ({account_name}) pour "
"l'instant"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Inscrivez-vous à la banque {currency} !"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Retour"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr "Désolé, ce nom d'utilisateur est déjà pris. Choisissez-en un autre !"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Confirmer le retrait"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"La banque {currency} a besoin de vérifier que vous avez l'intention de "
"retirer <b>{amount}</b> de <b>{exchange}</b> . Pour prouver que vous êtes le "
"propriétaire du compte, veuillez répondre aux &quot; questions de sécurité "
"&quot; suivantes (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Qu'est-ce que {question} ?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"Une vraie banque devrait demander un PIN/TAN au lieu d'un simple chiffrage. "
"En envoyant, par example, un mot de passe à usage unique au téléphone du "
"client ou de fournir un générateur de mot de passe aléatoire."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Retirer vers un portefeuille Taler"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Il semblerait que votre navigateur ne soit pas compatible avec les paiements "
"GNU Taler. Vous pouvez essayer d'installer un <a href=\"{wallet_link}\""
">portefeuille en extension du navigateur</a> ."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr ""
"Vous pouvez utiliser ce QR Code pour retirer vers votre portefeuille mobile :"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Cliquez sur <a href=\"{taler_withdraw_uri}\">ce lien</a> pour ouvrir votre "
"portefeuille Taler, s'il existe."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "Mauvais formulaire soumit !"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Virement bancaire réussi !"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Inscription réussie !"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "CAPTCHA erroné ."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Retrait réussi !"

View File

@ -0,0 +1,307 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-02-24 16:13+0000\n"
"Last-Translator: MS <ms@taler.net>\n"
"Language-Team: Italian <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/it/>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Errore"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Pagina non trovata!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "Banca {currency} - Taler Demo"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Questa parte della demo mostra come dovrebbe funzionare una banca che "
"supporti appieno il protocollo Taler. Oltre al tuo conto personale, è "
"possibile visualizzare lo storico di alcuni <a href=\"{public_accounts}\""
">conti pubblici</a>"
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Introduzione"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Banca"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Negozio di saggistica"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Donazioni"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Guadagna con il questionario"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Per maggiori informazioni su Taler, visita il nostro <a href={taler_site}"
">sito</a> "
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Benvenuti nella Banca {currency}"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Accedi!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "Il tuo nome utente e password sono incorretti. Prova di nuovo."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"Il tuo profilo non ha accesso a questa pagina. Per continuare, ti invitiamo "
"ad effettuare l'accesso con un profilo che abbia i diritti necessari."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Effettua l'accesso per vedere questa pagina."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Per diventare nuovo cliente, <a href=\"{register_link}\">clicca qui</a>. La "
"registrazione è facile e veloce, e ti regala un bonus di 100 {currency}"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "Le registrazioni sono al momento disabilitate"
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Clicca <a href=\"{public_accounts}\">qui</a> per visualizzare le transazioni "
"dei conti pubblici"
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Esci"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Effettua un bonifico"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Effettua un bonifico usando il sistema 'payto':"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "indirizzo 'payto'"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Conferma"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Benvenut* <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Bilancio"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Ricarica il tuo portafoglio Taler"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Cifra da ritirare"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Invia la richiesta"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Effettua un bonifico"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Storico di"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Data"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Cifra"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Controparte"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Causale"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "annullato"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Nessuna transazione effettuata"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Storico dei conti pubblici"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "conto"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr ""
"Ancora nessuno storico relativo al conto {account_number} ({account_name})"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Apri un conto presso la banca {currency}!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Indietro"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Ci dispiace, ma il nome utente scelto non è al momento disponibile. Prova "
"con un altro!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Conferma la richiesta"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"La banca {currency} deve verificare che tu intendi ritirare <b>{amount}</b> "
"da <b>{exchange}</b>. Per dimostrare che il proprietario del conto corrente "
"sia tu, ti preghiamo di rispondere alla seguente &quot;domanda di "
"sicurezza&quot; (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Indica il risultato di {question}:"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"Una banca vera dovrebbe comunicare un PIN aggiuntivo come misura di "
"sicurezza, invece di un semplice calcolo come avviene in questa pagina."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Ricarica il tuo portafoglio Taler"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Sembra che il tuo browser non supporti i pagamenti Taler, prova ad "
"installare il <a href=\"{wallet_link}\">portafoglio</a>."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "Usa questo QR-code per ricaricare il tuo portafoglio:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Clicca <a href=\"{taler_withdraw_uri}\">qui</a> per accedere al tuo "
"portafoglio."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "La richiesta effettuata non è valida."
#: talerbank/app/views.py:367
#| msgid "Wire transfer"
msgid "Wire transfer successful!"
msgstr "Bonifico effettuato con successo!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Registrazione avvenuta con successo!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "La risposta è incorretta."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Ricarica effettuata con successo!"

View File

@ -0,0 +1,291 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-03-15 15:11+0000\n"
"Last-Translator: Miyuki Komatsu <miykom@gmail.com>\n"
"Language-Team: Japanese <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/ja/>\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "エラー"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "ページが見つかりません!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} 銀行 - Talerデモ"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"デモのこの部分は、Talerを直接サポートしている銀行がどのように動作するかを示しています。自分の銀行口座を使うだけでなく、いくつかの <a href="
"\"{public_accounts}\">パブリックアカウント</a> の取引履歴を見ることもできます。"
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "序章"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "銀行"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "エッセイショップ"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "寄付"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "チップ/調査"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr "Talerの詳細については、メインの<a href={taler_site}>Webサイトをご覧ください</a>。"
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "{currency}銀行へようこそ!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "ログイン してください!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "ユーザー名とパスワードが一致していません."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr "あなたのアカウントにはこのページへのアクセス権がありません。先に進むには、アクセスできるアカウントでログインしてください。"
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "このページを表示するにはログインしてください。"
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"あなたが新しい顧客である場合は、<a href=\"{register_link}\">登録してください</a>。"
"登録は迅速かつ無料で,それはあなたに100 {currency}の登録ボーナスを与えます"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "登録は一般に公開されていません。"
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr "パブリックアカウントの取引を表示するには、<a href=\"{public_accounts}\">ここをクリックしてください</a>。"
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "ログアウト"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "電信送金"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Paytoシステムを介して送金します。"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "ペイトアドレス"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "確認"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "ようこそ<em>{name}</em> !"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "銀行口座残高"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "ターラーウォレットにお金を引き出す"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "引き出し額"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "引き出しを開始する"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "ワイヤーマネー"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "のトランザクション"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "日付"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "合計"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "カウンターパート"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "題名"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "キャンセル"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "このアカウントとの間で行われたトランザクションはありません"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "公会計の歴史"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "アカウント"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "アカウント#{account_number}{account_name})の履歴はまだありません"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "{currency}銀行に登録してください!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "戻る"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr "申し訳ありませんが、このユーザー名はご利用いただけなくなりました!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "引き出しを確認する"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"{currency}銀行は、 <b>{exchange}</b><b>から{amount}</b>を引き出すつもりであることを確認する必要があります。"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "{question}とは何ですか?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"本物の銀行は、単純な計算ではなく、PIN/"
"TANを要求するべきです。例えば、顧客の携帯電話にワンタイムパスワードを送信したり、ランダムパスワードジェネレータを提供したりします。"
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "ターラーウォレットに撤退する"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"あなたのブラウザはGNU Talerの支払いをサポートしていないようです。<a href=\"{wallet_link}\""
">ウォレットブラウザ拡張機能</a>をインストールしてみてください。"
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "このQRコードを使用して、モバイルウォレットに引き出すことができます。"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"<a href=\"{taler_withdraw_uri}\">このリンク</a>をクリックして、システムにTalerウォレットがあれば開きます。"
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "不正なフォームが送信されました!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "電信送金に成功しました!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "登録に成功しました!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "間違ったCAPTCHAの答え。"
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "撤退成功!"

View File

@ -0,0 +1,292 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-03-20 15:15+0000\n"
"Last-Translator: Choyi Whang <choyi.whang@gmail.com>\n"
"Language-Team: Korean <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/ko/>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "오류"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "페이지를 찾을 수 없습니다!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} 은행 - 탈러 데모"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"데모의 이 부분은 탈러를 직접 지원하는 은행이 어떻게 작동하는지 보여줍니다. 당신의 은행계좌를 사용하는 것에 더하여, 몇 개의 <a "
"href=\"{public_accounts}\">공개 계정</a>의 거래 내역도 볼 수 있습니다."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "소개"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "은행"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "에세이 샵"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "기부"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "팁 / 설문조사"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr "Taler에 대한 자세한 내용은 메인 <a href={taler_site}>웹 사이트</a> 에서 확인할 수 있습니다."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "{currency} 은행에 오신 것을 환영합니다!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "로그인 하세요!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "사용자명과 비밀번호가 일치하지 않습니다. 다시 시도하세요."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr "귀하의 계정은 이 페이지에 접근할 수 없습니다. 계속하기 위해, 접근 가능한 계정으로 로그인 해주세요."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "이 페이지를 보려면 로그인하십시오."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"신규 고객인 경우 <a href=\"{register_link}\">등록</a>하십시오. 등록은 빠르고 무료이며, 등록 보너스 100 "
"{currency}를 받을 수 있습니다"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "등록은 일반인에게 공개되지 않습니다."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr "공개 계정의 거래를 보려면 <a href=\"{public_accounts}\">여기</a> 를 클릭하십시오."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "로그아웃"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "송금"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Payto 시스템을 통한 송금:"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "payto 주소"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "확인"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "<em>{name} 님,</em> 환영합니다!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "은행 계좌 잔액"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Taler 지갑으로 돈 인출"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "인출할 금액"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "출금 시작"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "송금"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "거래"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "날짜"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "금액"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "상대"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "제목"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "취소됨"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "이 계정과의 거래 없음"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "공개 계정 내역"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "계정"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "계좌 # {account_number} {account_name} 에 대한 관련 기록이 없습니다"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "{currency} 은행에 등록하세요!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "뒤로"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr "죄송합니다, 해당 사용자 이름은 사용하실 수 없습니다. 다른 이름을 입력하세요!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "출금 확인"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"{currency} 은행은 본인의 의사로 <b>{exchange}</b>에서 <b>{amount}</b> 의 금액을 인출하려하는지 "
"확인해야 합니다. 계좌 주인 당사자인지 확인을 위해 &quot;보안 질문&quot; 에 답해주시길 바랍니다. (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "{question}이 무엇입니까?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"실제 은행에서는 간단한 계산을 하는 대신 PIN/TAN 번호를 요구합니다. 예를 들어 고객의 핸드폰으로 일회성 비밀번호를 보내거나 랜덤 "
"비밀번호를 발급받을 수 있게 신원 확인을 합니다."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Taler 지갑으로 출금"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"브라우저가 GNU Taler 결제를 지원하지 않는 것 같습니다. <a href=\"{wallet_link}\">지갑 브라우저 확장자</"
"a>로 시도해보세요."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "이 QR 코드를 사용하여 모바일 지갑으로 인출할 수 있습니다:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"<a href=\"{taler_withdraw_uri}\">이 링크</a> 를 클릭하면 시스템의 Taler 지갑이 존재할 시 열립니다."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "잘못된 양식이 제출되었습니다!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "성공적으로 이체!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "성공적으로 등록!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "CAPTCHA 답변이 틀렸습니다."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "인출 성공!"

View File

@ -0,0 +1,313 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-02-27 20:12+0000\n"
"Last-Translator: Salomé Mariano <salomemariano@gmail.com>\n"
"Language-Team: Portuguese <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/pt/>\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Erro"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Página não encontrada!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Banco - Demonstração da Taler"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Esta parte da demonstração mostra como funcionaria um banco que suporta "
"diretamente o Taler. Além de usar a sua própria conta bancária, você também "
"pode ver o histórico de transações de algumas <a href=\"{public_accounts}\""
">Contas Públicas</a>."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Introdução"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Banco"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Loja de Ensaios"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Doações"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Gorjeta/Inquérito"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Podes aprender mais sobre a Taler no nosso <a href={taler_site}>site</a> "
"principal."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Boas vindas ao Banco {currency}!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Por favor inicie sessão!"
#: talerbank/app/templates/login.html:33
#, fuzzy
msgid "Your username and password didn't match. Please try again."
msgstr ""
"O seu nome de usuário e senha não corresponderm. Por favor tente novamente."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"A sua conta não tem acesso a esta página. Para avançar, por favor inicie "
"sessão com uma conta que tenha acesso."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Por favor inicie sessão para ver esta página."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Se for um cliente novo, por favor <a href=\"{register_link}\">crie uma "
"conta</a>. É rápido e gratuito e você recebe um bónus de 100 {currency}"
#: talerbank/app/templates/login.html:63
#, fuzzy
msgid "Registrations are not open to the public."
msgstr "A criação de contas não está aberta ao público."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Para ver as transações de contas públicas, por favor <a href=\""
"{public_accounts}\">clique aqui</a>."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Terminar sessão"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Transferência bancária"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Transferir dinheiro através do sistema payto:"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "endereço payto"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Confirmar"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Boas vindas <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Saldo da conta bancária"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Sacar Dinheiro para uma carteira Taler"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Quantia a sacar"
#: talerbank/app/templates/profile_page.html:73
#, fuzzy
msgid "Start withdrawal"
msgstr "Iniciar saque"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Transferir dinheiro"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Transações para"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Data"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Quantia"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
#, fuzzy
msgid "Counterpart"
msgstr "Homólogo"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Assunto"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "cancelada"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Não foram feitas transações a partir/para esta conta"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Histórico de contas públicas"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "conta"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "A conta #{account_number} ({account_name}) ainda não tem um histórico"
#: talerbank/app/templates/register.html:22
#, fuzzy, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Criar uma conta com o banco {currency}!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Voltar"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Pedimos desculpa, mas este nome de usuário já não está disponível. Por favor "
"escolha outro!"
#: talerbank/app/templates/withdraw_confirm.html:23
#, fuzzy
msgid "Confirm Withdrawal"
msgstr "Confirmar Saque"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"O Banco {currency} precisa de confirmar que pretendes sacar <b>{amount}</b> "
"de <b>{exchange}</b>. Para provar que esta conta é sua, por favor responda a "
"esta &quot;perqunta de segurança&quot; (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, fuzzy, python-brace-format
msgid "What is {question}?"
msgstr "O que é {question}?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"Um banco real deve pedir um PIN/TAN em vez de um cálculo simples, por "
"exemplo enviando uma palavra passe de uso único para o celular da cliente ou "
"disponibilizando um gerador de senhas aleatórias."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Sacar para uma Carteira Taler"
#: talerbank/app/templates/withdraw_show.html:66
#, fuzzy, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Parece que o seu navegador não suporta pagamentos usando a GNU Taler. Pode "
"tentar instalar uma <a href=\"{wallet_link}\">extensão para navegador</a>."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "Pode usar este código QR para sacar dinheiro para a sua carteira móvel:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Clique <a href=\"{taler_withdraw_uri}\">neste link</a> para abrir a carteira "
"Taler do seu sistema, se a tiver."
#: talerbank/app/views.py:353
#, fuzzy
msgid "Bad form submitted!"
msgstr "Formulário incorreto submetido!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Transferência bancária bem sucedida!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Conta criada com sucesso!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "Resposta CAPTCHA incorreta."
#: talerbank/app/views.py:1223
#, fuzzy
msgid "Withdrawal successful!"
msgstr "Saque bem sucedido!"

View File

@ -0,0 +1,310 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-05-20 00:51+0000\n"
"Last-Translator: Yulia Greben <yulia.greben@gmail.com>\n"
"Language-Team: Russian <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/ru/>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Ошибка"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Страница не найдена!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Банк - демоверсия Taler"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"В этой части демоверсии продемонстрирована работа банка, напрямую "
"поддерживающего Taler. Вы можете не только использовать ваш банковский счёт, "
"а также видеть историю денежных операций по некоторым <a href=\""
"{public_accounts}\">доступным для публичного просмотра счетам</a>."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Введение"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Банк"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Демо-магазин"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Поддержать проект"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Опрос"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Подробнее о Taler можно прочитать на нашем <a href={taler_site}>сайте</a>."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Добро пожаловать в {currency} Банк!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Пожалуйста, войдите в систему!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Ваши имя пользователя и пароль не совпадают. Пожалуйста, попробуйте ввести "
"имя пользователя и пароль ещё раз."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"У вашей учётной записи нет доступа к данной странице. Чтобы продолжить, "
"войдите в систему с помощью учётной записи, имеющей доступ к данной странице."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Для просмотра данной страницы, пожалуйста, войдите в систему."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Если вы новый пользователь, пожалуйста, зарегистрируйтесь <a href=\""
"{register_link}\">здесь</a>. Регистрация бесплатная и не займёт много "
"времени. Вы также получите бонус в размере 100 {currency}"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "Закрытая для публики регистрация."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"Информацию о денежных операциях по счетам, доступным для публичного "
"просмотра, можно увидеть <a href=\"{public_accounts}\">здесь</a>."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Выйти"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Перевод"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Перевести деньги через Payto:"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "адрес Payto"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Подтвердить"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Добро пожаловать <em>{name}</em>!"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Баланс банковского счёта"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "Вывести деньги в кошелёк Taler"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Сумма для вывода средств"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Начать вывод средств"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Перевести деньги"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Транзакции для"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Дата"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Сумма"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Получатель"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Описание"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "отменена"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Нет транзакций"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "История счетов, доступных для публичного просмотра"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "счёт"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "Нет данных об истории счёта #{account_number} ({account_name})"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Зарегистрируйтесь в {currency} Банке!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Назад"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Извините, это имя пользователя уже занято. Пожалуйста, выберите другое!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Подтвердить вывод средств"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"{currency} Банк должен проверить, что именно вы хотите вывести <b>{amount}</"
"b> с <b>{exchange}</b>. Пожалуйста, ответьте на &quot;приведённый ниже "
"вопрос&quot; для подтверждения того, что именно вы являетесь владельцем "
"данной учётной записи (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Сколько будет {question}?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"В реальной, а не тестовой ситуации, банк должен запросить у клиента PIN/TAN-"
"коды вместо решения элементарного вычислительного примера. Например, клиент "
"получит на свой мобильный телефон одноразовый пароль или генератор случайных "
"паролей."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Вывести средства в Taler Кошелёк"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Ваш браузер не поддерживает платежи GNU Taler. Попробуйте установить <a href="
"\"{wallet_link}\">расширение браузера для кошелька</a>."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr ""
"Вы может использовать QR-код для вывода средств на ваш мобильный кошелёк:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Если у вас установлен кошелёк Taler, вы можете открыть его, щёлкнув «мышью» "
"на <a href=\"{taler_withdraw_uri}\">ссылке</a>."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "Ошибки при заполнении формы!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Перевод успешно завершён!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Регистрация прошла успешно!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "Неправильно введена CAPTCHA."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Вывод средств успешно завершён!"

View File

@ -0,0 +1,305 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-20 13:09+0000\n"
"PO-Revision-Date: 2021-02-02 19:26+0000\n"
"Last-Translator: Stefan Lidstrom <stefan.lidstrom@tanker.se>\n"
"Language-Team: Swedish <http://weblate.taler.net/projects/gnu-taler/"
"taler-bank/sv/>\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.4.2\n"
#: talerbank/app/templates/404.html:23
msgid "Error"
msgstr "Fel"
#: talerbank/app/templates/404.html:30
msgid "Page not found!"
msgstr "Sidan hittades inte!"
#: talerbank/app/templates/base.html:23
#, python-brace-format
msgid "{currency} Bank - Taler Demo"
msgstr "{currency} Bank - Taler Demo"
#: talerbank/app/templates/base.html:74
#, python-brace-format
msgid ""
"This part of the demo shows how a bank that supports Taler directly would "
"work. In addition to using your own bank account, you can also see the "
"transaction history of some <a href=\"{public_accounts}\">Public Accounts</"
"a>."
msgstr ""
"Denna del av demon visar hur en bank som stöder Taler direkt skulle fungera. "
"Förutom att använda ditt eget bankkonto kan du också se "
"transaktionshistoriken för vissa <a href=\"{public_accounts}\"> offentliga "
"konton </a>."
#: talerbank/app/templates/base.html:80
msgid "Introduction"
msgstr "Inledning"
#: talerbank/app/templates/base.html:81
msgid "Bank"
msgstr "Bank"
#: talerbank/app/templates/base.html:82
msgid "Essay Shop"
msgstr "Artiklar att köpa"
#: talerbank/app/templates/base.html:83
msgid "Donations"
msgstr "Donationer"
#: talerbank/app/templates/base.html:84
msgid "Tipping/Survey"
msgstr "Enkät/Belöning"
#: talerbank/app/templates/base.html:94
#, python-brace-format
msgid ""
"You can learn more about Taler on our main <a href={taler_site}>website</a>."
msgstr ""
"Du kan lära dig mer om GNU Taler på vår <a href=\"{taler_site}\">webb</a> ."
#: talerbank/app/templates/login.html:22
#, python-brace-format
msgid "Welcome to the {currency} Bank!"
msgstr "Välkommen till {currency} Banken!"
#: talerbank/app/templates/login.html:29
msgid "Please login!"
msgstr "Vänligen logga in!"
#: talerbank/app/templates/login.html:33
msgid "Your username and password didn't match. Please try again."
msgstr "Ditt användarnamn och lösenord matchade inte. Vänligen försök igen."
#: talerbank/app/templates/login.html:40
msgid ""
"Your account doesn't have access to this page. To proceed, please login with "
"an account that has access."
msgstr ""
"Ditt konto har inte åtkomst till den här sidan. Logga in med ett konto som "
"har åtkomst för att fortsätta."
#: talerbank/app/templates/login.html:43
msgid "Please login to see this page."
msgstr "Vänligen logga in för att se denna sida."
#: talerbank/app/templates/login.html:59
#, python-brace-format
msgid ""
"If you are a new customer please <a href=\"{register_link}\">register</a>. "
"Registration is fast and free, and it gives you a registration bonus of 100 "
"{currency}"
msgstr ""
"Om du är ny kund vänligen <a href=\"{register_link}\"> registrera dig </a>. "
"Registreringen är snabb och gratis, och det ger dig en registreringsbonus på "
"100 {currency}"
#: talerbank/app/templates/login.html:63
msgid "Registrations are not open to the public."
msgstr "Registreringar är inte öppna för allmänheten."
#: talerbank/app/templates/login.html:67
#, python-brace-format
msgid ""
"To view transactions of public accounts, please <a href="
"\"{public_accounts}\">click here</a>."
msgstr ""
"<a href=\"{public_accounts}\">Klicka här för</a> att se transaktioner med "
"offentliga konton."
#: talerbank/app/templates/payto_wiretransfer.html:23
#: talerbank/app/templates/profile_page.html:28
msgid "Logout"
msgstr "Logga ut"
#: talerbank/app/templates/payto_wiretransfer.html:30
msgid "Wire transfer"
msgstr "Banköverföring"
#: talerbank/app/templates/payto_wiretransfer.html:31
msgid "Transfer money via the payto system:"
msgstr "Överför pengar via betalningssystemet:"
#: talerbank/app/templates/payto_wiretransfer.html:41
msgid "payto address"
msgstr "betalningsadress"
#: talerbank/app/templates/payto_wiretransfer.html:45
msgid "Confirm"
msgstr "Bekräfta"
#: talerbank/app/templates/profile_page.html:25
#, python-brace-format
msgid "Welcome <em>{name}</em>!"
msgstr "Välkommen <em>{name}</em> !"
#: talerbank/app/templates/profile_page.html:33
msgid "Bank account balance"
msgstr "Saldo på bankkontot"
#: talerbank/app/templates/profile_page.html:51
msgid "Withdraw Money into a Taler wallet"
msgstr "För över pengar till en Taler-plånbok"
#: talerbank/app/templates/profile_page.html:58
msgid "Amount to withdraw"
msgstr "Summa att överföra"
#: talerbank/app/templates/profile_page.html:73
msgid "Start withdrawal"
msgstr "Starta uttag"
#: talerbank/app/templates/profile_page.html:78
msgid "Wire money"
msgstr "Överföra pengar"
#: talerbank/app/templates/profile_page.html:81
msgid "Transactions for"
msgstr "Transaktioner för"
#: talerbank/app/templates/profile_page.html:87
#: talerbank/app/templates/public_accounts.html:59
msgid "Date"
msgstr "Datum"
#: talerbank/app/templates/profile_page.html:88
#: talerbank/app/templates/public_accounts.html:60
msgid "Amount"
msgstr "Belopp"
#: talerbank/app/templates/profile_page.html:89
#: talerbank/app/templates/public_accounts.html:61
msgid "Counterpart"
msgstr "Motpart"
#: talerbank/app/templates/profile_page.html:90
#: talerbank/app/templates/public_accounts.html:62
msgid "Subject"
msgstr "Ämne"
#: talerbank/app/templates/profile_page.html:107
msgid "cancelled"
msgstr "avbruten"
#: talerbank/app/templates/profile_page.html:116
msgid "No transactions made to/from this account"
msgstr "Inga transaktioner gjorda till/från detta konto"
#: talerbank/app/templates/public_accounts.html:21
msgid "History of public accounts"
msgstr "Historik över offentliga konton"
#: talerbank/app/templates/public_accounts.html:71
msgid "account"
msgstr "konto"
#: talerbank/app/templates/public_accounts.html:101
#, python-brace-format
msgid "No history for account #{account_number} ({account_name}) yet"
msgstr "Ingen historik för konto #{account_number} ({account_name}) ännu"
#: talerbank/app/templates/register.html:22
#, python-brace-format
msgid "Register to the {currency} bank!"
msgstr "Registrera dig i {currency} banken!"
#: talerbank/app/templates/register.html:30
msgid "Back"
msgstr "Tillbaka"
#: talerbank/app/templates/register.html:39
msgid ""
"Sorry, this username is no longer available. Please choose another one!"
msgstr ""
"Tyvärr, det här användarnamnet är inte längre tillgänglig. Vänligen väl ett "
"annat!"
#: talerbank/app/templates/withdraw_confirm.html:23
msgid "Confirm Withdrawal"
msgstr "Bekräfta uttag"
#: talerbank/app/templates/withdraw_confirm.html:34
#, python-brace-format
msgid ""
"{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> "
"from <b>{exchange}</b>. To prove that you are the account owner, please "
"answer the following &quot;security question&quot; (*):"
msgstr ""
"{currency} Banken måste verifiera att du tänker ta ut <b> {amount} </b> från "
"<b> {exchange} </b>. För att bevisa att du är kontoägare, svara på följande "
"&quot;säkerhetsfråga&quot; (*):"
#: talerbank/app/templates/withdraw_confirm.html:38
#, python-brace-format
msgid "What is {question}?"
msgstr "Vad är {question}?"
#: talerbank/app/templates/withdraw_confirm.html:64
msgid ""
"A real bank should ask for a PIN/TAN instead of a simple calculation. For "
"example by sending a one time password to the customer's mobile or providing "
"her a random password generator."
msgstr ""
"En riktig bank bör be om ett lösenord/kod istället för en enkel beräkning. "
"Till exempel genom att skicka ett engångslösenord till kundens mobil eller "
"ge hen ett slumpmässigt lösenord."
#: talerbank/app/templates/withdraw_show.html:22
msgid "Withdraw to a Taler Wallet"
msgstr "Gör uttag till en Taler plånbok"
#: talerbank/app/templates/withdraw_show.html:66
#, python-brace-format
msgid ""
"Looks like your browser doesn't support GNU Taler payments. You can try "
"installing a <a href=\"{wallet_link}\">wallet browser extension</a>."
msgstr ""
"Det verkar som om din webbläsare inte stöder GNU Taler-betalningar. Du kan "
"försöka installera ett <a href=\"{wallet_link}\">tillägg</a>."
#: talerbank/app/templates/withdraw_show.html:72
msgid "You can use this QR code to withdraw to your mobile wallet:"
msgstr "Du kan använda den här QR-koden för att överföra till din mobilplånbok:"
#: talerbank/app/templates/withdraw_show.html:77
#, python-brace-format
msgid ""
"Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's "
"Taler wallet if it exists."
msgstr ""
"Klicka på den <a href=\"{taler_withdraw_uri}\">här länken för</a> att öppna "
"systemets Taler-plånbok om den finns."
#: talerbank/app/views.py:353
msgid "Bad form submitted!"
msgstr "Felaktigt formulär inskickat!"
#: talerbank/app/views.py:367
msgid "Wire transfer successful!"
msgstr "Banköverföring lyckades!"
#: talerbank/app/views.py:520
msgid "Registration successful!"
msgstr "Registreringen lyckades!"
#: talerbank/app/views.py:1212
msgid "Wrong CAPTCHA answer."
msgstr "Fel CAPTCHA-svar."
#: talerbank/app/views.py:1223
msgid "Withdrawal successful!"
msgstr "Uttag lyckades!"

View File

View File

@ -0,0 +1,70 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2106 Taler Systems SA
#
# TALER 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,
# 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 Marcello Stanisci
# @author Florian Dold
# @brief Create the basic accounts to make the demo bank work.
import sys
import logging
from django.contrib.auth.models import User
from django.db.utils import ProgrammingError, OperationalError
from django.core.management.base import BaseCommand
from django.conf import settings
from ...models import BankAccount
from ...views import wire_transfer
from taler.util.amount import Amount
import getpass
import uuid
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
class Command(BaseCommand):
help = "Add bank accounts."
def add_arguments(self, parser):
parser.add_argument(
"accountname", type=str, help="Login name of the new bank account"
)
parser.add_argument(
"--public", action="store_true", help="Make the bank account public"
)
##
# Django-specific definition to invoke the account creator
# @a make_account; it iterates over the list of basic accounts
# (defined in the settings) and invoke the account creator
# for each one of them.
def handle(self, *args, **options):
accountname = options["accountname"]
try:
existing_user = User.objects.get(username=accountname)
except User.DoesNotExist:
BankAccount(
user=User.objects.create_user(
username=accountname, password=str(uuid.uuid4())
),
is_public=False,
).save()
print(
f"Created new bank account {accountname} (password set to random password)"
)
else:
print(f"Bank account {accountname} already exists.")

View File

@ -0,0 +1,66 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2106 Taler Systems SA
#
# TALER 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,
# 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 Marcello Stanisci
# @author Florian Dold
# @brief Create the basic accounts to make the demo bank work.
import sys
import logging
from django.contrib.auth.models import User
from django.db.utils import ProgrammingError, OperationalError
from django.core.management.base import BaseCommand
from django.conf import settings
from ...models import BankAccount
from ...views import wire_transfer
from taler.util.amount import Amount
import getpass
import uuid
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
class Command(BaseCommand):
help = "Add bank accounts."
def add_arguments(self, parser):
parser.add_argument(
"accountname", type=str, help="Login name of the new bank account"
)
parser.add_argument(
"password", type=str, help="New plain text password of the bank account"
)
##
# Django-specific definition to invoke the account creator
# @a make_account; it iterates over the list of basic accounts
# (defined in the settings) and invoke the account creator
# for each one of them.
def handle(self, *args, **options):
accountname = options["accountname"]
password = options["password"]
try:
existing_user = User.objects.get(username=accountname)
existing_user.set_password(password)
existing_user.save()
except User.DoesNotExist:
print(f"Account {accountname} does not exist")
sys.exit(1)
else:
print(f"Password for {accountname} changed")

View File

@ -0,0 +1,52 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2106 Taler Systems SA
#
# TALER 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,
# 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 Marcello Stanisci
# @author Florian Dold
# @brief Create the basic accounts to make the demo bank work.
import sys
import logging
from django.contrib.auth.models import User
from django.db.utils import ProgrammingError, OperationalError
from django.core.management.base import BaseCommand
from django.conf import settings
from ...models import BankAccount
from ...views import wire_transfer
from taler.util.amount import Amount
import getpass
import uuid
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
class Command(BaseCommand):
help = "List bank accounts"
##
# Django-specific definition to invoke the account creator
# @a make_account; it iterates over the list of basic accounts
# (defined in the settings) and invoke the account creator
# for each one of them.
def handle(self, *args, **options):
accounts = BankAccount.objects.all().order_by("account_no")
for account in accounts:
print(
f"Account {repr(account.user.username)} balance {account.balance.stringify()}"
)

View File

@ -0,0 +1,77 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2106 Taler Systems SA
#
# TALER 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,
# 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 Marcello Stanisci
# @author Florian Dold
# @brief Create the basic accounts to make the demo bank work.
import sys
import logging
from django.contrib.auth.models import User
from django.db.utils import ProgrammingError, OperationalError
from django.core.management.base import BaseCommand
from django.conf import settings
from ...models import BankAccount
from ...views import wire_transfer
from taler.util.amount import Amount
import uuid
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
##
# Create a new bank account.
#
# @param username the username to associate to this account.
def make_account(username):
try:
User.objects.get(username=username)
except User.DoesNotExist:
LOGGER.info("Creating account for '%s'", username)
BankAccount(
user=User.objects.create_user(
username=username, password=str(uuid.uuid4())
),
is_public=True,
).save()
except (OperationalError, ProgrammingError):
LOGGER.error(
"db does not exist, or the project"
" is not migrated. Try 'taler-bank-manage"
" django migrate' in the latter case.",
stack_info=False,
exc_info=True,
)
sys.exit(1)
##
# Django-specific definition to register this command.
class Command(BaseCommand):
help = "Provide initial user accounts"
##
# Django-specific definition to invoke the account creator
# @a make_account; it iterates over the list of basic accounts
# (defined in the settings) and invoke the account creator
# for each one of them.
def handle(self, *args, **options):
for username in settings.TALER_PREDEFINED_ACCOUNTS:
make_account(username)

View File

@ -0,0 +1,83 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2106 Taler Systems SA
#
# TALER 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,
# 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 Marcello Stanisci
# @author Florian Dold
# @brief Create the basic accounts to make the demo bank work.
import sys
import logging
from django.contrib.auth.models import User
from django.db.utils import ProgrammingError, OperationalError
from django.core.management.base import BaseCommand
from django.conf import settings
from ...models import BankAccount
from ...views import wire_transfer
from taler.util.amount import Amount
import getpass
import uuid
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
class Command(BaseCommand):
help = "Add money to a bank account (and debit the bank's account)."
def add_arguments(self, parser):
parser.add_argument(
"user",
type=str,
metavar="USERNAME",
help="User that is getting credited with the top-up",
)
parser.add_argument(
"amount",
type=str,
metavar="AMOUNT",
help="Wire transfer's amount, given in the " "CURRENCY:X.Y form.",
)
##
# Django-specific definition to invoke the account creator
# @a make_account; it iterates over the list of basic accounts
# (defined in the settings) and invoke the account creator
# for each one of them.
def handle(self, *args, **options):
user = User.objects.get(username=options["user"])
# Take the money from the bank's account
try:
debit_account = BankAccount.objects.get(
account_no=1,
)
except BankAccount.DoesNotExist:
LOGGER.error("Debit account (bank's own account) does not exist.")
sys.exit(1)
try:
amount = Amount.parse(options["amount"])
except BadFormatAmount:
LOGGER.error("Amount's format is wrong: respect C:X.Y.")
sys.exit(1)
try:
transaction = wire_transfer(
amount, debit_account, user.bankaccount, "manual top-up by admin"
)
print("Transaction id: " + str(transaction.id))
except Exception as exc:
LOGGER.error(exc)
sys.exit(1)

View File

@ -0,0 +1,106 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2016 Taler Systems SA
#
# TALER 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, 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 Marcello Stanisci
# @brief CLI utility that issues a wire transfer.
import sys
import logging
import json
from django.core.management.base import BaseCommand
from django.contrib.auth import authenticate
from taler.util.amount import Amount
from ...views import wire_transfer, User
from ...models import BankAccount, BankTransaction
LOGGER = logging.getLogger(__name__)
##
# Django-specific definition to register the CLI utility.
class Command(BaseCommand):
help = "Wire transfer money and return the transaction id."
##
# Register the command line options of this command.
#
# @param self this object.
# @param parser API used to actually register the option.
def add_arguments(self, parser):
parser.add_argument(
"user",
type=str,
metavar="USERNAME",
help="Which user is performing the wire transfer",
)
parser.add_argument(
"password", type=str, metavar="PASSWORD", help="Performing user's password."
)
parser.add_argument(
"credit-account",
type=str,
metavar="CREDIT-ACCOUNT",
help="Which account will receive money.",
)
parser.add_argument(
"subject",
type=str,
metavar="SUBJECT",
help="SUBJECT will be the wire transfer subject.",
)
parser.add_argument(
"amount",
type=str,
metavar="AMOUNT",
help="Wire transfer's amount, given in the " "CURRENCY:X.Y form.",
)
##
# This callable gets invoked when the user invokes the
# CLI utility; it is responsible of making the wire transfer
# effective.
#
# @param self this object.
# @param args arguments list -- currently unused.
# @param options options given by the user at the command line.
def handle(self, *args, **options):
user = authenticate(username=options["user"], password=options["password"])
if not user:
LOGGER.error("Wrong user/password.")
sys.exit(1)
try:
amount = Amount.parse(options["amount"])
except BadFormatAmount:
LOGGER.error("Amount's format is wrong: respect C:X.Y.")
sys.exit(1)
try:
credit_account_user = User.objects.get(username=options["credit-account"])
credit_account = credit_account_user.bankaccount
except BankAccount.DoesNotExist:
LOGGER.error("Credit account does not exist.")
sys.exit(1)
try:
transaction = wire_transfer(
amount, user.bankaccount, credit_account, options["subject"]
)
print("Transaction id: " + str(transaction.id))
except Exception as exc:
LOGGER.error(exc)
sys.exit(1)

View File

@ -0,0 +1,106 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2016 Taler Systems SA
#
# TALER 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, 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 Marcello Stanisci
# @brief CLI utility to make wire transfers.
import sys
import logging
import json
from django.core.management.base import BaseCommand
from django.contrib.auth import authenticate
from taler.util.amount import Amount, AmountFormatError
from taler.util.payto import PaytoParse, PaytoFormatError
from ...views import wire_transfer, User
from ...models import BankAccount, BankTransaction
LOGGER = logging.getLogger(__name__)
##
# Django-specific definition to register the CLI utility.
class Command(BaseCommand):
help = "Wire transfer money and return the transaction id."
##
# Register the command line options of this command.
#
# @param self this object.
# @param parser API used to actually register the option.
def add_arguments(self, parser):
parser.add_argument(
"user",
type=str,
metavar="USERNAME",
help="Which user is performing the wire transfer",
)
parser.add_argument(
"password", type=str, metavar="PASSWORD", help="Performing user's password."
)
parser.add_argument(
"payto-credit-account-with-subject",
type=str,
metavar="PAYTO-CREDIT-ACCOUNT-WITH-SUBJECT",
help="Which account will receive money, in the form 'payto://x-taler-bank/[bank-host/]CreditAccountName?subject=PaymentSubject'.",
)
parser.add_argument(
"amount",
type=str,
metavar="AMOUNT",
help="Wire transfer's amount, given in the " "CURRENCY:X.Y form.",
)
##
# This callable gets invoked when the user invokes the
# CLI utility; it is responsible of making the wire transfer
# effective.
#
# @param self this object.
# @param args arguments list -- currently unused.
# @param options options given by the user at the command line.
def handle(self, *args, **options):
user = authenticate(username=options["user"], password=options["password"])
if not user:
LOGGER.error("Wrong user/password.")
sys.exit(1)
try:
amount = Amount.parse(options["amount"])
except AmountFormatError:
print("Amount format is wrong: respect C:X.Y.")
sys.exit(1)
try:
parsed_payto = PaytoParse(options["payto-credit-account-with-subject"])
credit_account_user = User.objects.get(username=parsed_payto.target)
credit_account = credit_account_user.bankaccount
except User.DoesNotExist:
print("Credit account does not exist.")
sys.exit(1)
except PaytoFormatError as e:
print(e)
sys.exit(1)
if not parsed_payto.message:
print("Please provide 'message' parameter along the payto URI")
sys.exit(1)
try:
transaction = wire_transfer(
amount, user.bankaccount, credit_account, parsed_payto.message
)
print("Transaction id: " + str(transaction.id))
except Exception as exc:
LOGGER.error(exc)
sys.exit(1)

121
talerbank/app/middleware.py Normal file
View File

@ -0,0 +1,121 @@
import traceback
import logging
import zlib
from . import urls
from django.http import JsonResponse
from django.urls import reverse
from django.shortcuts import redirect
from .models import BankAccount, BankTransaction
from .views import (
DebitLimitException,
SameAccountException,
LoginFailed,
UnhandledException,
set_session_hint,
)
from .schemas import JSONFieldException, URLParamValidationError, InvalidSession
from taler.util.amount import CurrencyMismatchError, AmountFormatError
from taler.util.taler_error_codes import ErrorCode
from http import HTTPStatus
LOGGER = logging.getLogger()
##
# Class decompressing requests.
class DecompressionMiddleware:
##
# Init constructor.
#
# @param self the object itself.
# @param get_response a Django-provided callable that calls
# whatever comes next in the chain: a further middleware
# or the view itself (please refer to the official
# documentation for more details).
def __init__(self, get_response):
self.get_response = get_response
##
# This function is transparently invoked by Django when
# a request traverses the chain made of middleware classes
# and the view itself as the last element in the chain.
#
# Here happens the decompression.
#
# @param self this class.
# @param request Django-specific request object (of the same
# type that is handed to views).
# @return Django-specific response object.
def __call__(self, request):
if "deflate" == request.META.get("HTTP_CONTENT_ENCODING"):
request._body = zlib.decompress(request.body)
return self.get_response(request)
class ExceptionMiddleware:
"""
Middleware for handling exceptions not caught directly
by the application logic.
"""
def __init__(self, get_response):
"""
# Init constructor.
#
# @param self the object itself.
# @param get_response a Django-provided callable that calls
# whatever comes next in the chain: a further middleware
# or the view itself (please refer to the official
# documentation for more details).
"""
self.get_response = get_response
# Map between endpoints and Web pages to render
# after the exception gets managed.
self.render = {
reverse("profile", urlconf=urls): "profile",
reverse("register", urlconf=urls): "index",
reverse("public-accounts", urlconf=urls): "index",
}
def __call__(self, request):
"""
This function is transparently invoked by Django when
a request traverses the chain made of middleware classes
and the view itself as the last element in the chain.
"""
return self.get_response(request)
def process_exception(self, request, exception):
"""
Main logic for processing the exception. It checks
if the exception captured can be managed, and does it
if so. Otherwise, it lets the native handler operate.
"""
LOGGER.error(f"Error: {exception}, while serving {request.get_full_path()}")
if hasattr(exception, "taler_error_code"):
render_to = self.render.get(request.path)
if not render_to:
response = JsonResponse(
dict(code=exception.taler_error_code.value, error=exception.hint),
status=exception.http_status_code,
)
response["Access-Control-Allow-Origin"] = "*"
return response
set_session_hint(request, success=False, hint=exception.hint)
return redirect(render_to)
else:
LOGGER.error(f"Stack trace: {traceback.format_exc()}")
return JsonResponse(
dict(
code=ErrorCode.BANK_UNMANAGED_EXCEPTION,
hint="unexpected exception",
exception=str(exception),
),
status=HTTPStatus.INTERNAL_SERVER_ERROR,
)

View File

@ -0,0 +1,113 @@
# Generated by Django 3.0.2 on 2020-01-21 15:47
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import talerbank.app.models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="BankAccount",
fields=[
("is_public", models.BooleanField(default=False)),
("account_no", models.AutoField(primary_key=True, serialize=False)),
(
"balance",
talerbank.app.models.SignedAmountField(
default=talerbank.app.models.get_zero_signed_amount
),
),
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name="TalerWithdrawOperation",
fields=[
(
"withdraw_id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("amount", talerbank.app.models.AmountField(default=False)),
("selection_done", models.BooleanField(default=False)),
("confirmation_done", models.BooleanField(default=False)),
("aborted", models.BooleanField(default=False)),
("selected_reserve_pub", models.TextField(null=True)),
(
"selected_exchange_account",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="selected_exchange_account",
to="app.BankAccount",
),
),
(
"withdraw_account",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="withdraw_account",
to="app.BankAccount",
),
),
],
),
migrations.CreateModel(
name="BankTransaction",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("amount", talerbank.app.models.AmountField(default=False)),
(
"subject",
models.CharField(default="(no subject given)", max_length=200),
),
("date", models.DateTimeField(auto_now=True, db_index=True)),
("cancelled", models.BooleanField(default=False)),
("request_uid", models.CharField(max_length=128, unique=True)),
(
"credit_account",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="credit_account",
to="app.BankAccount",
),
),
(
"debit_account",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="debit_account",
to="app.BankAccount",
),
),
],
),
]

View File

216
talerbank/app/models.py Normal file
View File

@ -0,0 +1,216 @@
##
# This file is part of TALER
# (C) 2014, 2015, 2016 Taler Systems SA
#
# 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 Marcello Stanisci
# @author Florian Dold
import uuid
from typing import Any, Tuple
from django.contrib.auth.models import User
from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from taler.util.amount import Amount, SignedAmount, CurrencyMismatchError
def get_zero_amount() -> Amount:
"""
Helper function that instantiates a zero-valued Amount
object in the currency that the bank runs on.
"""
return Amount(settings.TALER_CURRENCY, 0, 0)
def get_zero_signed_amount() -> SignedAmount:
"""
Helper function that instantiates a zero-valued SignedAmount
object in the currency that the bank runs on.
"""
return SignedAmount(True, get_zero_amount())
class SignedAmountField(models.Field):
"""Custom implementation of the SignedAmount class as a database type."""
description = "Signed amount object in Taler style"
def db_type(self, connection: Any) -> str:
"""
Return the database type of the serialized amount.
"""
return "varchar"
def get_prep_value(self, value: SignedAmount) -> str:
"""
Stringifies the Amount object to feed the DB connector.
"""
c = value.amount.currency
if settings.TALER_CURRENCY != c:
raise CurrencyMismatchError(settings.TALER_CURRENCY, c)
return value.stringify()
@staticmethod
def from_db_value(value: str, *args) -> Amount:
"""
Parse the stringified Amount back to Python.
Parameters
----------
value : str
Serialized amount coming from the database.
(String in the usual CURRENCY:X.Y format)
args : any
Unused
"""
del args # pacify PEP checkers
return SignedAmount.parse(value)
def to_python(self, value: Any) -> Amount:
"""
Parse the stringified Amount back to Python. FIXME:
why this serializer consider _more_ cases respect to the
one above ('from_db_value')?
Parameters
----------
value: serialized amount coming from the database
"""
if isinstance(value, SignedAmount):
return value
try:
return SignedAmount.parse(value)
except BadFormatAmount:
raise ValidationError(
"Invalid input for a signed amount string: %s" % value
)
class AmountField(models.Field):
"""Custom implementation of the Amount class as a database type."""
description = "Amount object in Taler style"
def db_type(self, connection: Any) -> str:
"""
Return the database type of the serialized amount.
"""
return "varchar"
def get_prep_value(self, value: Amount) -> str:
"""
Stringifies the Amount object to feed the DB connector.
"""
if settings.TALER_CURRENCY != value.currency:
raise CurrencyMismatchError(settings.TALER_CURRENCY, value.currency)
return value.stringify()
@staticmethod
def from_db_value(value: str, *args) -> Amount:
"""
Parse the stringified Amount back to Python.
Parameters
----------
value : str
Serialized amount coming from the database.
(String in the usual CURRENCY:X.Y format)
args : any
Unused
"""
del args # pacify PEP checkers
return Amount.parse(value)
def to_python(self, value: Any) -> Amount:
"""
Parse the stringified Amount back to Python. FIXME:
why this serializer consider _more_ cases respect to the
one above ('from_db_value')?
Parameters
----------
value: serialized amount coming from the database
"""
if isinstance(value, Amount):
return value
try:
return Amount.parse(value)
except BadFormatAmount:
raise ValidationError("Invalid input for an amount string: %s" % value)
def join_dict(**inputDict):
return ", ".join(["%s==%s" % (key, value) for (key, value) in inputDict.items()])
class BankAccount(models.Model):
"""
The class representing a bank account.
"""
is_public = models.BooleanField(default=False)
account_no = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
balance = SignedAmountField(default=get_zero_signed_amount)
class BankTransaction(models.Model):
"""
The class representing a bank transaction.
"""
amount = AmountField(default=False)
debit_account = models.ForeignKey(
BankAccount,
on_delete=models.CASCADE,
db_index=True,
related_name="debit_account",
)
credit_account = models.ForeignKey(
BankAccount,
on_delete=models.CASCADE,
db_index=True,
related_name="credit_account",
)
subject = models.CharField(default="(no subject given)", max_length=200)
date = models.DateTimeField(auto_now=True, db_index=True)
cancelled = models.BooleanField(default=False)
request_uid = models.CharField(max_length=128, unique=True)
class TalerWithdrawOperation(models.Model):
withdraw_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
amount = AmountField(default=False)
withdraw_account = models.ForeignKey(
BankAccount,
on_delete=models.CASCADE,
db_index=True,
related_name="withdraw_account",
)
selection_done = models.BooleanField(default=False)
confirmation_done = models.BooleanField(default=False)
aborted = models.BooleanField(default=False)
selected_exchange_account = models.ForeignKey(
BankAccount,
null=True,
on_delete=models.CASCADE,
related_name="selected_exchange_account",
)
selected_reserve_pub = models.TextField(null=True)

259
talerbank/app/schemas.py Normal file
View File

@ -0,0 +1,259 @@
##
# This file is part of TALER
# (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 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 Marcello Stanisci
# @brief definitions of JSON schemas for validating data
import json
from django.conf import settings
from django.core.exceptions import ValidationError
from django import forms
from django.core.validators import RegexValidator
from urllib.parse import urlparse
from taler.util.taler_error_codes import ErrorCode
from http import HTTPStatus
##
# Constant value for the biggest number the bank handles.
# This value is just equal to the biggest number that JavaScript
# can handle (because of the wallet).
# FIXME: also defined in views.py. Need a common.py to contain
# such definitions ?
UINT64_MAX = (2 ** 64) - 1
##
# Pattern for amounts, plain RegEx.
AMOUNT_REGEX = "^[A-Za-z0-9_-]+:([0-9]+)\.?([0-9]+)?$"
##
# Exception class to be raised when a expected URL parameter
# is not found.
class InvalidSession(ValueError):
##
# Init method.
#
# @param self the object itself.
# @param http_status_code the HTTP response code to return
# to the caller (client).
def __init__(self, http_status_code):
self.hint = "Landed on a broken session"
self.http_status_code = http_status_code
super().__init__()
class InternalServerError(Exception):
def __init__(self, hint):
self.hint = hint
self.http_status_code = HTTPStatus.INTERNAL_SERVER_ERROR
self.taler_error_code = ErrorCode.INTERNAL_LOGIC_ERROR
##
# Exception class to be raised when a JSON
# object does not respect a specification.
class JSONFieldException(ValueError):
##
# Init method.
#
# @param self the object itself.
# @param error object containing the hint, as created by
# the Form API.
# @param http_status_code the HTTP response code to return
# to the caller (client).
def __init__(self, error, http_status_code):
for k, errors in error.as_data().items():
messages = [", ".join(error.messages) for error in errors]
line = f"{k}: " + "".join(messages)
super(JSONFieldException, self).__init__(line)
self.hint = line
self.http_status_code = http_status_code
self.taler_error_code = TalerErrorCode.BANK_JSON_INVALID
##
# Exception class to be raised when at least one expected URL
# parameter is either not found or malformed.
class URLParamValidationError(ValueError):
##
# Init method.
#
# @param self the object itself.
# @param error object containing the hint.
# @param http_status_code the HTTP response code to return
# to the caller (client).
def __init__(self, error, http_status_code):
self.hint = json.stringify(error.as_json())
self.http_status_code = http_status_code
self.taler_error_code = ErrorCode.BANK_PARAMETER_MISSING_OR_INVALID
super().__init__()
class AuthForm(forms.Form):
type = forms.CharField(
validators=[
RegexValidator("^basic$", message="Only 'basic' method provided for now")
]
)
data = forms.Field(required=False)
class AuthField(forms.Field):
##
# No need to touch the input. Dict is good
# and gets validated by the "validate()" method.
def to_python(self, value):
return value
##
# Validate input.
def validate(self, value):
af = AuthForm(value)
if not af.is_valid():
raise ValidationError(json.dumps(af.errors.as_json()))
##
# Common logic to inherit from all the other validators
class BankValidator:
def __init__(self, validator, data):
self.validation_result = validator(data)
if not self.validation_result.is_valid():
raise JSONFieldException(
self.validation_result.errors, HTTPStatus.BAD_REQUEST
)
def get(self, name, default=None):
ret = self.validation_result.cleaned_data.get(name)
if not ret:
return default
return ret
class AddIncomingData(BankValidator):
def __init__(self, data):
super(AddIncomingData, self).__init__(self.InnerValidator, data)
class InnerValidator(forms.Form):
amount = forms.CharField(
validators=[
RegexValidator(
AMOUNT_REGEX, message="Format CURRENCY:X[.Y] not respected"
)
]
)
subject = forms.CharField()
credit_account = forms.IntegerField(min_value=1)
exchange_url = forms.URLField()
##
# Subset of /history and /history-range input.
class HistoryParamsBase(forms.Form):
cancelled = forms.CharField(
required=False,
empty_value="show",
validators=[
RegexValidator("^(omit|show)$", message="Only 'omit' or 'show' are valid")
],
)
ordering = forms.CharField(
required=False,
empty_value="descending",
validators=[
RegexValidator(
"^(ascending|descending)$",
message="Only 'ascending' or 'descending' are valid",
)
],
)
direction = forms.CharField(
validators=[
RegexValidator(
"^(debit|credit|both|cancel\+|cancel-)$",
message="Only: debit/credit/both/cancel+/cancel-",
)
]
)
# FIXME: adjust min/max values.
account_number = forms.IntegerField(required=False)
class HistoryParams(BankValidator):
def __init__(self, data):
super(HistoryParams, self).__init__(self.InnerValidator, data)
class InnerValidator(HistoryParamsBase):
# FIXME: adjust min/max values.
delta = forms.IntegerField()
start = forms.IntegerField(required=False)
class PaytoField(forms.Field):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def to_python(self, value):
return value
def validate(self, value):
# The request misses this, default exchange
# will be used. NOTE: experience showed that the
# "required=False" argument given when init the object
# does NOT prevent this function from being called!
if not value:
return
wire_uri = urlparse(value)
if "payto" != wire_uri.scheme:
raise ValidationError("URL is not 'payto'")
class WithdrawHeadless(BankValidator):
def __init__(self, data):
super(WithdrawHeadless, self).__init__(self.InnerValidator, data)
class InnerValidator(forms.Form):
amount = forms.CharField(
validators=[
RegexValidator(
AMOUNT_REGEX, message="Format CURRENCY:X[.Y] not respected"
)
]
)
reserve_pub = forms.CharField(required=True)
exchange_payto_uri = PaytoField(required=True)
class WithdrawHeadlessUri(BankValidator):
def __init__(self, data):
super(WithdrawHeadlessUri, self).__init__(self.InnerValidator, data)
class InnerValidator(forms.Form):
amount = forms.CharField(
validators=[
RegexValidator(
AMOUNT_REGEX, message="Format CURRENCY:X[.Y] not respected"
)
]
)

View File

@ -0,0 +1,80 @@
.abort-button {
margin-left: 2px;
border: 2px solid rgb(0, 120, 231);
color: rgb(0, 120, 231);
font-size: 87%;
margin-top: 1px;
background: white;
}
div.pages-list {
margin-top: 15px;
}
a.page-number {
color: blue;
}
a.current-page-number {
color: inherit;
}
.cancelled {
text-decoration: line-through;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
#transfer-fields {
display: flex;
flex-wrap: wrap;
}
#id_amount {
width: 6em;
display: inline-block;
border-radius: 4px 0px 0px 4px;
}
/**
* Amount without the currency,
* placed left to a .currency-indicator.
*/
#main .amount {
width: 6em;
display: inline-block;
border-radius: 4px 0px 0px 4px;
}
/*
* Currency indicator to the right of input fields,
* with non-rounded corners to the left.
*/
#main .currency-indicator {
color: black;
display: inline-block;
border-radius: 0px 4px 4px 0px;
}
#main .fieldlabel {
display: block;
padding-bottom: 0.5em;
}
#main .fieldbox {
margin-right: 1em;
margin-bottom: 0.5em;
}
#logout-button {
display: block;
width: fit-content;
}

View File

@ -0,0 +1,138 @@
@charset "UTF-8";
/*
Style common to all demo pages.
Colors:
- #1e2739 (dark blue)
- #0042b2 (default blue)
- #3daee9 (highlight blue)
*/
.demobar h1 {
text-align: center;
}
.demobar > p {
padding: 0.5em;
}
.demobar a,
.demobar a:visited {
color: inherit;
}
.tt {
font-family: "Lucida Console", Monaco, monospace;
}
.informational-ok {
background: lightgreen;
border-radius: 1em;
padding: 0.5em;
}
.informational-fail {
background: lightpink;
border-radius: 1em;
padding: 0.5em;
}
.content {
margin-left: 2em;
overflow-x: auto;
}
.demobar {
overflow-x: auto;
background-color: #0042b2;
color: white;
}
body {
overflow-x: hidden;
overflow-y: auto;
}
.navcontainer {
background: #0042b2;
margin-bottom: 50px;
width: 100%;
color: white;
position: -webkit-sticky;
position: sticky;
top: 0px;
width: 100vw;
backdrop-filter: blur(10px);
opacity: 1;
z-index: 10000;
}
nav {
left: 1vw;
position: relative;
background: #0042b2;
z-index: 10000;
}
nav a,
nav span,
.navbtn {
border: none;
color: white;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
background: #0042b2;
height: inherit;
}
nav a,
nav span,
.navbtn {
padding: 15px 32px;
}
nav a:hover,
nav span:hover,
.navbtn:hover {
background: #3daee9;
}
nav a.active,
nav span.active,
.navbtn.active {
background-color: #1e2739;
}
nav a.active:hover,
nav span.active:hover,
.navbtn.active:hover {
background: #3daee9;
}
nav a,
nav span,
.navbtn {
cursor: pointer;
}
nav .right {
float: right;
margin-right: 5vw;
}
nav .right div.nav {
display: none;
}
nav .right div.nav:hover {
display: block;
}
nav .right:hover div.nav {
display: block;
}
.langbtn {
width: 100%;
text-align: left;
}

View File

@ -0,0 +1,4 @@
a[disabled="true"] {
pointer-events: none;
color: grey;
}

View File

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="670"
height="300"
viewBox="0 0 201 90"
version="1.1"
id="svg8">
<g
id="logo">
<g
id="circles"
style="fill:#FFF;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.327943">
<path
d="m 86.662153,1.1211936 c 15.589697,0 29.129227,9.4011664 35.961027,23.2018054 h -5.81736 C 110.4866,13.623304 99.349002,6.5180852 86.662153,6.5180852 c -19.690571,0 -35.652876,17.1120008 -35.652876,38.2205688 0,10.331797 3.825597,19.704678 10.03957,26.582945 -1.342357,1.120912 -2.771532,2.127905 -4.275488,3.006754 C 50.071485,66.553412 45.974857,56.15992 45.974857,44.738654 c 0,-24.089211 18.216325,-43.6174604 40.687296,-43.6174604 z M 122.51416,65.375898 c -6.86645,13.680134 -20.34561,22.980218 -35.852007,22.980218 -1.052702,0 -2.096093,-0.04291 -3.128683,-0.127026 3.052192,-1.561167 5.913582,-3.480387 8.538307,-5.707305 10.320963,-1.684389 19.185983,-8.113638 24.601813,-17.145887 z"
id="path2350" />
<path
d="m 64.212372,1.1211936 c 1.052607,0 2.095998,0.042919 3.128684,0.1270583 C 64.288864,2.8094199 61.427378,4.728606 58.802653,6.9555572 41.679542,9.7498571 28.559494,25.601563 28.559494,44.738654 c 0,14.264563 7.29059,26.702023 18.093843,33.268925 -1.593656,0.26719 -3.226966,0.406948 -4.890748,0.406948 -1.239545,0 -2.46151,-0.07952 -3.663522,-0.229364 C 29.191129,70.184015 23.525076,58.171633 23.525076,44.738654 23.525076,20.649443 41.7414,1.1211936 64.212372,1.1211936 Z M 69.62209,82.521785 C 79.943207,80.837396 88.808164,74.407841 94.224059,65.375422 h 5.840511 c -6.866354,13.680305 -20.345548,22.980694 -35.852198,22.980694 -1.052703,0 -2.095999,-0.04291 -3.128684,-0.127026 3.052002,-1.561371 5.913836,-3.480218 8.538402,-5.707305 z M 94.355885,24.322999 c -3.13939,-5.314721 -7.467551,-9.74275 -12.584511,-12.853269 1.593656,-0.26719 3.226904,-0.406948 4.890779,-0.406948 1.239451,0 2.461512,0.07952 3.663524,0.229364 4.016018,3.607242 7.373195,8.030111 9.849053,13.030853 z"
id="path2352" />
<path
d="m 41.762589,1.1211936 c 1.064296,0 2.118804,0.044379 3.162607,0.1302161 -3.046523,1.558961 -5.903162,3.4745139 -8.52358,5.6968133 C 19.254624,9.7205882 6.1097128,25.583465 6.1097128,44.738654 c 0,21.108568 15.9624012,38.22057 35.6528762,38.22057 12.599746,0 23.672446,-7.007056 30.013748,-17.583802 h 5.838515 C 70.748498,79.055727 57.26924,88.356116 41.762589,88.356116 c -22.470907,0 -40.6871998,-19.52825 -40.6871998,-43.617462 0,-24.089211 18.2162928,-43.6174604 40.6871998,-43.6174604 z M 71.905375,24.322999 c -1.31192,-2.220567 -2.830984,-4.287049 -4.528877,-6.166508 1.342452,-1.120945 2.771374,-2.128381 4.275139,-3.00723 2.372984,2.753011 4.418875,5.834636 6.072489,9.173738 z"
id="path2354" />
</g>
<g
id="letters"
style="fill:#FFF">
<path
d="m 76.135411,34.409066 h 9.161042 V 29.36588 H 61.857537 v 5.043186 h 9.161137 v 25.92317 h 5.116737 z"
id="path2346" />
<path
d="m 92.647571,52.856334 h 13.659009 l 2.93009,7.476072 h 5.36461 L 101.89122,29.144903 H 97.187186 L 84.477089,60.332406 h 5.199533 z m 11.802109,-4.822276 h -9.944771 l 4.951718,-12.386462 z"
id="path2362" />
<path
d="m 123.80641,29.366084 h -4.58038 v 30.966322 h 20.54728 v -4.910253 c -5.32227,0 -10.64463,0 -15.9669,0 z"
id="path2356" />
<path
d="m 166.4722,29.366084 h -21.37564 v 30.966322 h 21.58203 v -4.910253 h -16.54771 v -8.27275 h 14.48439 V 42.23925 h -14.48439 v -7.962811 h 16.34132 z"
id="path2360" />
<path
d="m 191.19035,39.474593 c 0,1.59947 -0.53646,2.87535 -1.61628,3.818883 -1.07281,0.95124 -2.52409,1.422837 -4.34678,1.422837 h -7.44851 V 34.276439 h 7.4073 c 1.9051,0 3.38376,0.435027 4.42939,1.312178 1.05226,0.870258 1.57488,2.167734 1.57488,3.885976 z m 6.06602,20.857813 -7.79911,-11.723191 c 1.01771,-0.294794 1.94631,-0.714813 2.78553,-1.260566 0.83885,-0.545619 1.56122,-1.209263 2.16629,-1.990627 0.60541,-0.781738 1.07981,-1.681096 1.42369,-2.698345 0.34378,-1.017553 0.51561,-2.175238 0.51561,-3.472883 0,-1.50409 -0.24743,-2.867948 -0.74267,-4.092048 -0.49515,-1.223794 -1.20344,-2.256186 -2.12499,-3.096734 -0.92173,-0.840446 -2.04957,-1.489252 -3.38375,-1.946452 -1.33447,-0.457267 -2.82692,-0.685476 -4.4774,-0.685476 h -12.87512 v 30.966322 h 5.03433 V 49.538522 h 6.37569 l 7.11829,10.793884 z"
id="path2358" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,99 @@
/*
@licstart The following is the entire license notice for the
JavaScript code in this page.
Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.
@licend The above is the entire license notice
for the JavaScript code in this page. */
"use strict";
var bank_currency;
var precision;
var callback_url;
var reserve_pub;
var suggested_exchange;
/**
* Get a constant stored in the DOM
* as a meta tag.
*/
function getConst(name) {
var metas = document.getElementsByTagName("meta");
if (!(name in metas)) {
return;
}
return metas[name].getAttribute("value");
}
/**
* Parse fractional format (e.g. 42.12 EUR) into
* Taler amount.
*/
function parseAmount(amount_str) {
var re = /([0-9]+)(\.?[0-9][0-9]?)? ([A-Z]+)/;
var amount = re.exec(amount_str);
if (amount == null || amount[0] != amount_str){
window.alert("Incorrect amount entered, give in the"
+ " form 'XY.Z EUR' or 'XY EUR'");
return null;
}
var amount_fraction = 0;
if (amount[2] != null) // fractional part given
amount_fraction = Number("0." + amount[2]) * 1000000;
if (amount[1] + amount_fraction == 0)
return null;
return {
value: Number(amount[1]),
fraction: amount_fraction,
currency: amount[amount.length - 1]
};
};
function init() {
bank_currency = getConst("currency");
precision = getConst("precision");
callback_url = getConst("callback-url");
reserve_pub = getConst("reserve-pub");
suggested_exchange = getConst("suggested-exchange");
if (reserve_pub) {
console.log("confirming reserve", reserve_pub);
taler.confirmReserve(reserve_pub);
}
document.getElementById("select-exchange").onclick = function () {
var form = document.getElementById("reserve-form");
var amount = {
value: parseInt(form.elements["reserve-amount"].value),
fraction: 0,
currency: bank_currency
};
console.log("callback_url", callback_url);
taler.createReserve(callback_url, amount, ["test"], suggested_exchange);
};
}
if (document.readyState == "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block main %}
<h1>{{ _("Error") }}</h1>
<aside class="sidebar" id="left">
</aside>
<section id="main">
<article>
<p>{{ _("Page not found!") }}</p>
</article>
</section>
{% endblock main %}

View File

@ -0,0 +1,16 @@
{% extends "common_base.html" %}
{% block head %}
<link rel="stylesheet" type="text/css" href="{{ static('bank.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ static('disabled-button.css') }}" />
{% endblock %}
{% block header_content %}
<h1><span class="it"><a href="{{ env('TALER_ENV_URL_BANK') }}">{{ gettext("Demo Bank") }}</a></span></h1>
<p>
{{ _("This part of the demo shows how a bank that supports Taler directly would work. In addition to using your own bank account, you can also see the transaction history of some <a href=\"{public_accounts}\">Public Accounts</a>.").format(public_accounts=url('public-accounts')) }}
</p>
{% endblock %}

View File

@ -0,0 +1,93 @@
<!DOCTYPE html>
<!--
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016, 2020, 2021 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>
<title>{{ page_title | default(gettext("GNU Taler Demo")) }}</title>
<link rel="stylesheet" type="text/css" href="{{ static('pure.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ static('demo.css') }}" />
{% block head %}
{% endblock %}
</head>
<body>
<header class="demobar" style="display: flex; flex-direction: row; justify-content: space-between;">
<div style="max-width: 50em; margin-left: 2em;">
{% block header_content %}
<p>This is the header content.</p>
{% endblock %}
</div>
<a href="https://taler.net/">
<img src="{{ static('logo-white.svg') }}" height="100" width="224" style="margin: 2em 2em"><br/>
</a>
</header>
<div style="display:flex; flex-direction: column;" class="navcontainer">
<nav class="demolist">
<a href="{{ env('TALER_ENV_URL_INTRO', '#') + getlang() + '/' }}"
{% if getactive() == 'landing' %} class="active" {% endif %}
>{{gettext("Introduction")}}</a>
<a href="{{ env('TALER_ENV_URL_BANK', '#') + getlang() + '/' }}"
{% if getactive() == 'bank' %} class="active" {% endif %}
>{{gettext("Bank")}}</a>
<a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG', '#') + getlang() + '/' }}"
{% if getactive() == 'blog' %} class="active" {% endif %}
>{{gettext("Essay Shop")}}</a>
<a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS', '#') + getlang() + '/' }}"
{% if getactive() == 'donations' %} class="active" {% endif %}
>{{gettext("Donations")}}</a>
<a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') + getlang() + '/' }}"
{% if getactive() == 'survey' %} class="active" {% endif %}
>{{gettext("Tipping/Survey")}}</a>
{# Language Selector #}
<div class="right">
<span>{{ all_languages[getlang()] | default("en") | safe }}</span>
<div style="position: relative; overflow: visible;">
<div class="nav" style="position: absolute; background: #0042b2; max-height: 60vh; overflow-y: scroll">
<br>
{% for lang_code, lang_display in all_languages.items() %}
{% if lang_code != getlang() %}
<a href="/{{ lang_code }}/" class="navbtn">{{ lang_display | safe }}</a>
<br>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</nav>
</div>
<section id="main" class="content">
{% block main %}
This is the main content of the page.
{% endblock %}
<hr />
<div>
<p>
{{
gettext('You can learn more about GNU Taler on our main <a href="{site}">website</a>.').format(site="https://taler.net/" + getlang() + "/")
}}
</p>
<div style="flex-grow:1"></div>
<p>Copyright &copy; 2014&mdash;2021 Taler Systems SA</p>
</div>
</section>
</body>
</html>

View File

@ -0,0 +1,71 @@
{% extends "base.html" %}
{#
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016 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/>
@author Marcello Stanisci
@author Florian Dold
#}
{% block main %}
<h1 class="nav"> {{ _("Welcome to the {currency} Bank!").format(currency=settings_value("TALER_CURRENCY")) }}</h1>
<section id="main">
<article>
<div class="login-form">
<h2>{{ _("Please login!") }}</h2>
{% if form.errors %}
<p class="informational informational-fail">
{{ _("Your username and password didn't match. Please try again.") }}
</p>
{% endif %}
{% if next and next != url('profile') %}
{% if request.user and request.user.is_authenticated %}
<p class="informational informational-fail">
{{ _("Your account doesn't have access to this page. To proceed, please login with an account that has access.") }}</p>
{% else %}
<p class="informational informational-fail">
{{ _("Please login to see this page.") }}
</p>
{% endif %}
{% endif %}
<form method="post" class="pure-form" action="{{ url('login') }}">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
{{ form.username }}
<input type="password" name="password" placeholder="password" />
<input type="submit" value="login" class="pure-button pure-button-primary" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</div>
{% if settings_value("ALLOW_REGISTRATIONS") %}
<p>
{% autoescape off %}
{{ _("If you are a new customer please <a href=\"{register_link}\">register</a>. Registration is fast and free, and it gives you a registration bonus of 100 {currency}").format(register_link=url('register'), currency=settings_value("TALER_CURRENCY")) }}
{% endautoescape %}
</p>
{% else %}
<p>{{ _("Registrations are not open to the public.") }}</p>
{% endif %}
<p>
{% autoescape off %}
{{ _("To view transactions of public accounts, please <a href=\"{public_accounts}\">click here</a>.").format(public_accounts=url('public-accounts')) }}
{% endautoescape %}
</p>
</article>
</section>
{% endblock main %}

View File

@ -0,0 +1,66 @@
{% extends "base.html" %}
{#
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016 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/>
@author Marcello Stanisci
@author Florian Dold <dold@taler.net>
#}
{% block main %}
<div>
<a href="{{ url('logout') }}" class="pure-button">[{{ _("Logout") }}]</a><br>
</div>
<section id="main">
{% if hint != "" %}
<article>
<div class="notification">
{% if not is_success %}
<p class="informational informational-fail">
{% else %}
<p class="informational informational-ok">
{% endif %}
{{ hint }}
</p>
</div>
</article>
{% endif %}
<article>
<div>
<h2>{{ _("Wire transfer") }}</h2>
<p>{{ _("Transfer money via the payto system:") }}
<br>
<br>
<tt style="font-size: 15px">payto://x-taler-bank/[bank-hostname]/[receiver-username]?message=[subject]&amount=[{{ currency }}:X.Y]</tt>
</p>
<form action="{{ url('payto-transfer') }}"
method="POST"
name="payto-form">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input name="address"
size="90"
placeholder={{ _("payto address") }}
pattern="payto://x-taler-bank/[a-z\.]+(:[0-9]+)?/[0-9a-zA-Z]+\?message=[a-zA-Z0-9 ]+&amount={{ currency }}:[0-9]+(\.[0-9]+)?" />
<input class="pure-button pure-button-primary"
type="submit"
value={{ _("Confirm") }} />
</form>
</div>
</article>
</section>
{% endblock %}

View File

@ -0,0 +1,120 @@
{% extends "base.html" %}
{#
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016 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/>
@author Marcello Stanisci
@author Florian Dold <dold@taler.net>
#}
{% block main %}
<div>
<h1 class="nav">
{% autoescape off %}
{{ _("Welcome <em>{name}</em>!").format(name=name) }}
{% endautoescape %}
</h1>
<a href="{{ url('logout') }}" class="pure-button logout-button">[{{ _("Logout") }}]</a><br>
</div>
<section id="menu">
<p>{{ _("Bank account balance") }}: <br/> <b>{{ amount_stringify(balance) }}</b></p>
</section>
<section id="main">
{% if hint != "" %}
<article>
<div class="notification">
{% if not is_success %}
<p class="informational informational-fail">
{% else %}
<p class="informational informational-ok">
{% endif %}
{{ hint }}
</p>
</div>
</article>
{% endif %}
<article>
<div>
<h2>{{ _("Withdraw Money into a Taler wallet") }}</h2>
<form id="reserve-form"
class="pure-form"
action="{{ url('start-withdrawal') }}"
method="post"
name="tform">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
{{ _("Amount to withdraw") }}:
{% if settings_value("SHOW_FREEFORM_WITHDRAWAL") %}
<input type="number" name="withdraw-amount" class="amount" />
{%- else -%}
<select id="reserve-amount" name="withdraw-amount" class="amount" autofocus>
<option value="5.00">5.00</option>
<option value="10.00">10.00</option>
<option value="15.00">15.00</option>
<option value="20.00">20.00</option>
</select>
{%- endif -%}
<input type="text" readonly class="currency-indicator" size="{{ currency|length }}" tabindex="-1" value="{{ currency }}">
<input id="select-exchange"
class="pure-button pure-button-primary"
type="submit"
value="{{ _('Start withdrawal') }}" />
</form>
</div>
</article>
<article>
<h2>{{ _("Transactions for") }} {{ name }}</h2>
<p><a href={{ url('wiretransfer-form') }}>{{_("Transfer money manually") }}</a></p>
<div id="transactions-history">
{% if history %}
<table class="pure-table">
<thead>
<tr>
<th style="text-align:center">{{ _("Date") }}</th>
<th style="text-align:center">{{ _("Amount") }}</th>
<th style="text-align:center">{{ _("Counterpart") }}</th>
<th style="text-align:center">{{ _("Subject") }}</th>
</tr>
</thead>
<tbody>
{% for item in history %}
<tr>
<td style="text-align:right">{{ item.date }}</td>
<td style="text-align:right">
{{ item.sign }} {{ item.amount }}
</td>
<td class="text-align:left">
{% if item.counterpart_username %}
{{ item.counterpart_username }}
{% endif %} (account #{{ item.counterpart }})
</td>
<td class="text-align:left
{% if item.cancelled %}
{{ _("cancelled") }}
{% endif %}">{{ item.subject }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="informational informational-fail">
{{ _("No transactions made to/from this account") }}
</p>
{% endif %}
</div>
</article>
</section>
{% endblock %}

View File

@ -0,0 +1,105 @@
{% extends "base.html" %}
{#
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016 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/>
@author Marcello Stanisci
#}
{% block main %}
<h1 class="nav">{{ _("History of public accounts") }}</h1>
<a href="{{ url('index') }}">Back</a>
<section id="main">
<article>
{% if hint != "" %}
<div class="notification">
{% if not is_success %}
<p class="informational informational-fail">
{% else %}
<p class="informational informational-ok">
{% endif %}
{{ hint }}
</p>
</div>
{% endif %}
<div name="accountMenu" class="pure-menu pure-menu-horizontal">
<ul class="pure-menu-list">
{% for account in public_accounts %}
{% if account.account_no == selected_account.number %}
<li class="pure-menu-item pure-menu-selected">
{% else %}
<li class="pure-menu-item pure-menu">
{% endif %}
<a href="{{ url("public-accounts", name=account.user.username) }}" class="pure-menu-link">
{{ account.user.username }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="results">
{% if selected_account.history %}
<table class="pure-table pure-table-striped">
<thead>
<th>{{ _("Date") }}</th>
<th>{{ _("Amount") }}</th>
<th>{{ _("Counterpart") }}</th>
<th>{{ _("Subject") }}</th>
</thead>
<tbody>
{% for entry in selected_account.history %}
<tr>
<td>{{entry.date}}</td>
<td>
{{ entry.sign }} {{ entry.amount }}
</td>
<td>{{ _("account") }} #{{ entry.counterpart }}</td>
<td {% if entry.cancelled %} class="cancelled" {% endif %}>
{{ entry.subject }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pages-list">
{% if back %}
<a
class="page-number"
href="{{ url("public-accounts", name=selected_account.name, page=back) }}">&lsaquo;...</a>
{% endif %}
{% for pagej in pages %}
<a
{% if pagej == current_page%}
class="current-page-number"
{% else %}
class="page-number"
{% endif %}
href="{{ url("public-accounts", name=selected_account.name, page=pagej) }}">{{ pagej }}</a>
{% endfor %}
{% if forth %}
<a
class="page-number"
href="{{ url("public-accounts", name=selected_account.name, page=forth) }}">...&rsaquo;</a>
{% endif %}
</div>
{% else %}
<p>{{ _("No history for account #{account_number} ({account_name}) yet").format(account_number=selected_account.number, account_name=selected_account.name) }}</p>
{% endif %}
</div>
</article>
</section>
{% endblock main %}

View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block main %}
<h1 class="nav">{{ _("Register to the {currency} bank!").format(currency=settings_value('TALER_CURRENCY')) }}</h1>
<aside class="sidebar" id="left">
</aside>
<section id="main">
<article>
<a href="{{ url('index') }}">{{ _("Back") }}</a>
<div class="notification">
{% if wrong %}
<p class="informational informational-fail">
{{ hint }}
</p>
{% endif %}
{% if not_available %}
<p class="informational informational-fail">
{{ _("Sorry, this username is no longer available. Please choose another one!") }}
</p>
{% endif %}
</div>
</article>
</section>
<section id="main">
<article>
<div class="register-form">
<h1>{{ _("Registration form") }}</h1>
<form class="pure-form" method="post" action="{{ url('register') }}">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input type="text" name="username" placeholder="username" autofocus />
<input type="password" name="password" placeholder="password" />
<input type="submit" value="{{ _('Sign Up') }}" class="pure-button pure-button-primary" />
</form>
</div>
</article>
</section>
{% endblock main %}

View File

@ -0,0 +1,72 @@
{% extends "base.html" %}
{#
This file is part of GNU TALER.
Copyright (C) 2014, 2015, 2016 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/>
@author Marcello Stanisci
@author Florian Dold <dold@taler.net>
#}
{% block main %}
<div>
<a href="{{ url('logout') }}" class="pure-button">[{{ _("Logout") }}]</a><br>
</div>
<section id="main">
{% if hint != "" %}
<article>
<div class="notification">
{% if not is_success %}
<p class="informational informational-fail">
{% else %}
<p class="informational informational-ok">
{% endif %}
{{ hint }}
</p>
</div>
</article>
{% endif %}
<article>
<div>
<h2>{{ _("Wire transfer") }}</h2>
<p>{{ _("Transfer money to another account of this bank:") }}
<br>
<br>
</p>
<form action="{{ url('wiretransfer-form') }}"
method="POST"
name="wire-transfer-form">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input name="receiver" placeholder={{ _("receiver") }} required />
<br>
<br>
<input name="subject" placeholder={{ _("subject") }} required />
<br>
<br>
<input name="amount" placeholder="XY.XY" pattern="[0-9]+(\.[0-9]+)?" required>
<label>{{ currency }}</label>
</input>
<input type="hidden" name="currency" value={{ currency }} />
<br>
<br>
<input class="pure-button pure-button-primary" type="submit" value={{ _("Confirm") }} />
</form>
<p><a href="{{ url('payto-form') }}">Want to try the raw payto://-format?</a></p>
</div>
</article>
</section>
{% endblock %}

View File

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block main %}
<h1 class="nav">{{ _("Confirm Withdrawal") }}</h1>
{% if not is_success %}
<p class="informational informational-fail">
{{ hint }}
</p>
{% endif %}
<p>
{% autoescape off %}
{{ _("{currency} Bank needs to verify that you intend to withdraw <b>{amount}</b> from <b>{exchange}</b>. To prove that you are the account owner, please answer the following &quot;security question&quot; (*):").format(currency=settings_value("TALER_CURRENCY"), amount=amount, exchange=exchange) }}
{% endautoescape %}
</p>
<p>
{{ _("What is {question}?").format(question=question) }}
</p>
<div>
<form
method="post"
action="{{ url('withdraw-confirm', withdraw_id) }}"
class="pure-form"
style="float: left;">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input type="text" name="pin_0" value="" autocomplete="off" autofocus />
<input type="hidden" name="pin_1" value="{{ hashed_answer }}" />
<input type="hidden" name="question_url" value="{{ request.get_full_path() }}" />
<input type="submit" value="Ok" class="pure-button pure-button-primary" />
</form>
<form
method="post"
action="{{ url('abort-withdrawal', withdraw_id=withdraw_id) }}"
class="pure-form">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input
type="submit"
value="Abort"
class="pure-button pure-button-primary abort-button" />
</form>
</div>
<p>
<small style="margin: 40px 0px">(*) {{ _("A real bank should ask for a PIN/TAN instead of a simple calculation. For example by sending a one time password to the customer's mobile or providing her a random password generator.") }}
<small>
</p>
{% endblock main %}

View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block head %}
<script>
// prettier-ignore
let checkUrl = JSON.parse('{{ withdraw_check_url | tojson }}');
let delayMs = 500;
function check() {
let req = new XMLHttpRequest();
req.onreadystatechange = function () {
if (req.readyState === XMLHttpRequest.DONE) {
if (req.status === 200) {
try {
let resp = JSON.parse(req.responseText);
if (resp.selection_done) {
document.location.reload(true);
}
} catch (e) {
console.error("could not parse response:", e);
}
}
setTimeout(check, delayMs);
}
};
req.onerror = function () {
setTimeout(check, delayMs);
}
req.open("GET", checkUrl);
req.send();
}
setTimeout(check, delayMs);
</script>
{% endblock head %}
{% block main %}
<h1 class="nav">{{ _("Withdraw to a Taler Wallet") }}</h1>
<p>
{{ _("You can use this QR code to withdraw to your mobile wallet:") }}
</p>
{{ qrcode_svg | safe }}
<p>
{% autoescape off %}
{{ _("Click <a href=\"{taler_withdraw_uri}\">this link</a> to open your system's Taler wallet if it exists.").format(taler_withdraw_uri=taler_withdraw_uri) }}
{% endautoescape %}
</p>
{% endblock main %}

View File

@ -0,0 +1,17 @@
# Config file containing intentional errors, used
# to test how the bank reacts.
[taler]
CURRENCY = KUDOS
[bank]
# Which database should we use?
DATABASE = postgres:///talercheck
# FIXME
MAX_DEBT = 33 KUDOS
# FIXME
MAX_DEBT_BANK = KUDOS:0

View File

@ -0,0 +1,10 @@
[bank]
# Which database should we use?
DATABASE = postgres:///talerbank
# FIXME
MAX_DEBT = KUDOS:50
# FIXME
MAX_DEBT_BANK = KUDOS:0

View File

@ -0,0 +1,23 @@
[taler]
CURRENCY = KUDOS
[bank]
ALLOW_REGISTRATIONS = yes
# Which database should we use?
DATABASE = postgres:///talercheck
NDIGITS = 2
# FIXME
MAX_DEBT = KUDOS:50.0
# FIXME
MAX_DEBT_BANK = KUDOS:1000000
UWSGI_SERVE = unix
UWSGI_UNIXPATH = /tmp/banktest-unused.uwsgi
UWSGI_UNIXPATH_MODE = 660
SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/bank.example.com/2

798
talerbank/app/tests.py Normal file
View File

@ -0,0 +1,798 @@
# This file is part of TALER
# (C) 2014, 2015, 2016 Taler Systems SA
#
# 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 Marcello Stanisci
import json
import time
import uuid
import zlib
import timeit
import logging
import unittest
import base64
from urllib.parse import unquote
from django.db import connection
from django.test import TestCase, Client
from django.urls import reverse
from django.conf import settings
from django.contrib.auth.models import User
from mock import patch, MagicMock
from .models import BankAccount, BankTransaction, TalerWithdrawOperation
from . import urls
from .views import wire_transfer, get_reserve_pub
from taler.util.payto import PaytoParse, PaytoFormatError
from taler.util.amount import (
Amount,
SignedAmount,
CurrencyMismatchError,
AmountFormatError,
)
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.DEBUG)
LOGGER.handlers = []
# Logfile opens in 'append' mode.
fileHandler = logging.FileHandler("tests.log")
fileHandler.setLevel(logging.DEBUG)
fileHandler.setFormatter(
logging.Formatter(fmt="%(asctime)-15s %(module)s %(levelname)s %(message)s")
)
LOGGER.addHandler(fileHandler)
def make_auth_line(username, password):
credentials = "%s:%s" % (username, password)
b64enc = base64.b64encode(bytes(credentials, "utf-8"))
header_line = "Basic %s" % b64enc.decode()
return header_line
def clear_db():
User.objects.all().delete()
BankAccount.objects.all().delete()
BankTransaction.objects.all().delete()
with connection.cursor() as cursor:
cursor.execute("ALTER SEQUENCE app_bankaccount_account_no_seq" " RESTART")
cursor.execute("ALTER SEQUENCE app_banktransaction_id_seq RESTART")
class WireGatewayTestCase(TestCase):
def setUp(self):
clear_db()
exchange = User.objects.create_user(username="RandomExchange", password="XYZ")
exchange.save()
customer = User.objects.create_user(username="RandomCustomer", password="ABC")
customer.save()
exchange_bank_account = BankAccount(
user=exchange,
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
exchange_bank_account.save()
customer_bank_account = BankAccount(
user=customer,
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
customer_bank_account.save()
self.client = Client()
def test_all(self):
r = self.client.post(
reverse("twg-add-incoming", kwargs=dict(acct_id="RandomExchange")),
HTTP_AUTHORIZATION=make_auth_line("RandomExchange", "XYZ"),
content_type="application/json",
data=dict(
amount=f"{settings.TALER_CURRENCY}:10",
reserve_pub="FXWC2JHBY8B0XE2MMGAJ9TGPY307TN12HVEKYSTN6HE3GTHTF8XG",
debit_account="payto://x-taler-bank/localhost/RandomCustomer",
),
)
self.assertEqual(r.status_code, 200)
# Test incoming transfers of Exchange.
r = self.client.get(
reverse("twg-history-incoming", kwargs=dict(acct_id="RandomExchange")),
dict(delta=5),
HTTP_AUTHORIZATION=make_auth_line("RandomExchange", "XYZ"),
)
self.assertEqual(r.status_code, 200)
# Test outgoing transfers of the Exchange.
r = self.client.post(
reverse("twg-transfer", kwargs=dict(acct_id="RandomExchange")),
HTTP_AUTHORIZATION=make_auth_line("RandomExchange", "XYZ"),
content_type="application/json",
data=dict(
request_uid="0",
amount=f"{settings.TALER_CURRENCY}:3",
exchange_base_url="mock",
wtid="123",
credit_account="payto://x-taler-bank/localhost/RandomCustomer",
),
)
r = self.client.get(
reverse("twg-history-outgoing", kwargs=dict(acct_id="RandomExchange")),
dict(delta=5),
HTTP_AUTHORIZATION=make_auth_line("RandomExchange", "XYZ"),
)
self.assertEqual(r.status_code, 200)
class IntegrationApiTestCase(TestCase):
def setUp(self):
clear_db()
self.exchange = User.objects.create_user(
username="RandomExchange", password="XYZ"
)
self.exchange.save()
self.exchange_bank_account = BankAccount(
user=self.exchange,
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
self.exchange_bank_account.save()
self.user = User.objects.create_user(username="RandomUser", password="XYZ")
self.user.save()
self.user_bank_account = BankAccount(
user=self.user,
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
self.user_bank_account.save()
self.client = Client()
def test_config(self):
c = Client()
r = c.get("/config")
self.assertEqual(r.status_code, 200)
def test_withdraw(self):
operation = TalerWithdrawOperation(
amount=Amount(settings.TALER_CURRENCY, 100, 0),
withdraw_account=self.user_bank_account,
)
operation.save()
r = self.client.post(
reverse(
"api-withdraw-operation", kwargs=dict(withdraw_id=operation.withdraw_id)
),
data=dict(
reserve_pub="reserve-public-key",
selected_exchange=f"payto://x-taler-bank/localhost/RandomExchange",
),
content_type="application/json",
)
self.assertEqual(r.status_code, 200)
class AccessApiWithdrawTestCase(TestCase):
def setUp(self):
clear_db()
self.user = User.objects.create_user(username="RandomUser", password="XYZ")
self.user.save()
self.user_bank_account = BankAccount(
user=self.user,
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
self.user_bank_account.save()
self.client = Client()
def create_withdrawal(self):
r = self.client.post(
reverse("access-api-withdrawal", kwargs=dict(acct_id="RandomUser")),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
content_type="application/json",
data=dict(amount=f"{settings.TALER_CURRENCY}:5"),
)
self.assertEqual(r.status_code, 200)
data = r.content.decode("utf-8")
data_dict = json.loads(data)
withdrawal_id = data_dict.get("withdrawal_id")
self.assertTrue(withdrawal_id)
return withdrawal_id
def test_accees_withdraw_create(self):
self.create_withdrawal()
def test_accees_withdraw_status(self):
withdrawal_id = self.create_withdrawal()
r = self.client.get(
reverse(
"access-api-withdrawal-status",
kwargs=dict(acct_id="RandomUser", wid=withdrawal_id),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
def test_accees_withdraw_abort(self):
withdrawal_id = self.create_withdrawal()
r = self.client.post(
reverse(
"access-api-withdrawal-abort",
kwargs=dict(acct_id="RandomUser", wid=withdrawal_id),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
def test_accees_withdraw_confirm(self):
withdrawal_id = self.create_withdrawal()
r = self.client.post(
reverse(
"access-api-withdrawal-confirm",
kwargs=dict(acct_id="RandomUser", wid=withdrawal_id),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
def test_accees_withdraw_abort_then_confirm(self):
withdrawal_id = self.create_withdrawal()
r = self.client.post(
reverse(
"access-api-withdrawal-abort",
kwargs=dict(acct_id="RandomUser", wid=withdrawal_id),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
r = self.client.post(
reverse(
"access-api-withdrawal-confirm",
kwargs=dict(acct_id="RandomUser", wid=withdrawal_id),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 409)
def test_integration_api_withdraw_status(self):
wid = self.create_withdrawal()
r = self.client.get(
reverse(
"access-api-withdrawal-status",
kwargs=dict(acct_id="RandomUser", wid=wid),
),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
def test_integration_api_withdraw_confirm(self):
wid = self.create_withdrawal()
r = self.client.post(
reverse(
"access-api-withdrawal-confirm",
kwargs=dict(acct_id="RandomUser", wid=wid),
),
data=dict(
reserve_pub="FXWC2JHBY8B0XE2MMGAJ9TGPY307TN12HVEKYSTN6HE3GTHTF8XG",
selected_exchange="payto://x-taler-bank/localhost/RandomUser",
),
content_type="application/json",
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
class AccessApiBalanceTestCase(TestCase):
def setUp(self):
clear_db()
self.user = User.objects.create_user(username="RandomUser", password="XYZ")
self.user.save()
self.user_bank_account = BankAccount(user=self.user)
self.user_bank_account.save()
def test_balance(self):
c = Client()
r = c.get(
reverse("access-api-balance", kwargs=dict(acct_id="RandomUser")),
HTTP_AUTHORIZATION=make_auth_line("RandomUser", "XYZ"),
)
self.assertEqual(r.status_code, 200)
class AccessApiTestingRegistrationTestCase(TestCase):
def setUp(self):
clear_db()
self.user = User.objects.create_user(username="Bank", password="Bank")
self.user.save()
self.user_bank_account = BankAccount(user=self.user)
self.user_bank_account.save()
def test_testing_registration(self):
c = Client()
r = c.post(
reverse("testing-withdraw-register"),
data=dict(username="x", password="y"),
content_type="application/json",
)
self.assertEqual(r.status_code, 200)
class ReservePubExtractionTestCase(TestCase):
def test_extraction(self):
self.assertTrue(
get_reserve_pub("0T096A11M57GWGG0P6ZM9Z8G5829BFJFH2AN9R5T80FJ931DX7GG")
)
self.assertTrue(
get_reserve_pub(
"0T096A11M57GWGG0P6ZM9Z8G5829BFJFH2AN9R5T80FJ931DX7GG other data"
)
)
self.assertFalse(get_reserve_pub("not a reserve public key"))
class PaytoParseTestCase(TestCase):
def test_payto_wrong_protocol(self):
self.assertRaises(PaytoFormatError, PaytoParse, "http://foo/bar")
def test_payto_with_port_number(self):
parsed = PaytoParse("payto://iban/localhost:1234/account")
self.assertEqual(parsed.bank, "localhost:1234")
def test_minimal(self):
parsed = PaytoParse("payto://x-taler-bank/bank-hostname/Taler")
def test_payto_malformed(self):
self.assertRaises(PaytoFormatError, PaytoParse, "payto:foo/bar")
def test_payto_noamount(self):
parsed = PaytoParse(
"payto://x-taler-bank/bank.int.taler.net/Exchange?message=0T096A11M57GWGG0P6ZM9Z8G5829BFJFH2AN9R5T80FJ931DX7GG"
)
def test_payto_parse(self):
parsed = PaytoParse(
"payto://x-taler-bank/bank.int.taler.net/Exchange?message=0T096A11M57GWGG0P6ZM9Z8G5829BFJFH2AN9R5T80FJ931DX7GG&amount=EUR:1"
)
self.assertEqual("Exchange", parsed.target)
self.assertEqual("0T096A11M57GWGG0P6ZM9Z8G5829BFJFH2AN9R5T80FJ931DX7GG", parsed.message)
self.assertEqual(parsed.amount.value, 1)
self.assertEqual(parsed.amount.fraction, 0)
self.assertEqual(parsed.amount.currency, "EUR")
self.assertEqual(parsed.authority, "x-taler-bank")
self.assertEqual(parsed.bank, "bank.int.taler.net")
class PublicAccountsTestCase(TestCase):
def setUp(self):
clear_db()
self.user = User.objects.create_user(username="Bank", password="Bank")
self.user.save()
self.user_bank_account = BankAccount(
account_no=100, is_public=True, user=self.user
)
self.user_bank_account.save()
def test_public_accounts(self):
self.assertTrue(User.objects.get(username="Bank"))
response = self.client.get(reverse("public-accounts", urlconf=urls))
class WithdrawTestCase(TestCase):
def setUp(self):
self.user_bank_account = BankAccount(
user=User.objects.create_user(
username="test_user", password="test_password"
),
account_no=100,
)
self.user_bank_account.save()
self.exchange_bank_account = BankAccount(
user=User.objects.create_user(username="test_exchange", password=""),
account_no=99,
)
self.exchange_bank_account.save()
self.client = Client()
@patch("talerbank.app.views.wire_transfer")
@patch("hashlib.new")
@patch("time.time")
@unittest.skip("skip outdated test case")
def test_withdraw(self, mocked_time, mocked_hashlib, mocked_wire_transfer):
amount = Amount(settings.TALER_CURRENCY, 0, 1)
params = {
"amount_value": str(amount.value),
"amount_fraction": str(amount.fraction),
"amount_currency": amount.currency,
"reserve_pub": "UVZ789",
"exchange": "https://exchange.example.com/",
"exchange_wire_details": "payto://x-taler-bank/bank.example/99",
}
self.client.login(username="test_user", password="test_password")
response = self.client.get(reverse("pin-question", urlconf=urls), params)
self.assertEqual(response.status_code, 200)
# We mock hashlib in order to fake the CAPTCHA.
hasher = MagicMock()
hasher.hexdigest = MagicMock()
hasher.hexdigest.return_value = "0"
mocked_hashlib.return_value = hasher
mocked_time.return_value = 0
response = self.client.post(
reverse("withdraw-confirm", urlconf=urls), {"pin_1": "0"}
)
args, kwargs = mocked_wire_transfer.call_args
del kwargs
self.assertTrue(
args[0].dump() == amount.dump()
and self.user_bank_account in args
and "UVZ789" in args
and self.exchange_bank_account in args
)
def tearDown(self):
clear_db()
class RegisterTestCase(TestCase):
"""User registration"""
def setUp(self):
clear_db()
BankAccount(user=User.objects.create_user(username="Bank")).save()
def tearDown(self):
clear_db()
def test_register(self):
setattr(settings, "ALLOW_REGISTRATIONS", True)
client = Client()
response = client.post(
reverse("register", urlconf=urls),
{"username": "test_register", "password": "test_register"},
follow=True,
)
self.assertIn(("/en/profile", 302), response.redirect_chain)
# this assertion tests "/profile""s view
self.assertEqual(200, response.status_code)
def test_register_headless(self):
setattr(settings, "ALLOW_REGISTRATIONS", True)
client = Client()
# Normal case.
response = client.post(
reverse("testing-withdraw-register", urlconf=urls),
content_type="application/json",
data={"username": "test_register_headless", "password": "password*+#@"},
)
self.assertEqual(200, response.status_code)
# Double-check account creation.
self.assertTrue(
self.client.login(
username="test_register_headless", password="password*+#@"
)
)
# Try registering unavailable username.
response = client.post(
reverse("testing-withdraw-register", urlconf=urls),
content_type="application/json",
data={"username": "test_register_headless", "password": "password"},
)
self.assertEqual(409, response.status_code)
# NOTE: Django 2.2.2 allows ANY character! Is this normal?
response = client.post(
reverse("testing-withdraw-register", urlconf=urls),
content_type="application/json",
data={"username": "'''+++;;;'''", "password": "password2"},
)
self.assertEqual(200, response.status_code)
class LoginTestCase(TestCase):
"""User login"""
def setUp(self):
BankAccount(
user=User.objects.create_user(
username="test_user", password="test_password"
)
).save()
self.client = Client()
def tearDown(self):
clear_db()
def test_login(self):
self.assertTrue(
self.client.login(username="test_user", password="test_password")
)
self.assertFalse(
self.client.login(username="test_user", password="test_passwordii")
)
def test_failing_login(self):
response = self.client.get(
reverse("history", urlconf=urls),
{"auth": "basic"},
HTTP_AUTHORIZATION=make_auth_line("Wrong", "Credentials"),
)
data = response.content.decode("utf-8")
self.assertEqual(401, response.status_code)
class AddIncomingTestCase(TestCase):
"""Test money transfer's API"""
def setUp(self):
BankAccount(
user=User.objects.create_user(
username="bank_user", password="bank_password"
)
).save()
BankAccount(
user=User.objects.create_user(
username="user_user", password="user_password"
)
).save()
def tearDown(self):
clear_db()
def test_add_incoming(self):
client = Client()
request_body = dict(
reserve_pub="TESTWTID",
amount=f"{settings.TALER_CURRENCY}:1.0",
debit_account="payto://x-taler-bank/bank_user",
)
response = client.post(
reverse("twg-add-incoming", urlconf=urls, args=["user_user"]),
data=json.dumps(request_body),
content_type="application/json",
follow=True,
HTTP_AUTHORIZATION=make_auth_line("user_user", "user_password"),
)
self.assertEqual(200, response.status_code)
class CustomDoesNotExistTestCase(TestCase):
def test_bankaccount_doesnotexist(self):
with self.assertRaises(BankAccount.DoesNotExist):
BankAccount.objects.get(account_no=1000)
with self.assertRaises(BankTransaction.DoesNotExist):
BankTransaction.objects.get(subject="1000")
class HistoryTestCase(TestCase):
def setUp(self):
clear_db()
debit_account = BankAccount(
user=User.objects.create_user(username="User", password="Password"),
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 100, 0)),
)
debit_account.save()
credit_account = BankAccount(
user=User.objects.create_user(username="User0", password="Password0")
)
credit_account.save()
for subject in ("a", "b", "c", "d", "e", "f", "g", "h", "i"):
wire_transfer(
Amount(settings.TALER_CURRENCY, 1, 0),
debit_account,
credit_account,
subject,
)
def tearDown(self):
clear_db()
def test_history(self):
def histquery(**urlargs):
response = self.client.get(
reverse("history", urlconf=urls),
urlargs,
HTTP_AUTHORIZATION=make_auth_line("User", "Password"),
)
return response
# test query #1
r = histquery(delta="-4", direction="both")
rd = json.loads(r.content)
self.assertEqual(r.status_code, 200)
# test query #2
r = histquery(delta="+1", start="5", direction="both")
self.assertEqual(r.status_code, 200)
rd = json.loads(r.content)
self.assertEqual(r.status_code, 200)
self.assertEqual(rd["data"][0]["row_id"], 6)
# test query #3
r = histquery(delta="+1", start="2", direction="both")
self.assertEqual(r.status_code, 200)
rd = json.loads(r.content)
self.assertEqual(rd["data"][0]["wt_subject"], "c")
# test query #4
r = histquery(delta="-1", start="2", direction="both")
self.assertEqual(r.status_code, 200)
rd = json.loads(r.content)
self.assertEqual(rd["data"][0]["wt_subject"], "a")
# test query #5
r = histquery(delta="1", start="11", direction="both")
self.assertEqual(r.status_code, 200)
rd = json.loads(r.content)
self.assertEqual(len(rd["data"]), 0)
class DBCustomColumnTestCase(TestCase):
def setUp(self):
BankAccount(user=User.objects.create_user(username="U")).save()
def tearDown(self):
clear_db()
def test_exists(self):
user_bankaccount = BankAccount.objects.get(user=User.objects.get(username="U"))
self.assertTrue(isinstance(user_bankaccount.balance, SignedAmount))
## This tests whether a bank account goes debit and then goes >=0
## again
class DebitTestCase(TestCase):
def setUp(self):
BankAccount(user=User.objects.create_user(username="U")).save()
BankAccount(user=User.objects.create_user(username="U0")).save()
def tearDown(self):
clear_db()
def test_green(self):
user_bankaccount = BankAccount.objects.get(user=User.objects.get(username="U"))
self.assertTrue(user_bankaccount.balance.is_zero())
def test_red(self):
user_bankaccount = BankAccount.objects.get(user=User.objects.get(username="U"))
user_bankaccount0 = BankAccount.objects.get(
user=User.objects.get(username="U0")
)
wire_transfer(
Amount(settings.TALER_CURRENCY, 10, 0),
user_bankaccount0,
user_bankaccount,
"Go green",
)
wire_transfer(
Amount(settings.TALER_CURRENCY, 11, 0),
user_bankaccount,
user_bankaccount0,
"Go red",
)
amt_one = SignedAmount.parse(f"{settings.TALER_CURRENCY}:1")
self.assertEqual(user_bankaccount.balance, -amt_one)
self.assertEqual(user_bankaccount0.balance, amt_one)
class MeasureHistory(TestCase):
def setUp(self):
self.user_bankaccount0 = BankAccount(
user=User.objects.create_user(username="U0"),
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 3000, 0)),
)
self.user_bankaccount0.save()
user_bankaccount = BankAccount(user=User.objects.create_user(username="U"))
user_bankaccount.save()
self.ntransfers = 1000
# Make sure logging level is WARNING, otherwise the loop
# will overwhelm the console.
for i in range(self.ntransfers):
del i # to pacify PEP checkers
wire_transfer(
Amount(settings.TALER_CURRENCY, 1, 0),
self.user_bankaccount0,
user_bankaccount,
"bulk",
)
def tearDown(self):
clear_db()
def test_extract_history(self):
# Measure the time extract_history() needs to retrieve
# ~ntransfers records.
timer = timeit.Timer(
stmt="extract_history(self.user_bankaccount0, False)",
setup="from talerbank.app.views import extract_history",
globals=locals(),
)
total_time = timer.timeit(number=1)
allowed_time_per_record = 0.003
self.assertLess(total_time, self.ntransfers * allowed_time_per_record)
class BalanceTestCase(TestCase):
def setUp(self):
self.the_bank = BankAccount(
user=User.objects.create_user(username="U0", password="U0PASS"),
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 3, 0)),
)
self.the_bank.save()
user = BankAccount(
user=User.objects.create_user(username="U"),
balance=SignedAmount(True, Amount(settings.TALER_CURRENCY, 10, 0)),
)
user.save()
# bank: 3, user: 10 (START).
# bank: 2, user: 11
wire_transfer(
Amount(settings.TALER_CURRENCY, 1, 0), self.the_bank, user, "mock"
)
# bank: 4, user: 9
wire_transfer(
Amount(settings.TALER_CURRENCY, 2, 0), user, self.the_bank, "mock"
)
# bank: -1, user: 14
wire_transfer(
Amount(settings.TALER_CURRENCY, 5, 0), self.the_bank, user, "mock"
)
# bank: 7, user: 6 (END)
wire_transfer(
Amount(settings.TALER_CURRENCY, 8, 0), user, self.the_bank, "mock"
)
# bank: -3, user: 16 (END)
wire_transfer(
Amount(settings.TALER_CURRENCY, 10, 0), self.the_bank, user, "mock"
)
self.client = Client()
def tearDown(self):
clear_db()
def test_balance(self):
self.client.login(username="U0", password="U0PASS")
response = self.client.get(
reverse("history", urlconf=urls),
{"delta": -30, "direction": "both", "account_number": 55},
HTTP_AUTHORIZATION=make_auth_line("U0", "U0PASS"),
)
data = response.content.decode("utf-8")
self.assertEqual(response.status_code, 200)
entries = json.loads(data)
acc_bal = SignedAmount(True, Amount(settings.TALER_CURRENCY, 10, 0))
for entry in reversed(entries["data"]):
if entry["sign"] == "-":
acc_bal += SignedAmount.parse(entry["amount"])
if entry["sign"] == "+":
acc_bal -= SignedAmount.parse(entry["amount"])
expected_amount = SignedAmount.parse(f"{settings.TALER_CURRENCY}:16.0")
self.assertEqual(acc_bal, expected_amount)

144
talerbank/app/urls.py Normal file
View File

@ -0,0 +1,144 @@
# This file is part of TALER
# (C) 2014, 2015, 2016 Taler Systems SA
#
# 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 Marcello Stanisci
# @author Florian Dold
from django.urls import include, path
from django.views.generic.base import RedirectView
from django.contrib.auth import views as auth_views
from django.conf.urls.i18n import i18n_patterns
from . import views
# These paths are part of the GNU Taler wire gatweay API
taler_wire_gateway_patterns = [
path("<str:acct_id>/", views.twg_base, name="twg-base"),
path("<str:acct_id>/config", views.twg_config, name="twg-config"),
path(
"<str:acct_id>/admin/add-incoming",
views.twg_add_incoming,
name="twg-add-incoming",
),
path(
"<str:acct_id>/history/incoming",
views.twg_history_incoming,
name="twg-history-incoming",
),
path(
"<str:acct_id>/history/outgoing",
views.twg_history_outgoing,
name="twg-history-outgoing",
),
path("<str:acct_id>/transfer", views.twg_transfer, name="twg-transfer"),
]
taler_bank_integration_api_patterns = [
path("api/config", views.api_config, name="api-config"),
path(
"api/withdrawal-operation/<str:withdraw_id>",
views.api_withdraw_operation,
name="api-withdraw-operation",
),
]
# These paths are part of the bank access API
taler_bank_access_api_patterns = [
path(
"accounts/<str:acct_id>",
views.bank_accounts_api_balance,
name="access-api-balance",
),
path(
"accounts/<str:acct_id>/withdrawals",
views.bank_accounts_api_create_withdrawal,
name="access-api-withdrawal",
),
path(
"accounts/<str:acct_id>/withdrawals/<str:wid>",
views.bank_accounts_api_get_withdrawal,
name="access-api-withdrawal-status",
),
path(
"accounts/<str:acct_id>/withdrawals/<str:wid>/confirm",
views.bank_accounts_api_confirm_withdrawal,
name="access-api-withdrawal-confirm",
),
path(
"accounts/<str:acct_id>/withdrawals/<str:wid>/abort",
views.bank_accounts_api_abort_withdrawal,
name="access-api-withdrawal-abort",
),
path("testing/withdraw", views.withdraw_headless, name="testing-withdraw"),
path("testing/register", views.register_headless, name="testing-withdraw-register"),
]
urlpatterns = [
path("", include(taler_bank_integration_api_patterns)),
path("", include(taler_bank_access_api_patterns)),
path("taler-wire-gateway/", include(taler_wire_gateway_patterns)),
path(
"abort-withdrawal/<str:withdraw_id>",
views.abort_withdrawal,
name="abort-withdrawal",
),
path("favicon.ico", views.ignore),
path("config", views.config_view, name="config"),
path("history", views.serve_history, name="history"),
path("payto-transfer", views.payto_transfer, name="payto-transfer"),
]
urlpatterns += i18n_patterns(
path(
"confirm-withdrawal/<str:withdraw_id>",
views.confirm_withdrawal,
name="withdraw-confirm",
),
path("start-withdrawal", views.start_withdrawal, name="start-withdrawal"),
path(
"show-withdrawal/<str:withdraw_id>", views.show_withdrawal, name="withdraw-show"
),
path("", RedirectView.as_view(pattern_name="profile"), name="index"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("profile", views.profile_page, name="profile"),
path("payto-form", views.payto_form, name="payto-form"),
path("wiretransfer-form", views.wiretransfer_form, name="wiretransfer-form"),
path(
"login",
auth_views.LoginView.as_view(
template_name="login.html",
authentication_form=views.TalerAuthenticationForm,
),
name="login",
),
path(
"public-accounts",
views.serve_public_accounts,
name="public-accounts",
),
path(
"public-accounts/<str:name>",
views.serve_public_accounts,
name="public-accounts",
),
path(
"public-accounts/<str:name>/<int:page>",
views.serve_public_accounts,
name="public-accounts",
),
)

1574
talerbank/app/views.py Normal file

File diff suppressed because it is too large Load Diff

180
talerbank/cli.py Normal file
View File

@ -0,0 +1,180 @@
##
# This file is part of TALER
# (C) 2017 Taler Systems SA
#
# 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
# @file CLI tool to manage all the bank's tasks.
import argparse
import django
import sys
import os
import os.path
import site
import logging
import inspect
import click
from taler.util.talerconfig import TalerConfig
from django.core.management import call_command
SITE_PACKAGES = os.path.abspath(os.path.dirname(__file__) + "/..")
LOGGER = logging.getLogger(__name__)
# No perfect match to our logging format, but good enough ...
UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talerbank.settings")
# Argument to tell uWSGI to load the python plugin.
# This hack is required, because on systems where the plugin is statically linked,
# loading it causes an error.
arg_load_python = "--if-not-plugin python --plugins python --endif".split(" ")
@click.group(help="Manager script of Taler bank.")
@click.pass_context
@click.option(
"--http-port",
help="Set HTTP as the serving protocol (taking precedence over the config.), and set the port.",
type=int,
)
@click.option("-c", "--config", help="Path to the config file.")
@click.option("--with-db", help="Database connection string.")
def cli(ctx, http_port, config, with_db):
if with_db:
os.environ.setdefault("TALER_BANK_ALTDB", with_db)
if config:
os.environ["TALER_CONFIG_FILE"] = config
ctx.obj = dict(http_port=http_port, config=config)
@cli.command(help="Serve the bank")
@click.pass_obj
def serve(obj):
# if --http-port option is found, then serve via HTTP.
# Otherwise serve on whatever protocol is specified in the config.
serve = "http"
if not obj["http_port"]:
TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
serve = TC["bank"]["serve"].value_string(required=True).lower()
if serve == "http":
return handle_serve_http(obj["http_port"])
handle_serve_uwsgi()
@cli.command(
name="django",
help="Invoke 'django' sub-commands",
context_settings=dict(ignore_unknown_options=True),
)
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
def handle_django(args):
django.setup()
# always run 'migrate' first, in case a virgin db is being used.
call_command("migrate")
django_index = sys.argv.index("django")
from django.core.management import execute_from_command_line
execute_from_command_line([sys.argv[0] + " django"] + sys.argv[django_index + 1 :])
def handle_serve_http(port):
import django
django.setup()
print("migrating")
call_command("migrate")
print("providing accounts")
call_command("provide_accounts")
print("checking")
call_command("check")
if port is None:
TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
port = TC["bank"]["http_port"].value_int(required=True)
httpspec = ":%d" % (port,)
params = [
"uwsgi",
"uwsgi",
*arg_load_python,
"--static-map",
"/static=%s/talerbank/app/static" % SITE_PACKAGES,
"--die-on-term",
"--no-orphans",
"--master",
"--http",
httpspec,
"--log-format",
UWSGI_LOGFMT,
"--module",
"talerbank.wsgi",
]
os.execlp(*params)
def handle_serve_uwsgi():
django.setup()
call_command("migrate")
call_command("provide_accounts")
call_command("check")
TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
serve_uwsgi = TC["bank"]["uwsgi_serve"].value_string(required=True).lower()
params = [
"uwsgi",
"uwsgi",
*arg_load_python,
"--static-map",
"/static=%s/talerbank/app/static" % SITE_PACKAGES,
"--die-on-term",
"--no-orphans",
"--master",
"--log-format",
UWSGI_LOGFMT,
"--module",
"talerbank.wsgi",
]
if serve_uwsgi == "tcp":
port = TC["bank"]["uwsgi_port"].value_int(required=True)
spec = ":%d" % (port,)
params.extend(["--socket", spec])
else:
spec = TC["bank"]["uwsgi_unixpath"].value_filename(required=True)
mode = TC["bank"]["uwsgi_unixpath_mode"].value_filename(required=True)
params.extend(["--socket", spec])
params.extend(["--chmod-socket=" + mode])
try:
os.makedirs(os.path.dirname(spec), exist_ok=True)
except FileNotFoundError:
print(f"{spec} is not a valid file path.")
sys.exit(1)
logging.info("launching uwsgi with argv %s", params[1:])
os.execlp(*params)
@cli.command(help="Print config values.")
def config():
TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
TC.dump()
def run():
cli()
if __name__ == "__main__":
cli()

190
talerbank/jinja2.py Normal file
View File

@ -0,0 +1,190 @@
##
# This file is part of TALER
# (C) 2017 Taler Systems SA
#
# 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
# @brief Extends the Jinja2 API with custom functions.
import os
import math
import json
from urllib.parse import urlparse
from django.urls import reverse, get_script_prefix
from django.conf import settings
from jinja2 import Environment
from django.utils.translation import gettext
import markupsafe
##
# Check if a URL is absolute or not.
#
# @param urloc the URL to check.
# @return True if the URL is absolute, False otherwise.
def is_absolute(urloc):
return bool(urlparse(urloc).netloc)
##
# Join URL components held in a list, taking care
# of not having double slashes in the result.
#
# @param parts list of URL components.
# @return the string made of the joined components
def join_urlparts(*parts):
ret = ""
part = 0
while part < len(parts):
buf = parts[part]
part += 1
if ret.endswith("/"):
buf = buf.lstrip("/")
elif ret and not buf.startswith("/"):
buf = "/" + buf
ret += buf
return ret
##
# Prefixes the argument with the location for static content.
#
# @param urloc the URL portion that should be prefixed; in
# other words, this will be in final position in the
# produced result.
# @return the URL that picks @a urloc from the location for
# static content.
def static(urloc):
if is_absolute(urloc):
return urloc
return join_urlparts(get_script_prefix(), settings.STATIC_URL, urloc)
##
# Helper function that fetches a value from the settings.
#
# @param name the name to lookup in the settings.
# @return @a name's value as defined in the settings, or
# a empty string otherwise.
def settings_value(name):
return getattr(settings, name, "")
##
# Fetch the URL given its "name".
#
# @param url_name URL's name as defined in urlargs.py
# @param kwargs key-value list that will be appended
# to the URL as the parameter=value pairs.
def url(url_name, *args, **kwargs):
# strangely, Django's 'reverse' function
# takes a named parameter 'kwargs' instead
# of real kwargs.
return reverse(url_name, args=args, kwargs=kwargs)
##
# Helper function that reads a value from the environment.
#
# @param name env value to read
# @return the value, or None if not found.
def env_get(name, default=None):
return os.environ.get(name, default)
##
# Jinja2 specific function used to activate the definitions
# of this module.
#
# @param options opaque argument (?) given from the caller.
# @return Jinja2-specific object that contains the new definitions.
def is_valid_amount(amount):
if math.isnan(amount.value):
return False
return True
def tojson(x):
"""Convert object to json"""
return json.dumps(x)
##
# Stringifies amount.
#
# @param amount amount object.
# @return amount pretty string.
def amount_stringify(amount):
return amount.stringify(settings.TALER_DIGITS, pretty=True)
def get_locale(url):
parts = url.split('/', 2)
if (2 >= len(parts)):
# Totally unexpected path format, do not localize
return "en"
lang = parts[1]
return lang
all_languages = {
"en": "English&nbsp;[en]",
"ar": "Arabic&nbsp;[ar]",
"zh_Hant": "Chinese&nbsp;[zh]",
"fr": "French&nbsp;[fr]",
"de": "German&nbsp;[de]",
"hi": "Hindi&nbsp;[hi]",
"it": "Italian&nbsp;[it]",
"ja": "Japanese&nbsp;[ja]",
"ko": "Korean&nbsp;[ko]",
"pt": "Portuguese&nbsp;[pt]",
"pt_BR": "Portuguese (Brazil)&nbsp;[pt_BR]",
"ru": "Russian&nbsp;[ru]",
"es": "Spanish&nbsp;[es]",
"sv": "Swedish&nbsp;[sv]",
"tr": "Turkish&nbsp;[tr]",
}
def context_processor(request):
def getlang():
return get_locale(request.path)
return dict(getlang=getlang)
def environment(**options):
env = Environment(**options)
gettext_markup = lambda *args, **kwargs: markupsafe.Markup(
gettext(*args, **kwargs)
)
env.globals.update({
"static": static,
"url": url,
"settings_value": settings_value,
"env": env_get,
"is_valid_amount": is_valid_amount,
"amount_stringify": amount_stringify,
"tojson": tojson,
"_": gettext_markup,
"gettext": gettext_markup,
"get_locale": get_locale,
"getactive": lambda: "bank",
"all_languages": all_languages,
})
return env

247
talerbank/settings.py Normal file
View File

@ -0,0 +1,247 @@
"""
Django settings for talerbank.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import base64
import logging
import os
import re
import sys
import urllib.parse
from taler.util.talerconfig import TalerConfig, ConfigurationError
LOGGER = logging.getLogger(__name__)
LOGGER.info(
"DJANGO_SETTINGS_MODULE: %s" % os.environ.get("DJANGO_SETTINGS_MODULE")
)
TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
# Build paths inside the project like this:
# os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
SECRET_KEY = os.environ.get("TALER_BANK_SECRET_KEY", None)
if not SECRET_KEY:
LOGGER.info(
"secret key not configured in"
" TALER_BANK_SECRET_KEY env variable,"
" generating random secret"
)
SECRET_KEY = base64.b64encode(os.urandom(32)).decode("utf-8")
# SECURITY WARNING: don't run with debug turned on in production!
if "demo" == os.environ.get("TALER_ENV_NAME"):
DEBUG = False
else:
DEBUG = True
ADMIN_ENABLED = False
ALLOWED_HOSTS = ["*"]
LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "index"
# Application definition
INSTALLED_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"talerbank.app",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"talerbank.app.middleware.ExceptionMiddleware",
"talerbank.app.middleware.DecompressionMiddleware",
]
TEMPLATES = [{
"BACKEND":
"django.template.backends.jinja2.Jinja2",
"DIRS": [
os.path.join(BASE_DIR, "talerbank/app/templates"),
],
"OPTIONS": {
"environment": "talerbank.jinja2.environment",
"context_processors": ["talerbank.jinja2.context_processor"],
},
}]
# Disable those, since they don't work with
# jinja2 anyways.
TEMPLATE_CONTEXT_PROCESSORS = []
WSGI_APPLICATION = "talerbank.wsgi.application"
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {}
DBNAME = TC.value_string("bank", "database", required=True)
DBNAME = os.environ.get("TALER_BANK_ALTDB", DBNAME)
# Tells Django to add a BigAutoField column to every table that
# doesn't have a primary key explicitly defined. Such column will
# then be the primary key.
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
if not DBNAME:
raise Exception("DB not specified (neither in config or as" "cli argument)")
LOGGER.info("dbname: %s" % DBNAME)
CHECK_DBSTRING_FORMAT = re.search(
r"[a-z]+:///[a-z]+([\?][a-z]+=[a-z/]+)?", DBNAME
)
if not CHECK_DBSTRING_FORMAT:
LOGGER.error(
"Bad db string given '%s', respect the format"
"'dbtype:///dbname'" % DBNAME
)
sys.exit(2)
DBCONFIG = {}
# Maybe trust the parsing from urlparse?
DB_URL = urllib.parse.urlparse(DBNAME)
if DB_URL.scheme not in ("postgres") or DB_URL.scheme == "":
LOGGER.error("DB '%s' is not supported" % DB_URL.scheme)
sys.exit(1)
if DB_URL.scheme == "postgres":
DBCONFIG["ENGINE"] = "django.db.backends.postgresql_psycopg2"
DBCONFIG["NAME"] = DB_URL.path.lstrip("/")
if not DB_URL.netloc:
P = urllib.parse.parse_qs(DB_URL.query)
if ("host" not in P) or P["host"] == "":
HOST = None
else:
HOST = P["host"][0]
else:
HOST = DB_URL.netloc
if HOST:
DBCONFIG["HOST"] = HOST # Sockets directory.
DATABASES["default"] = DBCONFIG
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME":
"django.contrib.auth.password_validation"
".UserAttributeSimilarityValidator"
},
{
"NAME": "django.contrib.auth.password_validation"
".MinimumLengthValidator"
},
{
"NAME": "django.contrib.auth.password_validation"
".CommonPasswordValidator"
},
{
"NAME":
"django.contrib.auth.password_validation"
".NumericPasswordValidator"
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = "/static/"
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "talerbank/app/static"),
]
STATIC_ROOT = None
ROOT_URLCONF = "talerbank.urls"
try:
TALER_CURRENCY = TC.value_string("taler", "currency", required=True)
except ConfigurationError as exc:
LOGGER.error(exc)
sys.exit(3)
TALER_MAX_DEBT = TC.value_string(
"bank", "MAX_DEBT", default="%s:50.0" % TALER_CURRENCY
)
TALER_MAX_DEBT_BANK = TC.value_string(
"bank", "MAX_DEBT_BANK", default="%s:0.0" % TALER_CURRENCY
)
TALER_DIGITS = TC.value_int("bank", "NDIGITS", default=2)
# Order matters
TALER_PREDEFINED_ACCOUNTS = [
"Bank",
"Exchange",
"blog",
"Tor",
"GNUnet",
"Taler",
"FSF",
"Tutorial",
"Survey",
]
TALER_EXPECTS_DONATIONS = ["Tor", "GNUnet", "Taler", "FSF"]
TALER_SUGGESTED_EXCHANGE = TC.value_string("bank", "suggested_exchange")
TALER_SUGGESTED_EXCHANGE_PAYTO = TC.value_string(
"bank", "suggested_exchange_payto"
)
_allow_reg = TC.value_string("bank", "ALLOW_REGISTRATIONS", default="no")
if _allow_reg.lower() == "yes":
ALLOW_REGISTRATIONS = True
else:
ALLOW_REGISTRATIONS = False
_show_freeform_withdrawal = TC.value_string(
"bank", "SHOW_FREEFORM_WITHDRAWAL", default="no"
)
if _show_freeform_withdrawal.lower() == "yes":
SHOW_FREEFORM_WITHDRAWAL = True
else:
SHOW_FREEFORM_WITHDRAWAL = False

21
talerbank/urls.py Normal file
View File

@ -0,0 +1,21 @@
"""
Taler Bank URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf.urls import url
from .app.urls import urlpatterns as app_urlpatterns
urlpatterns = staticfiles_urlpatterns() + app_urlpatterns

16
talerbank/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for bank_project project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talerbank.settings")
application = get_wsgi_application()