├── .gitignore
├── CONTRIBUTING.md
├── GNU_AFFERO_GPL_V3_19_Nov_1997.txt
├── Harmony_Individual_Contributor_Assignment_Agreement.txt
├── NOTICE
├── README.md
├── mvn.sh
├── mvn_clean.sh
├── pom.xml
├── project
├── build.properties
├── build.scala
└── plugins.sbt
└── src
├── main
├── resources
│ ├── .keep
│ ├── cert
│ │ ├── server.jks
│ │ └── server.trust.jks
│ ├── default.logback.xml
│ ├── i18n
│ │ ├── lift-core.properties
│ │ ├── lift-core_en_GB.properties
│ │ ├── lift-core_it_IT.properties
│ │ └── lift-core_uk_UA.properties
│ ├── props
│ │ └── sample.props.template
│ └── test.logback.xml
├── scala
│ ├── bootstrap
│ │ └── liftweb
│ │ │ └── Boot.scala
│ └── code
│ │ ├── Constant.scala
│ │ ├── lib
│ │ ├── OAuthClient.scala
│ │ ├── ObpAPI.scala
│ │ ├── ObpOAuthProvider.scala
│ │ └── SSLHelper.scala
│ │ ├── snippet
│ │ ├── AccountSettings.scala
│ │ ├── AccountsOverview.scala
│ │ ├── Comments.scala
│ │ ├── CreateBankAccount.scala
│ │ ├── CreateExpenditure.scala
│ │ ├── CreateIncome.scala
│ │ ├── CreatePermissionForm.scala
│ │ ├── CustomEditable.scala
│ │ ├── Dashboard.scala
│ │ ├── DashboardAccountOverview.scala
│ │ ├── Login.scala
│ │ ├── Management.scala
│ │ ├── Nav.scala
│ │ ├── PermissionManagement.scala
│ │ ├── TimeHelpers.scala
│ │ ├── TransactionsList.scala
│ │ ├── ViewsOverview.scala
│ │ └── WebUI.scala
│ │ ├── util
│ │ ├── ExceptionLogger.scala
│ │ ├── Helper.scala
│ │ └── Util.scala
│ │ └── widgets
│ │ └── CustomTableSorter.scala
└── webapp
│ ├── 404.html
│ ├── WEB-INF
│ └── web.xml
│ ├── about.html
│ ├── banks
│ └── star
│ │ └── accounts
│ │ ├── create-bank-account.html
│ │ └── star
│ │ ├── account-overview-dashboard.html
│ │ ├── create-expenditure.html
│ │ ├── create-income.html
│ │ ├── management.html
│ │ ├── permissions.html
│ │ ├── permissions
│ │ └── create.html
│ │ ├── settings.html
│ │ ├── star.html
│ │ ├── transactions
│ │ └── star
│ │ │ └── star.html
│ │ └── views
│ │ └── list.html
│ ├── correlated-user.html
│ ├── dd.html
│ ├── debug-info.html
│ ├── favicon.ico
│ ├── font-awesome
│ ├── css
│ │ ├── font-awesome.css
│ │ └── font-awesome.min.css
│ └── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ ├── help.html
│ ├── index.html
│ ├── list-accounts.html
│ ├── media
│ ├── css
│ │ ├── highlight.js.min.css
│ │ ├── jquery.dropdown.min.css
│ │ ├── normalize.css
│ │ ├── reset.css
│ │ ├── screen.css
│ │ ├── style.css
│ │ ├── toastr.min.css
│ │ └── website.css
│ ├── images
│ │ ├── OBP_full_web.png
│ │ ├── OBP_logo_simple.png
│ │ ├── OpenBankProject_logo.jpg
│ │ ├── add-off.png
│ │ ├── add-on.png
│ │ ├── ajax-loader.gif
│ │ ├── arrow-right.png
│ │ ├── blank.gif
│ │ ├── cancel.png
│ │ ├── close-icon.png
│ │ ├── comment.png
│ │ ├── edit-off.png
│ │ ├── edit-on.png
│ │ ├── favicon.ico
│ │ ├── feedback-button.png
│ │ ├── header.png
│ │ ├── home-table-enter-icon.png
│ │ ├── home-table-exit-icon.png
│ │ ├── home-table-paper-icon.png
│ │ ├── home-table-quote-icon.png
│ │ ├── login-icon.png
│ │ ├── logo-footer.png
│ │ ├── logo-header.png
│ │ ├── logo.png
│ │ ├── logout-icon.png
│ │ ├── money-in-icon.png
│ │ ├── money-out-icon.png
│ │ ├── moreInfo.png
│ │ ├── nav-item.png
│ │ ├── nav-selected.png
│ │ ├── nav.png
│ │ ├── polarize-logo.png
│ │ ├── remove.png
│ │ ├── select-arrow-home.png
│ │ ├── select-arrow.png
│ │ ├── settings-icon.png
│ │ ├── sort.png
│ │ ├── submit.png
│ │ ├── transaction-in.png
│ │ ├── transaction-out.png
│ │ ├── upload-off.png
│ │ └── upload-on.png
│ └── js
│ │ ├── highlight.min.js
│ │ ├── notifications.js
│ │ ├── polarize.js
│ │ ├── scripts.js
│ │ ├── toastr.min.js
│ │ ├── vendor
│ │ ├── jquery.dropdown.min.js
│ │ ├── jquery.js
│ │ └── jquery.tablesorter.min.js
│ │ ├── views.js
│ │ └── website.js
│ ├── my-accounts.html
│ ├── oauthcallback.html
│ └── templates-hidden
│ ├── _comment.html
│ ├── _dashboard_account.html
│ ├── _nav_account_settings.html
│ ├── _tag.html
│ ├── _transactionImage.html
│ └── default.html
└── test
├── resources
└── .keep
└── scala
├── LiftConsole.scala
├── RunMtlsWebApp.scala
└── RunWebApp.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.db
3 | .DS_Store
4 | *.scala.orig
5 | *.orig
6 | *.log
7 | .gitignore
8 | .idea
9 | .settings
10 | .classpath
11 | .project
12 | .cache
13 | src/main/resources/rebel.xml
14 | src/main/resources/props/*
15 | !src/main/resources/props/sample.props.template
16 | src/test/resources/props
17 | src/main/resources/git.properties
18 |
19 | src/main/webapp/WEB-INF/jetty.xml
20 | src/main/webapp/conf.html
21 | target/
22 | *.iml
23 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 |
4 | ## Hello!
5 |
6 | Thank you for your interest in contributing to the Open Bank Project!
7 |
8 | ## Pull requests
9 |
10 | If submitting a pull request please read and sign our [CLA](http://github.com/OpenBankProject/OBP-API/blob/develop/Harmony_Individual_Contributor_Assignment_Agreement.txt) and send it to contact@tesobe.com - We'll send you back a code to include in the comment section of subsequent pull requests.
11 |
12 | Please reference Issue Numbers in your commits.
13 |
14 | ## Code comments
15 |
16 | Please comment your code ! :-) Imagine an engineer is trying to fix a production issue: she is working on a tiny screen, via a dodgy mobile Internet connection, in a sandstorm - Your code is fresh in your mind. Your comments could help her!
17 |
18 | ## Issues
19 |
20 | If would like to report an issue or suggest any kind of improvement please use Github Issues.
21 |
22 | ## Licenses
23 |
24 | Open Bank Project API, API Explorer and Sofi are dual licenced under the AGPL and commercial licenses. Open Bank Project SDKs are licenced under Apache 2 or MIT style licences.
25 |
26 | Please see the NOTICE for each project licence.
27 |
28 | ## Setup and Tests
29 |
30 | See the README for instructions on setup :-)
31 |
32 | Welcome!
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Open Bank Project - Sofi Web Application
2 | Copyright (C) 2011, 2012, TESOBE GmbH.
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Affero General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU Affero General Public License for more details.
13 |
14 | You should have received a copy of the GNU Affero General Public License
15 | along with this program. If not, see .
16 |
17 | Email: contact@tesobe.com
18 | TESOBE GmbH.
19 | Osloer Str. 16/17
20 | Berlin 13359, Germany
21 |
22 | This product includes software developed at
23 | TESOBE (http://www.tesobe.com/)
24 | by
25 | Simon Redfern : simon AT tesobe DOT com
26 | Stefan Bethge : stefan AT tesobe DOT com
27 | Everett Sochowski : everett AT tesobe DOT com
28 | Ayoub Benali: ayoub AT tesobe DOT com
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Welcome to Sofit
2 | =====================
3 |
4 | # ABOUT
5 |
6 | The Sofit application demonstrates some of the social aims behind the Open Bank Project:
7 |
8 | 1) Sliding scale of privacy and disclosure. e.g. Use aliases to protect real names but still show the flow of money.
9 | 2) Different views on account data (Public / Share holders / Team etc.) e.g. hide balances on public view.
10 | 3) Comment on transactions
11 | 4) Add other metadata e.g. tags / images to transactions / payees.
12 |
13 |
14 | # LICENSE
15 |
16 | This project is licensed under the AGPL V3 (see NOTICE). Commercial licences are available from TESOBE GmbH.
17 |
18 | # SETUP
19 |
20 | The project is using sbt or Maven 2 as a build tool.
21 | See build.scala or pom.xml respectively for the dependencies.
22 |
23 | ----
24 |
25 | To compile and run jetty, cd into the root directory (where this file is) and run:
26 |
27 | $ sbt
28 | ...
29 | > compile
30 | > ~;container:start; container:reload /
31 |
32 | (Note that you first have to start sbt and then on its console start jetty with the container:start task, otherwise, it will exit immediately. More here: https://github.com/siasia/xsbt-web-plugin/wiki)
33 |
34 | In OS X, sbt can be installed with $ sudo port install sbt
35 |
36 | ----
37 |
38 |
39 | Alternatively, maven can also be used:
40 |
41 | mvn jetty:run
42 |
43 | Note: You may need to add the pluginGroup to the $HOME/.m2/settings.xml
44 |
45 |
49 | ...
50 |
51 | org.mortbay.jetty
52 |
53 | ...
54 |
55 |
56 | ---
57 |
58 | ## Ubuntu
59 |
60 | If you use Ubuntu (or a derivative) and encrypted home directories (e.g. you have ~/.Private), you might run into the following error when the project is built:
61 |
62 | uncaught exception during compilation: java.io.IOException
63 | [ERROR] File name too long
64 | [ERROR] two errors found
65 | [DEBUG] Compilation failed (CompilerInterface)
66 |
67 | The current workaround is to move the project directory onto a different partition, e.g. under /opt/ .
68 |
69 | ## To run Sofit with IntelliJ IDEA
70 |
71 | 1. Make sure you have the IntelliJ Scala plugin installed.
72 | 2. Create a new folder e.g. OpenBankProject and cd there
73 | 3. git clone https://github.com/OpenBankProject/Sofit.git
74 | 4. In IntelliJ IDEA do File -> New -> Project from existing sources, navigate to the folder, and select pom.xml
75 | 5. If you see a message about an unmanaged pom.xml, click the option to let Maven manage it.
76 | 6. There is a props file template provided at src/main/resource/prop/sample.props.template. It needs to be renamed and modified or just copy and paste with a new file name for the application to work.
77 | 7.These change will be performe in default.props file (/src/main/resources/props/default.props).
78 |
79 | ## PROPS FILE
80 |
81 | 1. Copying and renaming:
82 |
83 | We suggest you copy and paste the sample.props.template file so you can refer to it later.
84 | The active Props file should be named *default.props* or another name that Liftweb accepts. See https://www.assembla.com/wiki/show/liftweb/Properties for the various options.
85 |
86 | Below are the important Props (see example.props.tempate for more info)
87 |
88 | ### *base_url*
89 |
90 | The base_url is used to calculate the callback url to give to the Open Bank Project API server. This should just be the
91 | base url used to access the social finance application. So if you're running a copy of the Sofit application at
92 | sofit.example.com over https, on the standard port, it would be "https://sofit.example.com".
93 | The suggested value for local development is: http://localhost:8081 but obviously you can choose a different non conflicting host:port
94 |
95 | ### *api_hostname*
96 |
97 | The api_hostname should be the base url (e.g. https://api.openbankproject.com) of the Open Bank Project API. \
98 | If Sofit is running locally then define api_hostname as http://127.0.0.1:8080 in your props file.
99 |
100 | ### *obp_consumer_key* \
101 | ### *obp_secret_key*
102 |
103 | You will need to get a consumer key and secret but registering a Consumer / Get API Key on the OBP API.
104 | See the README of OBP-API if you want to run OBP-API locally.
105 |
106 |
107 | #OBP API Setup
108 |
109 | Sofit needs some data setup for each User (Each user has their own bank and the user can create historical transactions at that bank)
110 | This is defined in OBP API function def sofitInitAction which runs after each user login.
111 | Sofit is also relying on using some Consumer Scopes (Entitlements that are set at the Consumer level not User level)
112 |
113 | Thus you will need to set the following *OBP API Props* (not Sofit!)
114 | sofit.logon_init_action.enabled=true
115 | allow_entitlements_or_scopes=true
116 |
117 | Also you will need to give your *Sofit Consumer* the following *Scopes* (using the OBP API Create Scope endpoint)
118 |
119 | CanCreateCustomerAtAnyBank
120 | CanCreateUserCustomerLinkAtAnyBank
121 |
122 | TODO: Consider extending sofit.login_init_action and grant CanCreateCustomer (at one bank) and CanCreateUserCustomerLink (at one bank) instead of relying on Scopes
123 |
124 |
--------------------------------------------------------------------------------
/mvn.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export MAVEN_OPTS="-Xmx512m -Xms512m -XX:MaxPermSize=256m"
4 |
5 | mvn $*
6 |
--------------------------------------------------------------------------------
/mvn_clean.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | mvn clean
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | sbt.version=0.13.9
3 |
--------------------------------------------------------------------------------
/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 | import com.earldouglas.xwp._
4 | import com.earldouglas.xwp.WebappPlugin
5 | import com.earldouglas.xwp.ContainerPlugin.autoImport._
6 |
7 |
8 | object LiftProjectBuild extends Build {
9 | override lazy val settings = super.settings ++ buildSettings
10 |
11 | lazy val buildSettings = Seq(
12 | organization := pom.groupId,
13 | version := pom.version
14 | )
15 |
16 | lazy val openBank = Project(
17 | pom.artifactId,
18 | base = file("."),
19 | settings = defaultSettings ++ pom.settings)
20 | .enablePlugins(JettyPlugin)
21 |
22 | object pom {
23 |
24 | val pomFile = "pom.xml"
25 | lazy val pom = xml.XML.loadFile(pomFile)
26 |
27 | lazy val pomProperties = (for{
28 | props <- (pom \ "properties")
29 | p <- props.child
30 | } yield {
31 | p.label -> p.text
32 | }).toMap
33 |
34 | private lazy val PropertiesExpr = """.*\$\{(.*?)\}.*""".r
35 |
36 | def populateProps(t: String) = t match {
37 | case PropertiesExpr(p) => {
38 | val replaceWith = pomProperties.get(p)
39 | t.replace("${"+p+"}", replaceWith.getOrElse(throw new Exception("Cannot find property: '" + p + "' required by: '" + t + "' in: " + pomFile)))
40 | }
41 | case _ => t
42 | }
43 |
44 | lazy val pomDeps = (for{
45 | dep <- pom \ "dependencies" \ "dependency"
46 | } yield {
47 | val scope = (dep \ "scope")
48 | val groupId = (dep \ "groupId").text
49 | val noScope = populateProps(groupId) % populateProps((dep \ "artifactId").text) % populateProps((dep \ "version").text)
50 | val nonCustom = if (scope.nonEmpty) noScope % populateProps(scope.text)
51 | else noScope
52 |
53 | if (groupId.endsWith("jetty")) Seq(noScope % "container", nonCustom) //hack to add jetty deps in container scope as it is required by the web plugin
54 | else Seq(nonCustom)
55 | }).flatten
56 |
57 | lazy val pomRepos = for {
58 | rep <- pom \ "repositories" \ "repository"
59 | } yield {
60 | populateProps((rep \ "url").text) at populateProps((rep \ "url").text)
61 | }
62 |
63 | lazy val pomScalaVersion = (pom \ "properties" \ "scala.compiler").text
64 |
65 | lazy val artifactId = (pom \ "artifactId").text
66 | lazy val groupId = (pom \ "groupId").text
67 | lazy val version = (pom \ "version").text
68 | lazy val name = (pom \ "name").text
69 |
70 | lazy val settings = Seq(
71 | scalaVersion := pomScalaVersion,
72 | libraryDependencies ++= pomDeps,
73 | resolvers ++= pomRepos,
74 | containerPort := 8081
75 | )
76 |
77 | }
78 |
79 | lazy val defaultSettings = Defaults.defaultSettings ++ Seq(
80 | name := pom.name,
81 | resolvers ++= Seq(
82 | "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases",
83 | "Java.net Maven3 Repository" at "http://download.java.net/maven/3/",
84 | "Scala-Tools Dependencies Repository for Releases" at "http://scala-tools.org/repo-releases",
85 | "Scala-Tools Dependencies Repository for Snapshots" at "http://scala-tools.org/repo-snapshots"),
86 |
87 | // compile options
88 | scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-Xmax-classfile-name", "78"),
89 | javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
90 |
91 | // show full stack traces
92 | testOptions in Test += Tests.Argument("-oF")
93 | )
94 |
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers += Classpaths.typesafeResolver
2 |
3 | // addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse" % "1.5.0")
4 |
5 | // resolvers += "sbt-deploy-repo" at "http://reaktor.github.com/sbt-deploy/maven"
6 | // addSbtPlugin("fi.reaktor" %% "sbt-deploy" % "0.3.1-SNAPSHOT")
7 |
8 |
9 | //xsbt-web-plugin
10 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.1.0")
11 |
12 | //resolvers += "Web plugin repo" at "http://siasia.github.com/maven2"
13 | //libraryDependencies <+= sbtVersion(v => v match {
14 | //case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8"
15 | //case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10"
16 | //case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11"
17 | //case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1"
18 | //})
19 |
20 | //sbteclipse
21 | resolvers += {
22 | val typesafeRepoUrl = new java.net.URL("http://repo.typesafe.com/typesafe/releases")
23 | val pattern = Patterns(false, "[organisation]/[module]/[sbtversion]/[revision]/[type]s/[module](-[classifier])-[revision].[ext]")
24 | Resolver.url("Typesafe Repository", typesafeRepoUrl)(pattern)
25 | }
26 |
27 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
28 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")
29 |
30 | //sbt-idea
31 | resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
32 |
33 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")
34 |
--------------------------------------------------------------------------------
/src/main/resources/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/.keep
--------------------------------------------------------------------------------
/src/main/resources/cert/server.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/cert/server.jks
--------------------------------------------------------------------------------
/src/main/resources/cert/server.trust.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/resources/cert/server.trust.jks
--------------------------------------------------------------------------------
/src/main/resources/default.logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/i18n/lift-core.properties:
--------------------------------------------------------------------------------
1 | login = Login
2 | logout = Logout
3 | views = Views
4 | private_accounts = Private accounts
5 | public_accounts = Public accounts
6 | my_accounts = My accounts
7 | account_name = Account Name
8 | you_are_logged_out = You are logged out. No authorised accounts available.
9 | enter_label = Enter a label...
10 | create_account_label = Create new account
11 | bank_name = Bank name
12 | #account_label = Account label
13 | account_label = Account name
14 | income_description = Description
15 | income_amount = Amount \u20ac
16 | payment_description = Description
17 | payment_amount = Amount \u20ac
18 | payment_category = Category
19 | income = Income
20 | payment = Expenditure
21 | expenditure = Expenditure
22 | add_expenditure = Add expenditure
23 | add_income = Add income
24 | button.save = Save
25 | button_cancel = Cancel
26 | button_show = Show
27 | api_documentation = API Documentation
28 | forgotten_password = Forgotten password?
29 | change_password = Change password
30 | new_label=Label
31 | expenditure.tags=Other,\
32 | Telco&Internet,\
33 | Family care,\
34 | Insurance,\
35 | Health,\
36 | Restaurants,\
37 | Spare time,\
38 | Off line shopping,\
39 | Transport,\
40 | Rent and house,\
41 | On-line shopping,\
42 | Groceries
43 | income.tags=Salary,\
44 | Transfer,\
45 | Refund,\
46 | Cash income,\
47 | Pensions and yields,\
48 | Extra and gifts
49 | months_ago = Period
50 | about = About
51 | about_info = Welcome to the IncludiMI App. IncludiMI is an innovative project which aims to provide financial identity to the underbanked. IncludiMI is led by Experian, Associazione Microfinanza e Sviluppo ONLUS, Associazione MicroLab and TESOBE.
52 | help_info = If you wish to access or amend any Personal Data we hold about you, or to request that we delete, export or transfer any information about you, you may email us with your request at dpoitaly@experian.com
53 | help = Help
54 | balance = Balance
55 | balance_colon = Balance:
56 | add_tag = ADD TAG
57 | add_image = ADD IMAGE
58 | add_comment = ADD COMMENT
59 | images = Images
60 | add_a_new_image = Add a new iage
61 | tags = Tags
62 | comments = Comments
63 | add_a_comment = Add a comment here
64 | months_ago_list = 1::month,2::months,3::months,4::months,5::months,6::months,7::months,8::months,9::months,10::months,11::months,12::months
65 | profile_completeness = Profile completeness
66 | credit_score = Credit score
67 | source_code_text = The source code for this application is available here:
68 | debug_info_text = Data we store related to your account includes the following:
69 |
--------------------------------------------------------------------------------
/src/main/resources/i18n/lift-core_en_GB.properties:
--------------------------------------------------------------------------------
1 | login = Login
2 | logout = Logout
3 | views = Views
4 | private_accounts = Private accounts
5 | public_accounts = Public accounts
6 | my_accounts = My accounts
7 | account_name = Account Name
8 | you_are_logged_out = You are logged out. No authorised accounts available.
9 | enter_label = Enter a label...
10 | create_account_label = Create an account
11 | bank_name = Bank name
12 | account_label = Account name
13 | income_description = Description
14 | income_amount = Amount \u20ac
15 | payment_description = Description
16 | payment_amount = Amount \u20ac
17 | income = Income
18 | payment = Expenditure
19 | add_expenditure = Add expenditure
20 | button.save = Save
21 | api_documentation = API Documentation
22 | forgotten_password = Forgotten password?
23 | change_password = Change password
24 | new_label=Label
25 | expenditure.tags=Other,\
26 | Telco&Internet,\
27 | Family care,\
28 | Insurance,\
29 | Health,\
30 | Restaurants,\
31 | Spare time,\
32 | Off line shopping,\
33 | Transport,\
34 | Rent and house,\
35 | On-line shopping,\
36 | Groceries
37 | income.tags=Salary,\
38 | Transfer,\
39 | Refund,\
40 | Cash income,\
41 | Pensions and yields,\
42 | Extra and gifts
--------------------------------------------------------------------------------
/src/main/resources/i18n/lift-core_it_IT.properties:
--------------------------------------------------------------------------------
1 | login = Accedi
2 | logout = Esci
3 | views = Visualizzazioni
4 | private_accounts = Conti privati
5 | public_accounts = Conti pubblici
6 | my_accounts = I miei registri
7 | account_name = Nome utente
8 | you_are_logged_out = Sei disconnesso. Nessun account autorizzato disponibile.
9 | enter_label = Crea un tag
10 | create_account_label = Crea un conto in banca
11 | bank_name = nome della banca
12 | account_label = Etichetta dell'account
13 | income_description = Descrizione del reddito
14 | income_amount = Importo del reddito
15 | payment_description = Descrizione
16 | payment_amount = Importo del pagamento
17 | payment_category = Categoria
18 | income = Reddito
19 | payment = Pagamento
20 | expenditure = Spesa
21 | add_expenditure = Aggiungi una spesa
22 | add_income = Aggiungi un'entrata
23 | button.save = Salva
24 | button_cancel = Annulla
25 | button_show = Vedi
26 | api_documentation = Documentazione API
27 | forgotten_password = Hai dimenticato la password?
28 | change_password = Cambia la password
29 | expenditure.tags=Spesa alimentari,\
30 | Telefono e Internet,\
31 | Spese familiari,\
32 | Assicurazioni,\
33 | Spese mediche,\
34 | Ristorante,\
35 | Tempo libero,\
36 | Acquisiti in negozio,\
37 | Trasporti,\
38 | Casa,\
39 | Acquisti on line,\
40 | Extra e imprevisti
41 | income.tags=Stipendio,\
42 | Bonifico,\
43 | Rimborso spese,\
44 | Entrate in contanti,\
45 | Rendita e pensione,\
46 | Extra e regali
47 | months_ago = Periodo
48 | about = Chi siamo
49 | help_info = Se desiderate accedere o modificare i vostri dati personali o richiedere la cancellazione, l'esportazione o il trasferimento delle informazioni che vi riguardano, potete inviarci un'e-mail con la vostra richiesta a gdpr@microfinanza.it
50 | balance = Bilancio
51 | balance_colon = Bilancio:
52 | add_tag = aggiungi un tag
53 | add_image = aggiungi un'immagine
54 | add_comment = aggiungi un commento
55 | images = Immagini
56 | add_a_new_image = aggiungi una nuova immagine
57 | tags = Tag
58 | comments = Commenti
59 | add_a_comment = Aggiungi un commento qui
60 | months_ago_list = 1::mese,2::mesi,3::mesi,4::mesi,5::mesi,6::mesi,7::mesi,8::mesi,9::mesi,10::mesi,11::mesi,12::mesi
61 | profile_completeness = Completezza del profilo
62 | credit_score = Punteggio di credito
--------------------------------------------------------------------------------
/src/main/resources/i18n/lift-core_uk_UA.properties:
--------------------------------------------------------------------------------
1 | # The main drawback here is that all application resources for a given locale must exist
2 | # in the same file, and that extended characters such as œ aren’t properly encoded by
3 | # default. This is due to Java properties bundles using ISO 8859-1 encoding and thus
4 | # requiring conversion to escaped format, such as \u0153. Fortunately this process can
5 | # be automated using tools like native2ascii
6 | # https://native2ascii.net/
7 | # This tool will allow you to convert national language characters to and from their Unicode equivalents in plain ASCII text.
8 | login = \u0443\u0432\u0456\u0439\u0442\u0438
9 | logout = \u0412\u0438\u0439\u0442\u0438
10 | views = \u041f\u0435\u0440\u0435\u0433\u043b\u044f\u0434\u0438
11 | private_accounts = \u041f\u0440\u0438\u0432\u0430\u0442\u043d\u0456 \u0440\u0430\u0445\u0443\u043d\u043a\u0438
12 | public_accounts = \u041f\u0443\u0431\u043b\u0456\u0447\u043d\u0456 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0456 \u0437\u0430\u043f\u0438\u0441\u0438
13 | my_accounts = \u041c\u043e\u0457 \u0440\u0430\u0445\u0443\u043d\u043a\u0438
14 | account_name = \u041d\u0430\u0437\u0432\u0430 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443
15 | you_are_logged_out = \u0412\u0438 \u0432\u0438\u0439\u0448\u043b\u0438 \u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u0438. \u041d\u0435\u043c\u0430\u0454 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0438\u0445 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u0438\u0445 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u0456\u0432.
16 | enter_label = \u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043c\u0456\u0442\u043a\u0443 ...
17 | create_account_label = \u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0430\u043a\u043a\u0430\u0443\u043d\u0442
18 | bank_name = \u043d\u0430\u0437\u0432\u0430 \u0431\u0430\u043d\u043a\u0443
19 | account_label = \u041c\u0456\u0442\u043a\u0430 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443
20 | income_description = \u041e\u043f\u0438\u0441
21 | income_amount = \u0421\u0443\u043c\u0430 \u20ac
22 | payment_description = \u041e\u043f\u0438\u0441
23 | payment_amount = \u0421\u0443\u043c\u0430 \u20ac
24 | income = \u0414\u043e\u0445\u0456\u0434
25 | payment = \u0412\u0438\u0442\u0440\u0430\u0442\u0438
26 | add_expenditure = \u0414\u043e\u0434\u0430\u0439\u0442\u0435 \u0432\u0438\u0442\u0440\u0430\u0442\u0438
27 | button.save = \u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438
28 | api_documentation = \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u044f API
29 | forgotten_password = \u0417\u0430\u0431\u0443\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c?
30 | change_password = \u0417\u043c\u0456\u043d\u0438\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u044c
31 | expenditure.tags=\u0411\u0430\u043a\u0430\u043b\u0456\u044f,\
32 | \u0422\u0435\u043b\u0435\u043a\u043e\u043c&\u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442,\
33 | \u0421\u0456\u043c\u0435\u0439\u043d\u0438\u0439 \u0434\u043e\u0433\u043b\u044f\u0434,\
34 | \u0421\u0442\u0440\u0430\u0445\u0443\u0432\u0430\u043d\u043d\u044f,\
35 | \u0417\u0434\u043e\u0440\u043e\u0432'\u044f,\
36 | \u0420\u0435\u0441\u0442\u043e\u0440\u0430\u043d\u0438,\
37 | \u0412\u0456\u043b\u044c\u043d\u0438\u0439 \u0447\u0430\u0441,\
38 | \u041f\u043e\u043a\u0443\u043f\u043a\u0438 \u043e\u0444\u043b\u0430\u0439\u043d,\
39 | \u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442,\
40 | \u041e\u0440\u0435\u043d\u0434\u0430 \u0442\u0430 \u0431\u0443\u0434\u0438\u043d\u043e\u043a,\
41 | \u0406\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0438,\
42 | \u0406\u043d\u0448\u0438\u0439
43 | income.tags=\u0417\u0430\u0440\u043e\u0431\u0456\u0442\u043d\u0430 \u043f\u043b\u0430\u0442\u0430,\
44 | \u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430,\
45 | \u0412\u0456\u0434\u0448\u043a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f,\
46 | \u0413\u0440\u043e\u0448\u043e\u0432\u0438\u0439 \u0434\u043e\u0445\u0456\u0434,\
47 | \u041f\u0435\u043d\u0441\u0456\u0457 \u0442\u0430 \u0432\u0440\u043e\u0436\u0430\u0457,\
48 | \u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u043e \u0456 \u043f\u043e\u0434\u0430\u0440\u0443\u043d\u043a\u0438
--------------------------------------------------------------------------------
/src/main/resources/props/sample.props.template:
--------------------------------------------------------------------------------
1 | #this is a sample props file you should edit and rename
2 | #see https://www.assembla.com/wiki/show/liftweb/Properties for all the naming options, or just use "default.props" in this same folder
3 |
4 |
5 | ######################################
6 | ## REQUIRED ##########################
7 |
8 | # The base url of the api to use (e.g. https://YOUR-OBP-API-HOST.com or http://127.0.0.1:8080 etc.)
9 | api_hostname=FILL_ME_IN
10 |
11 | # At the moment Sofit doesn't have the ability select multiple user auth providers
12 | # In most cases, this should just be identical to "api_hostname"
13 | # If not filled in you will get a page of red box errors!
14 | defaultAuthProvider=FILL_ME_IN
15 |
16 | # These are the oauth keys obtained from the api (at /consumer-registration)
17 | obp_consumer_key=FILL_ME_IN
18 | obp_secret_key=FILL_ME_IN
19 |
20 | # The base url / redirect url of the of this application
21 | # This is used for the oauth callback url.
22 | # When you register your consumer key for this application on the API, use this for redirect_url
23 | # Note: If you are running a local API instance and a local Sofit instance on the same machine, we *strongly* recommend that you call the API on 127.0.0.1 in your browser and Sofi on localhost to avoid weird cookie issues
24 | # Suggested value for local testing is: http://localhost:8081
25 | base_url=FILL_ME_IN
26 |
27 | ## End of REQUIRED ###################
28 | ######################################
29 |
30 |
31 | #this is only useful for running the api locally via RunWebApp
32 | #if you use it, make sure this matches your base_url port!
33 | #if you want to change the port when running via the command line, use "mvn -Djetty.port=8081 jetty:run" instead
34 | dev.port=8081
35 |
36 |
37 |
38 | #Your transloadit auth key (used to upload transaction images)
39 | #not needed unless you want to upload images
40 | transloadit.authkey=FILL_ME_IN
41 |
42 | #Your transloadit template used to process transaction image uploading
43 | #not needed unless you want to upload images
44 | transloadit.addImageTemplate=FILL_ME_IN
45 |
46 |
47 | # Link to the API Explorer
48 | webui_api_explorer_url=
49 |
50 | # API version
51 | # Default value is v4.0.0
52 | api_version=v4.0.0
53 |
54 | # Sngle Sign On
55 | # sso.enabled=false
56 | # Left logo at the header
57 | webui_header_logo_left_url=/media/images/logo-header.png
58 |
59 | ### OBP-API mode ##############################
60 | # If OBP-API split to two instances, eg: apis,portal
61 | # Then API_Explorer need to set two api hosts: api_hostname and this api_portal_hostname, for all Rest Apis will call api_hostname
62 | # but for all the portal home page link, we need to use this props. If do not set this, it will use api_hostname value instead.
63 | # api_portal_hostname=http://127.0.0.1:8080
64 | # --------------------------------------------
65 |
66 | # Enable/Disable features of the application
67 | management.counterparties.enabled=true
68 | management.views.enabled=true
69 | management.users.enabled=true
70 | management.create_account.enabled=false
71 | management.update_account_label.enabled=false
72 |
73 | # Limit the System views displayed by Sofit.
74 | sytems_views_to_display=owner,accountant,auditor
75 |
76 | # Limit the System views displayed by Sofit.
77 | hide_public_accounts_panel=false
78 |
79 | # Show/Hide Twitter link at the footer.
80 | display_twitter_link=true
81 |
82 | # Show/Hide GitHub link at the footer.
83 | display_github_link=true
84 |
85 | # Show/Hide API documentation link at the footer.
86 | display_api_docs_link=true
87 |
88 | # Set Locale
89 | language_tag = en-GB
90 |
91 | # Incoming account at manual_transaction_bank_id
92 | incoming.account_id=OBP_DEFAULT_INCOMING_ACCOUNT_ID
93 |
94 | # Outgoing account at manual_transaction_bank_id
95 | outgoing.account_id=OBP_DEFAULT_OUTGOING_ACCOUNT_ID
96 |
97 | # Display link to the counterpart account
98 | display_other_account_link_at_transaction=true
99 |
100 | # Help page documentation link
101 | documentation_link = FILL_ME_IN
102 |
103 | # If you want to make the Lift inactivity timeout shorter than
104 | # the container inactivity timeout, set the inactivity timeout here
105 | session_inactivity_timeout_in_minutes = 30
106 |
107 | # EULA
108 | eula_url=https://www.example.com
109 | eula_text=End User Licence Agreement & Privacy Policy
110 |
111 | # Source coe url
112 | source_code_url=https://github.com/OpenBankProject/Sofit
--------------------------------------------------------------------------------
/src/main/resources/test.logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{yyyy-MM-dd HH:mm:ss} %t %c{0} [%p] %m%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/scala/code/Constant.scala:
--------------------------------------------------------------------------------
1 | package code
2 |
3 | import net.liftweb.util.Props
4 |
5 | object Constant {
6 | final val CUSTOM_OWNER_VIEW_ID = "owner"
7 | final val versionOfApi = Props.get("api_version").getOrElse("v4.0.0")
8 | final val versionOfApi121 = "v1.2.1"
9 | final val correlatedUserIdTargetCookieName = "CORRELATED_USER_ID_TARGET"
10 | final val correlatedUserIdBoundCookieName = "CORRELATED_USER_ID_BOUND"
11 | final val correlatedCustomerIdCreatedCookieName = "CORRELATED_CUSTOMER_ID_CREATED"
12 | final val linkBetweenCorrelatedUserAndCustomerCreatedCookieName = "LINK_BETWEEN_CORRELATED_USER_AND_CUSTOMER_CREATED"
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/code/lib/OAuthClient.scala:
--------------------------------------------------------------------------------
1 | /**
2 | Open Bank Project - Sofi Web Application
3 | Copyright (C) 2011 - 2021, TESOBE GmbH.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Affero General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Affero General Public License for more details.
14 |
15 | You should have received a copy of the GNU Affero General Public License
16 | along with this program. If not, see .
17 |
18 | Email: contact@tesobe.com
19 | TESOBE GmbH.
20 | Osloer Str. 16/17
21 | Berlin 13359, Germany
22 |
23 | This product includes software developed at
24 | TESOBE (http://www.tesobe.com/)
25 | by
26 | Simon Redfern : simon AT tesobe DOT com
27 | Stefan Bethge : stefan AT tesobe DOT com
28 | Everett Sochowski : everett AT tesobe DOT com
29 | Ayoub Benali: ayoub AT tesobe DOT com
30 |
31 | */
32 |
33 | package code.lib
34 |
35 | import code.util.Helper
36 | import code.util.Helper.MdcLoggable
37 | import net.liftweb.common.{Box, Empty, Failure, Full}
38 | import net.liftweb.http.{LiftResponse, S, SessionVar}
39 | import net.liftweb.util.Props
40 | import oauth.signpost.{OAuthConsumer, OAuthProvider}
41 | import oauth.signpost.basic.DefaultOAuthConsumer
42 | import oauth.signpost.signature.HmacSha256MessageSigner
43 |
44 | sealed trait Provider {
45 | val name : String
46 |
47 | val apiBaseUrl : String
48 | val requestTokenUrl : String
49 | val accessTokenUrl : String
50 | val authorizeUrl : String
51 | val signupUrl : Option[String]
52 |
53 | /**
54 | * Can't do oAuthProvider = new DefaultOAuthProvider(requestTokenUrl, accessTokenUrl, authorizeUrl)
55 | * here as the Strings all evaluate at null at this point in object creation
56 | */
57 | val oAuthProvider : OAuthProvider
58 |
59 | val consumerKey : String
60 | val consumerSecret : String
61 | }
62 |
63 | trait DefaultProvider extends Provider {
64 | val name = "The Open Bank Project Demo"
65 |
66 | val baseUrl = Props.get("api_hostname", S.hostName)
67 | val oauthBaseUrlPortal = Props.get("api_portal_hostname").getOrElse(baseUrl)
68 | val apiBaseUrl = baseUrl + "/obp"
69 | val requestTokenUrl = baseUrl + "/oauth/initiate"
70 | val accessTokenUrl = baseUrl + "/oauth/token"
71 | val authorizeUrl = oauthBaseUrlPortal + "/oauth/authorize"
72 | val signupUrl = Some(baseUrl + "/user_mgt/sign_up")
73 |
74 | lazy val oAuthProvider : OAuthProvider = new ObpOAuthProvider(requestTokenUrl, accessTokenUrl, authorizeUrl)
75 |
76 | val consumerKey = Props.get("obp_consumer_key", "")
77 | val consumerSecret = Props.get("obp_secret_key", "")
78 | }
79 |
80 | object OBPDemo extends DefaultProvider
81 |
82 | object AddBankAccountProvider extends DefaultProvider {
83 | override val name = "The Open Bank Project Demo - Add Bank Account"
84 |
85 | //The "login" prefix before /oauth means that we will use the oauth flow that will ask the user
86 | //to connect a bank account
87 | override val requestTokenUrl = baseUrl + "/login/oauth/initiate"
88 | override val accessTokenUrl = baseUrl + "/login/oauth/token"
89 | override val authorizeUrl = baseUrl + "/login/oauth/authorize"
90 | }
91 |
92 | case class Consumer(consumerKey : String, consumerSecret : String) {
93 | val oAuthConsumer : OAuthConsumer = new DefaultOAuthConsumer(consumerKey, consumerSecret)
94 | }
95 |
96 | case class Credential(provider : Provider, consumer : OAuthConsumer, readyToSign : Boolean)
97 |
98 | object credentials extends SessionVar[Option[Credential]](None)
99 | object mostRecentLoginAttemptProvider extends SessionVar[Box[Provider]](Empty)
100 |
101 | object OAuthClient extends MdcLoggable {
102 |
103 | def getAuthorizedCredential() : Option[Credential] = {
104 | credentials.filter(_.readyToSign)
105 | }
106 |
107 | def currentApiBaseUrl : String = {
108 | getAuthorizedCredential().map(_.provider.apiBaseUrl).getOrElse(OBPDemo.apiBaseUrl)
109 | }
110 |
111 | def setNewCredential(provider : Provider) : Credential = {
112 | val consumer = new DefaultOAuthConsumer(provider.consumerKey, provider.consumerSecret)
113 | val credential = Credential(provider, consumer, false)
114 |
115 | credentials.set(Some(credential))
116 | credential
117 | }
118 |
119 | def handleCallback(): Box[LiftResponse] = {
120 |
121 | val success = for {
122 | verifier <- S.param("oauth_verifier") ?~ "No oauth verifier found"
123 | provider <- mostRecentLoginAttemptProvider.get ?~ "No provider found for callback"
124 | consumer <- Box(credentials.map(_.consumer)) ?~ "No consumer found for callback"
125 | } yield {
126 | //after this, consumer is ready to sign requests
127 | provider.oAuthProvider.retrieveAccessToken(consumer, verifier)
128 | //update the session credentials
129 | val newCredential = Credential(provider, consumer, true)
130 | credentials.set(Some(newCredential))
131 | }
132 |
133 | success match {
134 | case Full(_) => S.redirectTo("/") //TODO: Allow this redirect to be customised
135 | case Failure(msg, _, _) => logger.warn(msg)
136 | case _ => logger.warn("Something went wrong in an oauth callback and there was no error message set for it")
137 | }
138 | Empty
139 | }
140 |
141 | def redirectToOauthLogin() = {
142 | redirect(OBPDemo)
143 | }
144 |
145 | private def redirect(provider : Provider) = {
146 | mostRecentLoginAttemptProvider.set(Full(provider))
147 | val credential = setNewCredential(provider)
148 | credential.consumer.setMessageSigner(new HmacSha256MessageSigner())
149 | val authUrl = provider.oAuthProvider.retrieveRequestToken(credential.consumer, Props.get("base_url", S.hostName) + "/oauthcallback")
150 | S.redirectTo(authUrl)
151 | }
152 |
153 | def redirectToConnectBankAccount() = {
154 | redirect(AddBankAccountProvider)
155 | }
156 |
157 | def loggedIn : Boolean = credentials.map(_.readyToSign).getOrElse(false)
158 |
159 | def logoutAll() = {
160 | val apiExplorerHost = {Props.get("base_url", S.hostName)}
161 | val obpApiHost = { Props.get("api_portal_hostname").or(Props.get("api_hostname")).getOrElse("Unknown") }
162 | credentials.set(None)
163 | S.redirectTo(s"$obpApiHost/user_mgt/logout?redirect=$apiExplorerHost")
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/scala/code/lib/ObpOAuthProvider.scala:
--------------------------------------------------------------------------------
1 | package code.lib
2 |
3 | import java.io.IOException
4 | import java.net.{HttpURLConnection, MalformedURLException}
5 |
6 | import oauth.signpost.AbstractOAuthProvider
7 | import oauth.signpost.basic.{HttpURLConnectionRequestAdapter, HttpURLConnectionResponseAdapter}
8 | import oauth.signpost.http.{HttpRequest, HttpResponse}
9 |
10 |
11 | /**
12 | * Library https://github.com/mttkay/signpost we use for OAuth1 does not implement MTLS connection.
13 | * In order to support it ObpOAuthProvider inherit AbstractOAuthProvider and override function createRequest
14 | * in a way that {@link HttpURLConnection} is used in case the props ssl_client_auth=false
15 | * or {@link HttpsURLConnection} in case the props ssl_client_auth=true
16 | * to receive tokens from a service provider.
17 | */
18 | @SerialVersionUID(1L)
19 | class ObpOAuthProvider(val requestTokenEndpointUrl: String, val accessTokenEndpointUrl: String, val authorizationWebsiteUrl: String) extends AbstractOAuthProvider(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl) {
20 | @throws[MalformedURLException]
21 | @throws[IOException]
22 | override protected def createRequest(endpointUrl: String): HttpRequest = {
23 | val connection = SSLHelper.getConnection(endpointUrl)
24 | connection.setRequestMethod("POST")
25 | connection.setAllowUserInteraction(false)
26 | connection.setRequestProperty("Content-Length", "0")
27 | new HttpURLConnectionRequestAdapter(connection)
28 | }
29 |
30 | @throws[IOException]
31 | override protected def sendRequest(request: HttpRequest): HttpResponse = {
32 | val connection = request.unwrap.asInstanceOf[HttpURLConnection]
33 | connection.connect()
34 | new HttpURLConnectionResponseAdapter(connection)
35 | }
36 |
37 | override protected def closeConnection(request: HttpRequest, response: HttpResponse): Unit = {
38 | val connection = request.unwrap.asInstanceOf[HttpURLConnection]
39 | if (connection != null) connection.disconnect()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/scala/code/lib/SSLHelper.scala:
--------------------------------------------------------------------------------
1 | package code.lib
2 |
3 | import java.io.FileInputStream
4 | import java.net.{HttpURLConnection, URL}
5 | import java.security.{KeyStore, SecureRandom}
6 |
7 | import javax.net.ssl.{TrustManagerFactory, _}
8 | import net.liftweb.util.Props
9 |
10 | object SSLHelper {
11 |
12 |
13 | private lazy val sSLSocketFactory = {
14 | val keystoreFile: String = Props.get("ssl_keystore_location").openOrThrowException("props value of ssl_keystore_location is missing")
15 | val keystorePassword = Props.get("ssl_keystore_password", "")
16 | val truststoreFile = Props.get("ssl_truststore_location","")
17 | val truststorePassword = Props.get("ssl_truststore_password", "")
18 | val keyPassword = Props.get("ssl_key_password", "")
19 |
20 |
21 |
22 | val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
23 | val keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
24 | val keystore = KeyStore.getInstance(KeyStore.getDefaultType)
25 | val inputStream = new FileInputStream(keystoreFile)
26 | try {
27 | keystore.load(inputStream, keystorePassword.toCharArray)
28 | } finally {
29 | inputStream.close()
30 | }
31 | keyManager.init(keystore,keyPassword.toCharArray)
32 |
33 | val truststore = KeyStore.getInstance(KeyStore.getDefaultType)
34 | val trustInputStream = new FileInputStream(truststoreFile)
35 | try {
36 | truststore.load(trustInputStream, truststorePassword.toCharArray)
37 | } finally {
38 | trustInputStream.close()
39 | }
40 |
41 |
42 | tmf.init(truststore)
43 |
44 |
45 |
46 | val sslContext = SSLContext.getInstance("TLSv1.2")
47 | sslContext.init(keyManager.getKeyManagers, tmf.getTrustManagers, new SecureRandom())
48 |
49 | val hostnameVerifier: HostnameVerifier = new HostnameVerifier {
50 | override def verify(host: String, sslSession: SSLSession): Boolean = true
51 | }
52 |
53 | HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier)
54 |
55 | sslContext.getSocketFactory
56 | }
57 |
58 | def getConnection(url: String): HttpURLConnection = {
59 | Props.get("ssl_client_auth", "false") match {
60 | case "true" => {
61 | val httpsUrl = if (url.startsWith("https://")) url else url.replaceFirst("^http://", "https://")
62 |
63 | val connection = new URL(httpsUrl).openConnection().asInstanceOf[HttpsURLConnection]
64 | connection.setSSLSocketFactory(sSLSocketFactory)
65 | connection
66 | }
67 | case "false" => new URL(url).openConnection().asInstanceOf[HttpURLConnection]
68 | case errorValue => sys.error(s"obp_certificate_activate props should be true or false, current value is: $errorValue")
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/AccountSettings.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.Constant._
4 | import code.lib.ObpAPI.{getAccount, updateAccountLabel}
5 | import code.util.Helper.{MdcLoggable, getAccountTitle}
6 | import net.liftweb.http.SHtml
7 | import net.liftweb.http.js.JE.Call
8 | import net.liftweb.http.js.JsCmd
9 | import net.liftweb.http.js.JsCmds.SetHtml
10 | import net.liftweb.util.Helpers._
11 |
12 | import scala.xml.{NodeSeq, Text}
13 |
14 |
15 | /*
16 | For maintaining permissions on the views (entitlements on the account)
17 | */
18 | class AccountSettings(params: List[String]) extends MdcLoggable {
19 | val bankId = params(0)
20 | val accountId = params(1)
21 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson")
22 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson)
23 |
24 | //set up ajax handlers to edit account label
25 | def editLabel(xhtml: NodeSeq): NodeSeq = {
26 | var newLabel = ""
27 |
28 | def process(): JsCmd = {
29 | logger.debug(s"AccountSettings.editLabel.process: edit label $newLabel")
30 | val result = updateAccountLabel(bankId, accountId, newLabel)
31 | if (result.isDefined) {
32 | val msg = "Label " + newLabel + " has been set"
33 | SetHtml("account-title", Text(newLabel)) &
34 | Call("socialFinanceNotifications.notify", msg).cmd
35 | } else {
36 | val msg = "Sorry, Label" + newLabel + " could not be set ("+ result +")"
37 | Call("socialFinanceNotifications.notifyError", msg).cmd
38 | }
39 | }
40 |
41 | (
42 | // Bind newViewName field to variable (e.g. http://chimera.labs.oreilly.com/books/1234000000030/ch03.html)
43 | "@new_label" #> SHtml.text(accountJson.label.getOrElse(""), s => newLabel = s) &
44 | // Replace the type=submit with Javascript that makes the ajax call.
45 | "type=submit" #> SHtml.ajaxSubmit("Save account label", process)
46 | ).apply(xhtml)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/CreateBankAccount.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.lib.ObpAPI
4 | import code.lib.ObpAPI.createAccount
5 | import code.lib.ObpJson.BankJson400
6 | import code.util.Helper.MdcLoggable
7 | import net.liftweb.common.Box
8 | import net.liftweb.http.js.JE.Call
9 | import net.liftweb.http.js.JsCmd
10 | import net.liftweb.http.js.JsCmds.SetHtml
11 | import net.liftweb.http.{RequestVar, SHtml}
12 | import net.liftweb.util.Helpers._
13 |
14 | import scala.collection.immutable.List
15 | import scala.xml.{NodeSeq, Text}
16 |
17 |
18 | class CreateBankAccount(params: List[BankJson400]) extends MdcLoggable {
19 |
20 | private object bankVar extends RequestVar("")
21 | private object userIdVar extends RequestVar("")
22 |
23 |
24 | def editLabel(xhtml: NodeSeq): NodeSeq = {
25 | var newLabel = ""
26 | val bankId = "user." + ObpAPI.currentUser.map(_.user_id).getOrElse(System.currentTimeMillis())
27 | val listOfBanks = params
28 | .filter(_.id == bankId)
29 | .map(b => (b.id, b.full_name))
30 |
31 | def process(): JsCmd = {
32 | ObpAPI.currentUser.map {
33 | u => userIdVar.set(u.user_id)
34 | }
35 | logger.debug(s"CreateBankAccount.editLabel.process: edit label $newLabel")
36 | if(listOfBanks.size == 1) {
37 | val result = createAccount(bankVar.is, newLabel, userIdVar.is)
38 | if (result.isDefined) {
39 | val msg = "Saved"
40 | SetHtml("account-title", Text(newLabel)) &
41 | Call("socialFinanceNotifications.notify", msg).cmd
42 | } else {
43 | val msg = "Sorry, the new account with the label" + newLabel + " could not be set ("+ result +")"
44 | Call("socialFinanceNotifications.notifyError", msg).cmd
45 | }
46 | } else {
47 | val msg = "Sorry, the new account with the label " + newLabel + " could not be set due to unresolved bank id value"
48 | Call("socialFinanceNotifications.notifyError", msg).cmd
49 | }
50 | }
51 |
52 | (
53 | "@new_label" #> SHtml.text("", s => newLabel = s) &
54 | "#bank-id" #> SHtml.select(listOfBanks, Box!! bankVar.is, bankVar(_)) &
55 | // Replace the type=submit with Javascript that makes the ajax call.
56 | "type=submit" #> SHtml.ajaxSubmit("Create", process)
57 | ).apply(xhtml)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/CreateExpenditure.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.Constant._
4 | import code.lib.ObpAPI
5 | import code.lib.ObpAPI.{createOutcome, getAccount}
6 | import code.util.Helper.{MdcLoggable, getAccountTitle}
7 | import net.liftweb.common.Box
8 | import net.liftweb.http.js.JE.Call
9 | import net.liftweb.http.js.JsCmd
10 | import net.liftweb.http.{RequestVar, S, SHtml}
11 | import net.liftweb.util.Helpers._
12 |
13 | import scala.xml.NodeSeq
14 |
15 |
16 | class CreateExpenditure(params: List[String]) extends MdcLoggable {
17 | private object tagVar extends RequestVar("")
18 | val bankId = params(0)
19 | val accountId = params(1)
20 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson")
21 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson)
22 |
23 |
24 | def addPayment(xhtml: NodeSeq): NodeSeq = {
25 | var outcomeDescription = ""
26 | var outcomeAmount = 0D
27 | val listOfTags: Seq[(String, String)] = S.?("expenditure.tags").
28 | split(",").toList.map(_.trim).map(i => i.replaceAll("_", "/")).map(i => (i,i))
29 |
30 | def process(): JsCmd = {
31 | logger.debug(s"CreateOutcome.addOutcome.process: edit label $outcomeDescription")
32 | val result = createOutcome(bankId, accountId, outcomeDescription, outcomeAmount.toString, "EUR")
33 | if (result.isDefined) {
34 | val transactionId = result.map(_.transaction_id).getOrElse("")
35 | val addTags = ObpAPI.addTags(bankId, accountId, "owner", transactionId, List(tagVar.is))
36 | logger.debug(s"CreateOutcome.addOutcome.process.addTags $addTags")
37 | val msg = "Saved"
38 | Call("socialFinanceNotifications.notify", msg).cmd
39 | S.redirectTo(s"/banks/$bankId/accounts/$accountId/owner")
40 | } else {
41 | val msg = s"Sorry, Expenditure $outcomeDescription could not be added ($result)"
42 | Call("socialFinanceNotifications.notifyError", msg).cmd
43 | }
44 | }
45 |
46 | (
47 | "@payment_description" #> SHtml.text("", s => outcomeDescription = s) &
48 | "#payment_category" #> SHtml.select(listOfTags, Box!! tagVar.is, tagVar(_)) &
49 | "@payment_amount" #> SHtml.number(0D, (s:Double) => outcomeAmount = s, 0D, 1000000000D, 0.01D) &
50 | // Replace the type=submit with Javascript that makes the ajax call.
51 | "type=submit" #> SHtml.ajaxSubmit(S.?("button.save"), process)
52 | ).apply(xhtml)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/CreateIncome.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.Constant._
4 | import code.lib.ObpAPI
5 | import code.lib.ObpAPI.{createIncome, getAccount, getBalancingTransaction}
6 | import code.util.Helper.{MdcLoggable, getAccountTitle}
7 | import net.liftweb.common.Box
8 | import net.liftweb.http.js.JE.Call
9 | import net.liftweb.http.js.JsCmd
10 | import net.liftweb.http.{RequestVar, S, SHtml}
11 | import net.liftweb.util.Helpers._
12 | import net.liftweb.util.Props
13 |
14 | import scala.xml.NodeSeq
15 |
16 |
17 | class CreateIncome(params: List[String]) extends MdcLoggable {
18 | private object tagVar extends RequestVar("")
19 | val bankId = params(0)
20 | val accountId = params(1)
21 | val accountJson = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID).openOrThrowException("Could not open accountJson")
22 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson)
23 |
24 | //set up ajax handlers to edit account label
25 | def addIncome(xhtml: NodeSeq): NodeSeq = {
26 | var incomeDescription = ""
27 | var incomeAmount = 0D
28 | val listOfTags: Seq[(String, String)] = S.?("income.tags").
29 | split(",").toList.map(_.trim).map(i => i.replaceAll("_", "/")).map(i => (i,i))
30 |
31 | def process(): JsCmd = {
32 | logger.debug(s"CreateIncome.addIncome.process: edit label $incomeDescription")
33 | val result = createIncome(bankId, accountId, incomeDescription, incomeAmount.toString, "EUR")
34 | if (result.isDefined) {
35 | val incomeAccountId = Props.get("incoming.account_id", "")
36 | val transactionId = result.map(_.transaction_id).getOrElse("")
37 | logger.debug(s"CreateIncome.addIncome.process.transactionId $transactionId")
38 | val creditTransactionId = getBalancingTransaction(transactionId)
39 | .map(_.credit_transaction.transaction_id).getOrElse("")
40 | logger.debug(s"CreateIncome.addIncome.process.creditTransactionId $creditTransactionId")
41 | val addTags = ObpAPI.addTags(bankId, accountId, "owner", creditTransactionId, List(tagVar.is))
42 | logger.debug(s"CreateIncome.addIncome.process.addTags $addTags")
43 | val msg = "Saved"
44 | Call("socialFinanceNotifications.notify", msg).cmd
45 | S.redirectTo(s"/banks/$bankId/accounts/$accountId/owner")
46 | } else {
47 | val msg = s"Sorry, Income $incomeDescription could not be added ($result)"
48 | Call("socialFinanceNotifications.notifyError", msg).cmd
49 | }
50 | }
51 |
52 | (
53 | "@income_description" #> SHtml.text("", s => incomeDescription = s) &
54 | "#income_category" #> SHtml.select(listOfTags, Box!! tagVar.is, tagVar(_)) &
55 | "@income_amount" #> SHtml.number(0D, (s:Double) => incomeAmount = s, 0D, 1000000000D, 0.01D) &
56 | // Replace the type=submit with Javascript that makes the ajax call.
57 | "type=submit" #> SHtml.ajaxSubmit(S.?("button.save"), process)
58 | ).apply(xhtml)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/CreatePermissionForm.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.lib.ObpAPI
4 | import code.lib.ObpJson.{AccountJson, ViewJson}
5 | import code.util.Helper.{MdcLoggable, getAccountTitle}
6 | import net.liftweb.common.Failure
7 | import net.liftweb.http.{S, SHtml}
8 | import net.liftweb.http.js.JsCmd
9 | import net.liftweb.http.js.JsCmds._
10 | import net.liftweb.util.Helpers._
11 |
12 | import scala.xml.Text
13 |
14 |
15 | class CreatePermissionForm(params : (List[ViewJson], AccountJson, PermissionsUrlParams)) extends MdcLoggable {
16 | case class ViewData(view : ViewJson, allowed: Boolean)
17 |
18 | val views = params._1
19 | val accountJson = params._2
20 | val urlParams = params._3
21 |
22 | val url = S.uri.split("/")
23 | var username = ""
24 |
25 | val allowed : scala.collection.mutable.Map[ViewJson, Boolean] = scala.collection.mutable.Map(views.map(v => (v, false)).toSeq : _*)
26 |
27 | val nonPublicViews = views.filterNot(_.is_public.getOrElse(true))
28 | val nonBadViews = nonPublicViews.filterNot(_.id == None)
29 | def viewData = nonBadViews.map(view => ViewData(view, allowed(view)))
30 |
31 | def render = {
32 |
33 | def process(): JsCmd = {
34 |
35 | def showMsg(msg : String) = {
36 | SetHtml("create-permission-message", Text(msg))
37 | }
38 |
39 | if(username.isEmpty()) showMsg("You must enter a username")
40 | //else if(invalidEmail()) showMsg("Invalid e-mail address")
41 | //else if(viewData.forall(vData => allowed(vData.view) == false)) showMsg("You must select at least one view to grant access to.")
42 | else if(viewData.forall(_.allowed == false)) showMsg("You must select at least one view to grant access to.")
43 | else {
44 | //create the permission and return any failure
45 | if(url.length > 4) {
46 | val bankId = url(2)
47 | val accountId = url(4)
48 | val viewIds = for {
49 | vData <- viewData
50 | if(vData.allowed)
51 | vId <- vData.view.id
52 | } yield vId
53 |
54 | val result = ObpAPI.addPermissions(bankId, accountId, username, viewIds)
55 | result match {
56 | case Failure(msg, _, _) => showMsg(msg)
57 | case _ => {
58 | //Redirect to permissions overview
59 | //TODO: Would be nice to calculate this but at the moment there is nothing to do it
60 | S.redirectTo("/banks/" + bankId + "/accounts/" + accountId + "/permissions")
61 | Noop
62 | }
63 | }
64 |
65 | } else {
66 | logger.warn("Couldn't determine bank and account from url:" + url.toList.toString)
67 | showMsg("error")
68 | }
69 |
70 | }
71 |
72 | }
73 |
74 | ".view_row *" #> viewData.map(vData => {
75 | val onOffSwitch = "onoffswitch-view-" + vData.view.id.getOrElse("")
76 | ".view_name *" #> vData.view.short_name.getOrElse(vData.view.id.getOrElse("")) &
77 | ".view_check" #> SHtml.checkbox(
78 | allowed(vData.view),
79 | allowed.put(vData.view, _),
80 | "class" -> "onoffswitch-checkbox view_check",
81 | "id" -> onOffSwitch) &
82 | ".onoffswitch-label [for]" #> onOffSwitch
83 | }) &
84 | "name=email" #> SHtml.text(username, username = _) &
85 | "type=submit" #> SHtml.ajaxSubmit("Grant access", process)
86 |
87 | }
88 |
89 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson)
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/CustomEditable.scala:
--------------------------------------------------------------------------------
1 | /**
2 | Open Bank Project - Sofi Web Application
3 | Copyright (C) 2011 - 2021, TESOBE GmbH
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Affero General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Affero General Public License for more details.
14 |
15 | You should have received a copy of the GNU Affero General Public License
16 | along with this program. If not, see .
17 |
18 | Email: contact@tesobe.com
19 | TESOBE GmbH
20 | Osloer Str. 16/17
21 | Berlin 13359, Germany
22 |
23 | This product includes software developed at
24 | TESOBE (http://www.tesobe.com/)
25 | by
26 | Simon Redfern : simon AT tesobe DOT com
27 | Stefan Bethge : stefan AT tesobe DOT com
28 | Everett Sochowski : everett AT tesobe DOT com
29 | Ayoub Benali: ayoub AT tesobe DOT com
30 | Nina Gänsdorfer: nina AT tesobe.com
31 |
32 | */
33 |
34 | package code.snippet
35 |
36 | import net.liftweb.http.SHtml
37 | import scala.xml.NodeSeq
38 |
39 | object CustomEditable {
40 |
41 | import net.liftweb.http.js
42 | import net.liftweb.util.Helpers
43 | import js.{ jquery, JsCmd, JsCmds, JE }
44 | import jquery.JqJsCmds
45 | import JsCmds.{ Noop, SetHtml }
46 | import JE.Str
47 | import JqJsCmds.{ Hide, Show }
48 |
49 |
50 | val addClass = "add"
51 | val editClass = "edit"
52 | val removeClass = "remove"
53 | val noAliasTooltip = "No alias is set, so the real account name will be displayed."
54 | val confirmRemoval = "If no alias is set, the real account name will be displayed."
55 |
56 | def dispName(divName: String) : String = divName + "_display"
57 | def editName(divName: String) : String = divName + "_edit"
58 |
59 |
60 | /*
61 | * Depending on what was clicked only either the "_edit" or the "_display" div is visible
62 | */
63 | def swapJsCmd(show: String, hide: String): JsCmd = Show(show) & Hide(hide)
64 | def setAndSwap(show: String, showContents: => NodeSeq, hide: String): JsCmd =
65 | (SHtml.ajaxCall(Str("ignore"), { ignore: String => SetHtml(show, showContents) })._2.cmd & swapJsCmd(show, hide))
66 |
67 |
68 | /*
69 | * The edit markup consists of the input field (editForm), a submit button and a cancel button
70 | * Clicking on either of buttons will swap back to the display markup, the submit button will save the data of the input field
71 | */
72 | def editMarkup(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): NodeSeq = {
73 |
74 | val formData: NodeSeq =
75 | editForm ++
76 | ++
77 | SHtml.hidden(onSubmit, ("float", "left")) ++
78 |
79 |
80 | SHtml.ajaxForm(formData,
81 | Noop,
82 | setAndSwap(dispName(divName), displayMarkup(label, editForm, onSubmit, onDelete, defaultValue, divName, removable), editName(divName)))
83 | }
84 |
85 |
86 | /*
87 | * The display markup shows the label (default value if none is set), an edit button and for some fields (currently only the alias fields) also a remove button
88 | * Clicking on the edit button will swap to the edit markup
89 | * Clicking on the remove button will pop up a confirmation window
90 | */
91 | def displayMarkup(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): NodeSeq = {
92 |
93 | label match {
94 | case "" => {
95 | if(removable){
96 |
97 |
{ defaultValue }
98 |
Edit Transaction Narrative
99 |
Delete
100 |
101 | } else{
102 |
103 |
104 |
{ " " ++ defaultValue }
105 |
106 | }
107 | }
108 | case _ => {
109 |
110 |
{ label }
111 |
Edit Transaction Narrative
112 | { if (removable)
113 |
Delete
114 | }
115 |
116 | }
117 | }
118 | }
119 |
120 |
121 | /*
122 | * Pop-up window when removing alias: on approval deletes alias and empties value in input field of the edit markup
123 | */
124 |
125 | def removeAlias(label: String, editForm: NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, divName: String, removable: Boolean): String = {
126 |
127 | def removalConfirmed: JsCmd = {
128 | SHtml.ajaxInvoke(() => {
129 | onDelete()
130 | var empty = ""
131 | setAndSwap(dispName(divName), displayMarkup(empty, SHtml.text(empty, empty = _), onSubmit, onDelete, defaultValue, divName, removable), editName(divName))
132 | })._2.cmd
133 | }
134 |
135 | val confirmationPopup = JsCmds.Confirm(confirmRemoval, removalConfirmed)
136 | confirmationPopup
137 | }
138 |
139 | /*
140 | *
141 | */
142 |
143 | def editable(label : => String, editForm: => NodeSeq, onSubmit: () => JsCmd, onDelete: () => Unit, defaultValue: String, removable: Boolean): NodeSeq ={
144 | val divName = Helpers.nextFuncName
145 |
146 |
147 |
148 | { displayMarkup(label, editForm, onSubmit, onDelete, defaultValue, divName, removable) }
149 |
150 |
151 | { editMarkup(label, editForm, onSubmit, onDelete, defaultValue, divName, removable) }
152 |
153 |
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/Dashboard.scala:
--------------------------------------------------------------------------------
1 | /**
2 | Open Bank Project - Sofi Web Application
3 | Copyright (C) 2011 - 2021, TESOBE GmbH
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Affero General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Affero General Public License for more details.
14 |
15 | You should have received a copy of the GNU Affero General Public License
16 | along with this program. If not, see .
17 |
18 | Email: contact@tesobe.com
19 | TESOBE GmbH.
20 | Osloer Str. 16/17
21 | Berlin 13359, Germany
22 |
23 | This product includes software developed at
24 | TESOBE (http://www.tesobe.com/)
25 | by
26 | Simon Redfern : simon AT tesobe DOT com
27 | Stefan Bethge : stefan AT tesobe DOT com
28 | Everett Sochowski : everett AT tesobe DOT com
29 | Ayoub Benali: ayoub AT tesobe DOT com
30 |
31 | */
32 | package code.snippet
33 |
34 | import code.lib.{OAuthClient, ObpAPI}
35 | import code.lib.ObpJson._
36 | import code.util.Helper.MdcLoggable
37 | import net.liftweb.common.Full
38 |
39 | class OBPDashboardLeftSnippet (params : (TransactionsJson, AccountJson, TransactionsListURLParams, TransactionsJson, AccountJson, TransactionsListURLParams)) extends OBPTransactionSnippet ((params._1, params._2, params._3)) {
40 | }
41 |
42 | class OBPDashboardRightSnippet (params : (TransactionsJson, AccountJson, TransactionsListURLParams, TransactionsJson, AccountJson, TransactionsListURLParams)) extends OBPTransactionSnippet ((params._4, params._5, params._6)) {
43 | }
44 |
45 |
46 |
47 | ///////////////
48 |
49 | //import code.snippet.CallUrlForm._
50 | import net.liftweb.http.S
51 |
52 | import scala.xml.NodeSeq
53 | // for compact render
54 | import code.lib.ObpAPI.allBanks
55 | import net.liftweb.http.CurrentReq
56 | import net.liftweb.http.SHtml.ajaxSelect
57 | import net.liftweb.util.Helpers._
58 |
59 |
60 | /*
61 | Present a list of OBP resource URLs
62 | */
63 | class OBPDashboard extends MdcLoggable {
64 |
65 |
66 |
67 | val presetBankId = S.param("bank_id").getOrElse("")
68 | logger.info(s"bank_id in url param is $presetBankId")
69 |
70 | val presetAccountId = S.param("account_id").getOrElse("")
71 | logger.info(s"account_id in url param is $presetAccountId")
72 |
73 | val presetViewId = S.param("view_id").getOrElse("")
74 | logger.info(s"account_id in url param is $presetViewId")
75 |
76 | val presetCounterpartyId = S.param("counterparty_id").getOrElse("")
77 | logger.info(s"counterparty_id in url param is $presetCounterpartyId")
78 |
79 | val presetTransactionId = S.param("transaction_id").getOrElse("")
80 | logger.info(s"transaction_id in url param is $presetTransactionId")
81 |
82 |
83 | def stringToNodeSeq(html : String) : NodeSeq = {
84 | scala.xml.XML.loadString("" + html + "
")
85 | }
86 |
87 |
88 | def modifiedRequestUrl(url: String, presetBankId: String, presetAccountId: String) = {
89 | // Potentially replace BANK_ID
90 | val url2: String = presetBankId match {
91 | case "" => url
92 | case _ => url.replaceAll("BANK_ID", presetBankId)
93 | }
94 |
95 | // Potentially replace ACCOUNT_ID
96 | val url3: String = presetAccountId match {
97 | case "" => url2
98 | case _ => url2.replaceAll("/ACCOUNT_ID", s"/$presetAccountId") // so we don't change OTHER_ACCOUNT_ID
99 | }
100 |
101 | // Potentially replace VIEW_ID
102 | val url4: String = presetViewId match {
103 | case "" => url3
104 | case _ => url3.replaceAll("VIEW_ID", presetViewId)
105 | }
106 |
107 | // Potentially replace OTHER_ACCOUNT_ID
108 | val url5: String = presetCounterpartyId match {
109 | case "" => url4
110 | case _ => url4.replaceAll("OTHER_ACCOUNT_ID", presetCounterpartyId)
111 | }
112 |
113 | // Potentially replace TRANSACTION_ID
114 | val url6: String = presetTransactionId match {
115 | case "" => url5
116 | case _ => url5.replaceAll("TRANSACTION_ID", presetTransactionId)
117 | }
118 |
119 | url6
120 | }
121 |
122 | def showAccountSelector = {
123 |
124 |
125 | val uri = CurrentReq.value.uri
126 |
127 | val banks = allBanks
128 |
129 | // TODO dehardcode the redirect path.
130 |
131 |
132 | def onAccountChange (v: Any) = {
133 | logger.info("account changed to " + v.toString)
134 | S.redirectTo(s"$uri?bank_id=${presetBankId}&account_id=${v}")
135 | }
136 |
137 |
138 |
139 | // Get a list of tuples List(("bank short name", "id"),("bank two", "id2")) to populate the drop down select list.
140 | // Could we write this in a way such that if there are no banks the doBankSelect is not run?
141 | val bankOptions = ("", "Select Bank") :: banks.map(b => b.bankJsons.map(bj => (bj.id.getOrElse(""), bj.short_name.getOrElse("") + " (" + bj.id.getOrElse("") + ")"))).getOrElse(List(("", "No Banks")))
142 |
143 | // TODO create BankId case class like in the API
144 | type BankID = String
145 |
146 | val privateAccountJsons : List[(String, String)] = for {
147 | privateAccountsJson <- ObpAPI.allAccountsAtOneBank(presetBankId).toList
148 | barebonesAccountJson <- privateAccountsJson.accounts.toList.flatten
149 | //bankId <- barebonesAccountJson.bank_id
150 | accountId <- barebonesAccountJson.id
151 | label <- barebonesAccountJson.label
152 | } yield (accountId, label)
153 |
154 | def getAccountOptions : List[(String,String)] = {
155 |
156 | val selectAccount = ("", "Select Account")
157 | val noneFound = ("", "") // No Accounts Found
158 |
159 | val options: List[(String, String)] = presetBankId match {
160 | case "" => List(noneFound)
161 | case _ => for {
162 | allAccountsJson <- ObpAPI.allAccountsAtOneBank(presetBankId).toList
163 | barebonesAccountJson <- allAccountsJson.accounts.toList.flatten
164 | accountId <- barebonesAccountJson.id
165 | label <- barebonesAccountJson.label
166 | } yield (accountId, label)
167 | }
168 |
169 | selectAccount :: options
170 | }
171 |
172 | // def getViewOptions : List[(String,String)] = {
173 | //
174 | // val selectOne = OAuthClient.loggedIn match {
175 | // case true => ("", "Select View")
176 | // case false => ("", "Login for Views")
177 | // }
178 | //
179 | // val noneFound = ("", "") // No Views Found
180 | //
181 | // // TODO Should check for both presetBankId and presetAccountId
182 | // // Logged in user required?
183 | // val options: List[(String, String)] = presetAccountId match {
184 | // case "" => List(noneFound)
185 | // case _ => for {
186 | // views <- ObpAPI.getViewsForBankAccount(presetBankId, presetAccountId).toList
187 | // view <- views.views.toList.flatten
188 | // viewId <- view.id
189 | // shortName <- view.short_name
190 | // } yield (viewId, shortName)
191 | // }
192 | //
193 | // selectOne :: options
194 | // }
195 |
196 |
197 |
198 | // Drop down box to select account. Selected item taken from url param.
199 | def doAccountSelect(in: NodeSeq) = ajaxSelect(getAccountOptions,
200 | Full(presetAccountId),
201 | v => onAccountChange(v))
202 |
203 | // Drop down box to select view for bank/account. Selected item taken from url param.
204 | // def doViewSelect(in: NodeSeq) = ajaxSelect(getViewOptions,
205 | // Full(presetViewId),
206 | // v => onViewChange(v))
207 |
208 |
209 |
210 |
211 | def loggedInStatusMessage = {
212 | if (OAuthClient.loggedIn) "" else "Some options and calls require login."
213 | }
214 |
215 |
216 | ".account_selector" #> doAccountSelect _
217 | }
218 |
219 | }
220 |
221 |
222 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/Login.scala:
--------------------------------------------------------------------------------
1 | /**
2 | Open Bank Project - Sofi Web Application
3 | Copyright (C) 2011 - 2021, TESOBE GmbH
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Affero General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Affero General Public License for more details.
14 |
15 | You should have received a copy of the GNU Affero General Public License
16 | along with this program. If not, see .
17 |
18 | Email: contact@tesobe.com
19 | TESOBE GmbH.
20 | Osloer Str. 16/17
21 | Berlin 13359, Germany
22 |
23 | This product includes software developed at
24 | TESOBE (http://www.tesobe.com/)
25 | by
26 | Simon Redfern : simon AT tesobe DOT com
27 | Stefan Bethge : stefan AT tesobe DOT com
28 | Everett Sochowski : everett AT tesobe DOT com
29 | Ayoub Benali: ayoub AT tesobe DOT com
30 |
31 | */
32 |
33 | package code.snippet
34 |
35 | import code.Constant
36 | import code.lib.{OAuthClient, ObpAPI}
37 | import code.util.Helper.MdcLoggable
38 | import code.util.{Helper, Util}
39 | import net.liftweb.common.{Box, Full}
40 | import net.liftweb.http.js.JsCmd
41 | import net.liftweb.http.js.JsCmds.Noop
42 | import net.liftweb.http.provider.HTTPCookie
43 | import net.liftweb.http.{S, SHtml}
44 | import net.liftweb.util.Helpers._
45 |
46 | class Login extends MdcLoggable {
47 |
48 |
49 | private def loggedIn = {
50 | // Correlated User ID Flow
51 | S.cookieValue(Constant.correlatedUserIdTargetCookieName) match {
52 | case Full(correlatedUserId) if correlatedUserId != null =>
53 | // This will do the full correlatedUserFlow
54 | logger.debug("Expecting full correlatedUserFlow")
55 | Util.correlatedUserFlow(Some(correlatedUserId))
56 | case _ =>
57 | // This will do the partial correlatedUserFlow i.e. maybe add a customer and user customer link
58 | logger.debug("Expecting partial correlatedUserFlow")
59 | Util.correlatedUserFlow(None)
60 | }
61 | def getDisplayNameOfUser(): Box[String] = {
62 | ObpAPI.currentUser.map {
63 | u =>
64 | u.provider.toLowerCase() match {
65 | case provider if provider.contains("google") => u.email
66 | case provider if provider.contains("yahoo") => u.email
67 | case _ => u.username
68 | }
69 | }
70 | }
71 | ".logged-out *" #> "" &
72 | "#logged-in-name *" #> getDisplayNameOfUser() &
73 | "#logout [onclick+]" #> SHtml.onEvent(s => {
74 | OAuthClient.logoutAll()
75 | Noop
76 | })
77 | }
78 |
79 | def loggedOut = {
80 | ".logged-in *" #> "" &
81 | "#start-login [onclick]" #> {
82 | def actionJS: JsCmd = {
83 | OAuthClient.redirectToOauthLogin()
84 | Noop
85 | }
86 | SHtml.onEvent((s: String) => actionJS)
87 | }
88 | }
89 |
90 | def login = {
91 | if(OAuthClient.loggedIn) loggedIn
92 | else loggedOut
93 | }
94 |
95 |
96 |
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/PermissionManagement.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import code.lib.ObpAPI
4 | import code.lib.ObpJson._
5 | import code.util.Helper.{MdcLoggable, _}
6 | import net.liftweb.common.{Failure, Full}
7 | import net.liftweb.http.{S, SHtml}
8 | import net.liftweb.http.js.JE.JsRaw
9 | import net.liftweb.http.js.JsCmds.{Noop, Script}
10 | import net.liftweb.json._
11 | import net.liftweb.util.{CssSel, Props}
12 | import net.liftweb.util.Helpers._
13 |
14 | case class PermissionsUrlParams(bankId : String, accountId: String)
15 | case class ClickJson(userId: String, checked: Boolean, viewId : String)
16 |
17 | class PermissionManagement(params : (PermissionsJson, AccountJson, List[ViewJson], PermissionsUrlParams)) extends MdcLoggable {
18 |
19 | val permissionsJson = params._1
20 | val accountJson = params._2
21 | val nonPublicViews : List[ViewJson] = params._3.filterNot(_.is_public.getOrElse(true))
22 | val urlParams = params._4
23 | val NOOP_SELECTOR = "#i_am_an_id_that_should_never_exist" #> ""
24 |
25 | implicit val formats = DefaultFormats
26 |
27 | def rowId(userId: String) = "permission_row_" + userId
28 |
29 | val clickAjax = SHtml.ajaxCall(JsRaw("permissionsCheckBoxCallback(this)"), checkBoxClick)
30 | val removeAjax = SHtml.ajaxCall(JsRaw("this.getAttribute('data-userid')"), userId => {
31 | ObpAPI.removeAllPermissions(urlParams.bankId, urlParams.accountId, userId)
32 | Noop
33 | })
34 |
35 | def checkBoxClick(rawData : String) = {
36 | val data = tryo{parse(rawData).extract[ClickJson]}
37 |
38 | data match {
39 | case Full(d) => {
40 | if(d.checked) ObpAPI.addPermission(urlParams.bankId, urlParams.accountId, d.userId, d.viewId)
41 | else ObpAPI.removePermission(urlParams.bankId, urlParams.accountId, d.userId, d.viewId)
42 | }
43 | case Failure(msg, _, _) => logger.warn("Could not parse raw checkbox click data: " + rawData + ", " + msg)
44 | case _ => logger.warn("Could not parse raw checkbox click data: " + rawData)
45 | }
46 |
47 | Noop
48 | }
49 |
50 | val checkBoxJsFunc = JsRaw("""
51 | function permissionsCheckBoxCallback(checkbox) {
52 | var json = {
53 | "userId" : checkbox.getAttribute("data-userid"),
54 | "checked" : checkbox.checked,
55 | "viewId" : checkbox.getAttribute("data-viewid")
56 | }
57 | return JSON.stringify(json);
58 | }
59 | """).cmd
60 |
61 | def accountTitle = ".account-title *" #> getAccountTitle(accountJson)
62 |
63 | private def getAllowedSystemVies = {
64 | val allowedSystemViews: List[String] = Props.get("sytems_views_to_display", "owner,accountant,auditor")
65 | .split(",").map(_.trim()).toList
66 | allowedSystemViews
67 | }
68 |
69 | def accountViewHeaders = {
70 | val viewNames : List[String] = nonPublicViews.filter(i => getAllowedSystemVies.exists(i.id.getOrElse("").toLowerCase == _.toLowerCase) || i.id.getOrElse("").startsWith("_")).map(_.short_name.getOrElse(""))
71 |
72 | ".view_name *" #> viewNames
73 | }
74 |
75 | def checkBoxes(permission : PermissionJson) = {
76 | ".view-checkbox *" #> nonPublicViews.filter(i => getAllowedSystemVies.exists(i.id.getOrElse("").toLowerCase == _.toLowerCase) || i.id.getOrElse("").startsWith("_")).map(view => {
77 |
78 | val permissionExists = (for {
79 | views <- permission.views
80 | }yield {
81 | views.exists(_.id == (view.id))
82 | }).getOrElse(false)
83 |
84 | val checkedSelector : CssSel =
85 | if(permissionExists) {{".check [checked]"} #> "checked"}
86 | else NOOP_SELECTOR
87 |
88 | val userid = permission.user.flatMap(_.id).getOrElse("invalid_userid")
89 | val onOffSwitch = "onoffswitch-user-" + userid + "-" + view.id.getOrElse("invalid_view_id")
90 | checkedSelector &
91 | ".check [onclick]" #> clickAjax &
92 | ".check [data-userid]" #> userid &
93 | ".check [data-viewid]" #> view.id &
94 | ".check [name]" #> onOffSwitch &
95 | ".check [id]" #> onOffSwitch &
96 | ".onoffswitch-label [for]" #> onOffSwitch
97 | })
98 | }
99 |
100 | def manage = {
101 |
102 | permissionsJson.permissions match {
103 | case None => "* *" #> "No permissions exist"
104 | case Some(ps) => {
105 | ".callback-script" #> Script(checkBoxJsFunc) &
106 | ".row" #> {
107 | ps.map(permission => {
108 | val userId = permission.user.flatMap(_.id).getOrElse("")
109 |
110 | "* [id]" #> rowId(userId) &
111 | ".user *" #> permission.user.flatMap(_.id).getOrElse("") &
112 | checkBoxes(permission) &
113 | ".remove [data-userid]" #> userId &
114 | ".remove [onclick]" #> removeAjax
115 | })
116 | }
117 | }
118 | }
119 |
120 | }
121 |
122 | def addPermissionLink = {
123 | //TODO: Should generate this url instead of hardcode it
124 | "* [href]" #> {S.uri + "/create"}
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/scala/code/snippet/TimeHelpers.scala:
--------------------------------------------------------------------------------
1 | package code.snippet
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.Calendar
5 |
6 | import net.liftweb.util.Helpers._
7 |
8 | /**
9 | * So we can put the current year in a template
10 | */
11 |
12 | class TimeHelpers {
13 |
14 | val now = Calendar.getInstance().getTime()
15 | // create the date/time formatter
16 | val yearFormat = new SimpleDateFormat("yyyy")
17 | val nowYear = yearFormat.format(now)
18 |
19 | def currentYear = "#current_year" #> nowYear.toString
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/scala/code/util/ExceptionLogger.scala:
--------------------------------------------------------------------------------
1 | package code.util
2 |
3 | import net.liftweb.common.{Failure, Full, Box}
4 | import code.util.Helper.MdcLoggable
5 | import net.liftweb.util.{Mailer, Props}
6 |
7 | object MyExceptionLogger extends MdcLoggable{
8 | import net.liftweb.http.Req
9 |
10 | def unapply(in: (Props.RunModes.Value, Req, Throwable)): Option[(Props.RunModes.Value, Req, Throwable)] = {
11 | import net.liftweb.util.Helpers.now
12 | import Mailer.{From, To, Subject, PlainMailBodyType}
13 |
14 | val outputStream = new java.io.ByteArrayOutputStream
15 | val printStream = new java.io.PrintStream(outputStream)
16 | in._3.printStackTrace(printStream)
17 | val currentTime = now.toString
18 | val stackTrace = new String(outputStream.toByteArray)
19 | val error = currentTime + ": " + stackTrace
20 | val host = Props.get("base_url", "unknown host")
21 |
22 | val mailSent = for {
23 | from <- Props.get("mail.exception.sender.address") ?~ "Could not send mail: Missing props param for 'from'"
24 | // no spaces, comma separated e.g. mail.api.consumer.registered.notification.addresses=notify@example.com,notify2@example.com,notify3@example.com
25 | toAddressesString <- Props.get("mail.exception.registered.notification.addresses") ?~ "Could not send mail: Missing props param for 'to'"
26 | } yield {
27 |
28 | //technically doesn't work for all valid email addresses so this will mess up if someone tries to send emails to "foo,bar"@example.com
29 | val to = toAddressesString.split(",").toList
30 | val toParams = to.map(To(_))
31 | val params = PlainMailBodyType(error) :: toParams
32 |
33 | //this is an async call
34 | Mailer.sendMail(
35 | From(from),
36 | Subject("you got an exception on "+host),
37 | params :_*
38 | )
39 | }
40 |
41 | //if Mailer.sendMail wasn't called (note: this actually isn't checking if the mail failed to send as that is being done asynchronously)
42 | if(mailSent.isEmpty)
43 | logger.warn("Exception notification failed: " +mailSent)
44 |
45 | None
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/scala/code/util/Helper.scala:
--------------------------------------------------------------------------------
1 | package code.util
2 |
3 | import code.Constant._
4 | import code.lib.ObpAPI.getAccount
5 | import code.lib.ObpJson.AccountJson
6 | import net.liftweb.common._
7 | import net.liftweb.util.Props
8 |
9 |
10 | object Helper {
11 |
12 |
13 |
14 | // From bankId / accountId
15 | def getAccountTitle (bankId: String, accountId: String) : String = {
16 | val accountJsonBox = getAccount(bankId, accountId, CUSTOM_OWNER_VIEW_ID)
17 |
18 | val accountTitle = accountJsonBox match {
19 | case Full(accountJson) => getAccountTitle(accountJson)
20 | case _ => "Unknown Account"
21 | }
22 | accountTitle
23 | }
24 |
25 |
26 | /*
27 | Returns a string which can be used for the title of the account
28 | Uses the Label else Id if possible
29 | */
30 | def getAccountTitle(accountJson: AccountJson ) : String = {
31 | accountJson.label.getOrElse(accountJson.id.getOrElse("---"))
32 | }
33 |
34 |
35 | def hasManagementAccess (accountJson: AccountJson ) : Boolean = {
36 | val availableViews = accountJson.views_available.toList.flatten
37 | availableViews.exists(view => view.id == Some(CUSTOM_OWNER_VIEW_ID))
38 | }
39 |
40 |
41 | def getHostname(): String = {
42 | Props.get("base_url", "") match {
43 | case s: String if s.nonEmpty => s.split(":").lift(1) match {
44 | case Some(s) => s.replaceAll("\\/", "").replaceAll("\\.", "-")
45 | case None => "unknown"
46 | }
47 | case _ => "unknown"
48 | }
49 | }
50 |
51 | trait MdcLoggable extends Loggable {
52 | MDC.put("host" -> getHostname)
53 | }
54 |
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/scala/code/util/Util.scala:
--------------------------------------------------------------------------------
1 | package code.util
2 |
3 | import java.text.SimpleDateFormat
4 | import java.time.LocalDate
5 | import java.time.format.DateTimeFormatter
6 | import java.util.{Calendar, Date, Locale}
7 |
8 | import code.Constant
9 | import code.lib.ObpAPI
10 | import code.util.Helper.MdcLoggable
11 | import net.liftweb.common.Full
12 | import net.liftweb.http.S
13 | import net.liftweb.http.provider.HTTPCookie
14 | import net.liftweb.util.Props
15 |
16 | import scala.util.Try
17 |
18 | object Util extends MdcLoggable {
19 | def correlatedUserFlow(correlatedUserId: Option[String]): String = {
20 | logger.debug("Hello from the correlatedUserFlow, Correlated User ID: " + correlatedUserId)
21 | ObpAPI.currentUser match {
22 | case Full(currentUser) =>
23 | val currentUserId: String = currentUser.user_id
24 | val bankId = "user." + currentUserId
25 | ObpAPI.getOrCreateCustomer(bankId, legalName = currentUser.username) match {
26 | case Full(customerId) =>
27 | S.addCookie(HTTPCookie(Constant.correlatedCustomerIdCreatedCookieName, customerId))
28 | // TODO refactor this so that we check if there are links instead of relying on the error message.
29 | logger.debug(s"Before createUserCustomerLinkIfDoesNotExists bankid: $bankId currentUserId: $currentUserId customerId: $customerId")
30 | val loggedOnUserIdDone = ObpAPI.createUserCustomerLinkIfDoesNotExists(bankId, currentUserId, customerId)
31 | if(loggedOnUserIdDone) S.addCookie(HTTPCookie(Constant.linkBetweenCorrelatedUserAndCustomerCreatedCookieName, currentUserId))
32 |
33 | correlatedUserId match {
34 | case Some(localCorrelatedUserId) =>
35 | logger.debug("Before create user customer link Customer ID: " + customerId + " Correlated User ID: " + correlatedUserId)
36 | val correlatedUserIdDone = ObpAPI.createUserCustomerLinkIfDoesNotExists(bankId, localCorrelatedUserId, customerId)
37 | if(loggedOnUserIdDone) S.addCookie(HTTPCookie(Constant.correlatedUserIdBoundCookieName, localCorrelatedUserId)) // Seems this cookie is only for info purposes
38 | if(loggedOnUserIdDone && correlatedUserIdDone) {
39 | S.deleteCookie(Constant.correlatedUserIdTargetCookieName)
40 | "loggedOnUserIdDone&&correlatedUserIdDone"
41 | } else {
42 | "NOT (loggedOnUserIdDone&&correlatedUserIdDone)"
43 | }
44 | case _ =>
45 | logger.debug("No correlated user_id supplied, Not creating correlated user.")
46 | "no correlatedUserId just customer"
47 | }
48 |
49 | case someIssue =>
50 | logger.error("Correlated User Flow Error: " + someIssue)
51 | "Error"
52 | }
53 | case _ =>
54 | logger.debug("Correlated User Flow - user is not logged in")
55 | "NotLoggedIn"
56 | }
57 | }
58 |
59 | def monthsAgo(months: Int): Date = {
60 | val currentDate = new Date()
61 | val calendar = Calendar.getInstance
62 | calendar.setTime(currentDate)
63 | calendar.add(Calendar.MONTH, -months)
64 | val pastDate: Date = calendar.getTime
65 | pastDate
66 | }
67 |
68 | def getLocalDate(date: Date): String = {
69 | import java.text.DateFormat
70 | val df = DateFormat.getDateInstance(DateFormat.LONG, currentLocale())
71 | val formattedDate = df.format(date)
72 | formattedDate
73 | }
74 |
75 | def getLocale(): Locale = Locale.getAvailableLocales().toList.filter { l =>
76 | l.toLanguageTag == Props.get("language_tag", "en-GB")
77 | }.headOption.getOrElse(Locale.ENGLISH)
78 |
79 | def currentLocale() : Locale = {
80 | val locale = getLocale()
81 | // Cookie name
82 | val localeCookieName = "SELECTED_LOCALE"
83 | S.findCookie(localeCookieName).flatMap {
84 | cookie => cookie.value.map(computeLocale)
85 | } openOr locale
86 | }
87 | // Properly convert a language tag to a Locale
88 | def computeLocale(tag : String) = tag.split(Array('-', '_')) match {
89 | case Array(lang) => new Locale(lang)
90 | case Array(lang, country) => new Locale(lang, country)
91 | case Array(lang, country, variant) => new Locale(lang, country, variant)
92 | }
93 |
94 | def getTimeAgo(date: Date): String = {
95 | import com.github.marlonlom.utilities.timeago.TimeAgo
96 | import com.github.marlonlom.utilities.timeago.TimeAgoMessages
97 | val messages = new TimeAgoMessages.Builder().withLocale(currentLocale()).build
98 | val timeInMillis = date.getTime()
99 | val text = TimeAgo.using(timeInMillis, messages)
100 | text
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/scala/code/widgets/CustomTableSorter.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * The difference between this and lift's TableSorter.scala is that this one will use a text extractor function that
3 | * will search a td for an element with class "text" and use that to sort on. This allows for markup in a td rather than
4 | * just plain strings. Requires jquery. If no "text" class is found in the table, it will just use the table cell element's innerHTML to
5 | * sort on.
6 | *
7 | */
8 |
9 | /*
10 | * Copyright 2007-2010 WorldWide Conferencing, LLC
11 | *
12 | * Licensed under the Apache License, Version 2.0 (the "License");
13 | * you may not use this file except in compliance with the License.
14 | * You may obtain a copy of the License at
15 | *
16 | * http://www.apache.org/licenses/LICENSE-2.0
17 | *
18 | * Unless required by applicable law or agreed to in writing, software
19 | * distributed under the License is distributed on an "AS IS" BASIS,
20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | * See the License for the specific language governing permissions and
22 | * limitations under the License.
23 | */
24 |
25 | package code.widgets.tableSorter {
26 |
27 | import _root_.net.liftweb.http.ResourceServer
28 | import _root_.scala.xml.NodeSeq
29 | import _root_.net.liftweb.http.{LiftRules}
30 | import _root_.net.liftweb.http.js._
31 | import JsCmds._
32 | import JE._
33 | import jquery.JqJE._
34 |
35 | // types to ease the creation of a JsObj to configure the TableSorter plugin.
36 | trait TableSorterOption[T] {
37 |
38 | def toJsObj: JsObj
39 |
40 | }
41 |
42 | // Because of the whacky js api we have to deal with sorter options
43 | // being either a string specifying the name of the sorter or the boolean value
44 | // false in the case you want to disable the sorting.
45 |
46 | trait SorterOption[T] extends TableSorterOption[T] {
47 | val sorter: T
48 | }
49 |
50 | /**
51 | * This is used to create the special case of SorterOption where sorting is
52 | * disabled
53 | */
54 | case class DisableSorting() extends SorterOption[Boolean]{
55 |
56 | val sorter = false
57 |
58 | def toJsObj = JsObj("sorter" -> sorter)
59 | }
60 |
61 | // Allows the user to type stuff like Sorting.DSC to specify that a column should be sorted
62 | // decening instead of just a plain integer that the js api expects.
63 | object Sorting extends Enumeration {
64 | type Sorting = Value
65 | val ASC, DSC = Value
66 | }
67 |
68 | /**
69 | * Sorter is used to define how the TableSorter should sort the columns. The possible list
70 | * of build in sorters are: shortDate, usLongDate, percent, isoDate, url, ipAddress, currency,
71 | * digit, text, shortDate, time, metadata
72 | *
73 | */
74 | case class Sorter(sorter: String) extends SorterOption[String] {
75 | def toJsObj = JsObj("sorter" -> sorter)
76 | }
77 |
78 | object CustomTableSorter {
79 |
80 | private val emptyJsObj = new JsObj {
81 | def props = Nil
82 | }
83 |
84 |
85 | def apply(selector: String): NodeSeq = renderOnLoad(selector, CustomTableSorter.options())
86 | def apply(selector: String, options: String): NodeSeq = renderOnLoad(selector, options)
87 |
88 | /**
89 | * Initializes the widget. You have to call this in boot for the widget to work.
90 | */
91 | def init() {
92 | ResourceServer.allow({
93 | case "tablesorter" :: tail => true
94 | })
95 | }
96 |
97 | import Sorting._
98 |
99 | /**
100 | * Use this method to create a type-safe configuration JsObj for the
101 | * TableSorter. See http://tablesorter.com/docs/ for more information
102 | * about the possible configurations of the jQuery plugin.
103 | *
104 | * Example usage: TableSorter.options(
105 | * headers = (0, DisableSorting()) :: (3,Sorter("currency")) :: Nil,
106 | * sortList = (3,Sorting.DSC) :: Nil)
107 | *
108 | * @param headers A list of tuples of int * sorter options. the int is the column number.
109 | * The column number is indexed starting from 0
110 | * @param sortList A list of tuples of int * Sorting. the int is the column number.
111 | * Use DisableSorting to disable sorting
112 | * @return A JsObj with valid properties to configure the jQuery TableSorter plugin
113 | */
114 | def options(headers: List[(Int, TableSorterOption[_])],
115 | widgets: List[String],
116 | sortList: List[(Int, Sorting)]): String = {
117 |
118 | val jsHeaders = headers
119 | .map{ case (index, header) => JsObj(index.toString -> header.toJsObj)}
120 | .foldLeft[JsObj](emptyJsObj)(_ +* _)
121 | val jSSortList = JsArray( sortList.map{ case (index, sorting) => JsArray(index,sorting.id)}:_*)
122 | val widgetsArr = JsArray ( widgets.map( Str(_) ):_* )
123 | "{" + "sortList:" + jSSortList.toJsCmd + ",headers:" + jsHeaders.toJsCmd + ",widgets:" + widgetsArr.toJsCmd + ",textExtraction:" +
124 | """function(node){var sortOn = $(node).find(".text").first().html(); return sortOn? sortOn : node.innerHTML}""" + "}"
125 | }
126 |
127 | //Convenience versions of the options method that provide default values for all the arguments.
128 | def options(): String = options(Nil, List("zebra"), List((0, ASC)))
129 |
130 | def options(headers: List[(Int, TableSorterOption[_])],
131 | sortList: List[(Int, Sorting)]): String = options(headers, List("zebra"), sortList)
132 |
133 | /**
134 | * Transforms a regular table into a tablesorter when page is loaded
135 | *
136 | * @param selector A CSS selector for the table you want to transform
137 | * @param options A JsObject configuring the tablesorter. You can use the connivance method
138 | * TableSorter.options to create the JsObj in a type-safe manner. Check out
139 | http://tablesorter.com/docs/ for more info about the possible configurations.
140 | */
141 | def renderOnLoad(selector: String, options: String): NodeSeq = {
142 | val onLoad = """jQuery(document).ready(function(){
143 | jQuery('"""+selector+"""').tablesorter("""+options+""")
144 | });"""
145 |
146 |
147 |
148 | {Script(JsRaw(onLoad))}
149 |
150 | }
151 |
152 | /**
153 | * Transforms a regular table into a tablesorter
154 | */
155 | def jsRender(selector: String, options: String): JsExp =
156 | JqId(selector) ~> new JsRaw("tablesorter("+options+");") with JsMember
157 |
158 | def jsRender(selector: String): JsExp = jsRender(selector,CustomTableSorter.options())
159 |
160 | }
161 |
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/webapp/404.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
404 - Resource Not Found
34 |
35 |
36 | Sorry, we couldn't find the page you were looking for.
37 | Maybe this is because you don't have the right permissions.
38 |
39 |
40 | You might like to
login .
41 |
42 |
43 |
44 | Please double check the URL or
logout and login as a different user.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | LiftFilter
10 | Lift Filter
11 | The Filter that intercepts lift calls
12 | net.liftweb.http.LiftFilter
13 |
14 |
15 |
16 |
17 | LiftFilter
18 | /*
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/webapp/about.html:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
About
25 |
26 |
27 |
28 | Open Bank Project is © 2011-2021 TESOBE GmbH and distributed under the AGPL and commercial licenses.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
Debug Info
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/create-bank-account.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Create an account
40 |
41 |
42 |
43 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/account-overview-dashboard.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
45 | Balance:
46 |
47 |
48 | 100 EUR
49 |
50 |
51 |
56 |
57 |
58 | Profile completeness
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Credit score
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/create-expenditure.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
Add expenditure
46 |
ACCOUNT
47 |
48 |
49 |
50 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/create-income.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
Add income
46 |
ACCOUNT
47 |
48 |
49 |
50 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/management.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |
38 |
39 |
40 |
41 | Counterparty
42 | Public Alias
43 | Private Alias
44 | Website
45 | Corporates URL
46 | More info
47 | Image URL
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/permissions.html:
--------------------------------------------------------------------------------
1 |
31 |
77 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/permissions/create.html:
--------------------------------------------------------------------------------
1 |
31 |
76 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/settings.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
Update account label
42 |
ACCOUNT
43 |
44 |
45 |
46 |
64 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/star.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | ACCOUNT
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 01.01.1970
65 |
66 |
67 | BALANCE
68 | € 0
69 |
70 |
71 |
72 |
73 |
74 |
75 | Account holder
76 | Transaction Description (reference)
77 | Transaction Narrative (owner comment)
78 |
79 |
80 |
81 |
82 |
83 | 0
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/transactions/star/star.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
36 |
37 |
38 |
124 |
--------------------------------------------------------------------------------
/src/main/webapp/banks/star/accounts/star/views/list.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
39 |
40 |
119 |
--------------------------------------------------------------------------------
/src/main/webapp/correlated-user.html:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
Correlated user
24 |
25 |
26 | Open Bank Project
27 | © 2011-2021 TESOBE GmbH and distributed under the AGPL and commercial licenses.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/main/webapp/dd.html:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | YOUR ACCOUNTS DD
37 |
38 | Particip
39 | Placeholder
40 | Placeholder
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Centre for Transparency & Civic Engagement (PARTICIP) AND Fairnopoly
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/main/webapp/debug-info.html:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
Debug Info
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/webapp/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/favicon.ico
--------------------------------------------------------------------------------
/src/main/webapp/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/src/main/webapp/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/src/main/webapp/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/main/webapp/help.html:
--------------------------------------------------------------------------------
1 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
My Accounts
41 |
42 |
43 |
44 |
45 |
46 | Account Name
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Public Accounts
68 |
69 |
70 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/main/webapp/list-accounts.html:
--------------------------------------------------------------------------------
1 |
31 |
55 |
--------------------------------------------------------------------------------
/src/main/webapp/media/css/highlight.js.min.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#f0f0f0;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rule .hljs-value,.hljs-preprocessor,.hljs-pragma,.hljs-name,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute,.tp .hljs-variable{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-doctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.hljs-type,.hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status,.tp .hljs-data,.tp .hljs-io{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis,.tp .hljs-units{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:0.5}
--------------------------------------------------------------------------------
/src/main/webapp/media/css/jquery.dropdown.min.css:
--------------------------------------------------------------------------------
1 | .jq-dropdown{position:absolute;z-index:1039;display:none}.jq-dropdown .jq-dropdown-menu,.jq-dropdown .jq-dropdown-panel{min-width:160px;max-width:360px;list-style:none;background:#fff;border:solid 1px #ddd;border-radius:4px;box-shadow:0 5px 10px rgba(0,0,0,0.2);overflow:visible;padding:4px 0;margin:0}.jq-dropdown .jq-dropdown-panel{padding:10px}.jq-dropdown.jq-dropdown-tip{margin-top:8px}.jq-dropdown.jq-dropdown-tip:before{position:absolute;top:-6px;left:9px;content:"";border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ddd;display:inline-block}.jq-dropdown.jq-dropdown-tip:after{position:absolute;top:-5px;left:10px;content:"";border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;display:inline-block}.jq-dropdown.jq-dropdown-tip.jq-dropdown-anchor-right:before{left:auto;right:9px}.jq-dropdown.jq-dropdown-tip.jq-dropdown-anchor-right:after{left:auto;right:10px}.jq-dropdown.jq-dropdown-scroll .jq-dropdown-menu,.jq-dropdown.jq-dropdown-scroll .jq-dropdown-panel{max-height:180px;overflow:auto}.jq-dropdown .jq-dropdown-menu li{list-style:none;padding:0 0;margin:0;line-height:18px}.jq-dropdown .jq-dropdown-menu li>a,.jq-dropdown .jq-dropdown-menu label{display:block;color:inherit;text-decoration:none;line-height:18px;padding:3px 15px;margin:0;white-space:nowrap}.jq-dropdown .jq-dropdown-menu li>a:hover,.jq-dropdown .jq-dropdown-menu label:hover{background-color:#f2f2f2;color:inherit;cursor:pointer}.jq-dropdown .jq-dropdown-menu .jq-dropdown-divider{font-size:1px;border-top:solid 1px #e5e5e5;padding:0;margin:5px 0}
2 |
--------------------------------------------------------------------------------
/src/main/webapp/media/css/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
--------------------------------------------------------------------------------
/src/main/webapp/media/css/screen.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/css/screen.css
--------------------------------------------------------------------------------
/src/main/webapp/media/css/toastr.min.css:
--------------------------------------------------------------------------------
1 | .toast-title{font-weight:bold}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:bold;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")!important}#toast-container>.toast-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}#toast-container>.toast-success{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")!important}#toast-container>.toast-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")!important}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:241px)and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container .toast-close-button{right:-.2em;top:-.2em}}@media all and (min-width:481px)and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}}
--------------------------------------------------------------------------------
/src/main/webapp/media/images/OBP_full_web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OBP_full_web.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/OBP_logo_simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OBP_logo_simple.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/OpenBankProject_logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/OpenBankProject_logo.jpg
--------------------------------------------------------------------------------
/src/main/webapp/media/images/add-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/add-off.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/add-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/add-on.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/ajax-loader.gif
--------------------------------------------------------------------------------
/src/main/webapp/media/images/arrow-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/arrow-right.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/blank.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/blank.gif
--------------------------------------------------------------------------------
/src/main/webapp/media/images/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/cancel.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/close-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/close-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/comment.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/edit-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/edit-off.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/edit-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/edit-on.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/favicon.ico
--------------------------------------------------------------------------------
/src/main/webapp/media/images/feedback-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/feedback-button.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/header.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/home-table-enter-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-enter-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/home-table-exit-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-exit-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/home-table-paper-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-paper-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/home-table-quote-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/home-table-quote-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/login-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/login-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/logo-footer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo-footer.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/logo-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo-header.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logo.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/logout-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/logout-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/money-in-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/money-in-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/money-out-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/money-out-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/moreInfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/moreInfo.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/nav-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav-item.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/nav-selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav-selected.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/nav.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/nav.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/polarize-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/polarize-logo.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/remove.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/select-arrow-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/select-arrow-home.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/select-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/select-arrow.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/settings-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/settings-icon.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/sort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/sort.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/submit.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/transaction-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/transaction-in.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/transaction-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/transaction-out.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/upload-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/upload-off.png
--------------------------------------------------------------------------------
/src/main/webapp/media/images/upload-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenBankProject/Sofit/1bf7f193e94a804da8e3dbe83bcf8af6fe692b21/src/main/webapp/media/images/upload-on.png
--------------------------------------------------------------------------------
/src/main/webapp/media/js/notifications.js:
--------------------------------------------------------------------------------
1 | var socialFinanceNotifications = {
2 | notify : function(msg) {
3 | toastr.info(msg);
4 | },
5 |
6 | notifyError : function(msg) {
7 | toastr.error(msg);
8 | },
9 |
10 | notifyReload : function(msg, delay) {
11 | toastr.info(msg);
12 | if (!delay) var delay = 1500;
13 | setTimeout(function() {
14 | window.location.reload();
15 | }, delay);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/webapp/media/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | 'use strict';
3 |
4 | var project = {};
5 |
6 | project.init = function() {
7 | project.counterpartiesFilter();
8 | project.removeUserFromView();
9 | project.managementTableSorter();
10 | // project.transloadit();
11 | project.fileUpload();
12 | };
13 |
14 | project.counterpartiesFilter = function() {
15 | $('.counterparties-table-head__cell').on('click', function() {
16 | $('.counterparties-table-head__cell').removeClass('counterparties-table-head__cell--active');
17 | $(this).addClass('counterparties-table-head__cell--active');
18 | });
19 | };
20 |
21 | project.removeUserFromView = function() {
22 | $('.users-table-body__cell .remove').click(function () {
23 | $(this).closest('.row').css('background-color', '#CC0000').fadeOut(1000, function() {$(this).remove();});
24 | });
25 | };
26 |
27 | project.managementTableSorter = function() {
28 | $("#management .tablesorter").tablesorter();
29 | }
30 |
31 | project.transloadit = function() {
32 | $('#imageUploader').transloadit({
33 | wait: true
34 | });
35 | }
36 |
37 | project.fileUpload = function() {
38 | $(".file-upload").change(function() {
39 | $(this).siblings('.file-upload-filename').html(this.value);
40 | });
41 | }
42 |
43 | $(document).ready(project.init);
44 |
45 | })(jQuery);
46 |
47 |
--------------------------------------------------------------------------------
/src/main/webapp/media/js/toastr.min.js:
--------------------------------------------------------------------------------
1 | (function(n){n(["jquery"],function(n){return function(){function l(n,t,f){return u({type:r.error,iconClass:i().iconClasses.error,message:n,optionsOverride:f,title:t})}function a(n,t,f){return u({type:r.info,iconClass:i().iconClasses.info,message:n,optionsOverride:f,title:t})}function v(n){e=n}function y(n,t,f){return u({type:r.success,iconClass:i().iconClasses.success,message:n,optionsOverride:f,title:t})}function p(n,t,f){return u({type:r.warning,iconClass:i().iconClasses.warning,message:n,optionsOverride:f,title:t})}function w(r){var u=i();if(t||f(u),r&&n(":focus",r).length===0){r[u.hideMethod]({duration:u.hideDuration,easing:u.hideEasing,complete:function(){c(r)}});return}t.children().length&&t[u.hideMethod]({duration:u.hideDuration,easing:u.hideEasing,complete:function(){t.remove()}})}function b(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:undefined,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:undefined,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:"
×<\/button>",newestOnTop:!0}}function h(n){e&&e(n)}function u(r){function l(t){if(!n(":focus",e).length||t)return e[u.hideMethod]({duration:u.hideDuration,easing:u.hideEasing,complete:function(){c(e),u.onHidden&&u.onHidden(),s.state="hidden",s.endTime=new Date,h(s)}})}function b(){(u.timeOut>0||u.extendedTimeOut>0)&&(y=setTimeout(l,u.extendedTimeOut))}function k(){clearTimeout(y),e.stop(!0,!0)[u.showMethod]({duration:u.showDuration,easing:u.showEasing})}var u=i(),v=r.iconClass||u.iconClass;typeof r.optionsOverride!="undefined"&&(u=n.extend(u,r.optionsOverride),v=r.optionsOverride.iconClass||v),o++,t=f(u);var y=null,e=n("
"),p=n("
"),w=n("
"),a=n(u.closeHtml),s={toastId:o,state:"visible",startTime:new Date,options:u,map:r};return r.iconClass&&e.addClass(u.toastClass).addClass(v),r.title&&(p.append(r.title).addClass(u.titleClass),e.append(p)),r.message&&(w.append(r.message).addClass(u.messageClass),e.append(w)),u.closeButton&&(a.addClass("toast-close-button"),e.prepend(a)),e.hide(),u.newestOnTop?t.prepend(e):t.append(e),e[u.showMethod]({duration:u.showDuration,easing:u.showEasing,complete:u.onShown}),u.timeOut>0&&(y=setTimeout(l,u.timeOut)),e.hover(k,b),!u.onclick&&u.tapToDismiss&&e.click(l),u.closeButton&&a&&a.click(function(n){n.stopPropagation(),l(!0)}),u.onclick&&e.click(function(){u.onclick(),l()}),h(s),u.debug&&console&&console.log(s),e}function f(r){return(r||(r=i()),t=n("#"+r.containerId),t.length)?t:(t=n("
").attr("id",r.containerId).addClass(r.positionClass),t.appendTo(n(r.target)),t)}function i(){return n.extend({},b(),s.options)}function c(n){(t||(t=f()),n.is(":visible"))||(n.remove(),n=null,t.children().length===0&&t.remove())}var t,e,o=0,r={error:"error",info:"info",success:"success",warning:"warning"},s={clear:w,error:l,getContainer:f,info:a,options:{},subscribe:v,success:y,version:"2.0.1",warning:p};return s}()})})(typeof define=="function"&&define.amd?define:function(n,t){typeof module!="undefined"&&module.exports?module.exports=t(require(n[0])):window.toastr=t(window.jQuery)});
2 | //@ sourceMappingURL=toastr.min.js.map
--------------------------------------------------------------------------------
/src/main/webapp/media/js/vendor/jquery.dropdown.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Dropdown: A simple dropdown plugin
3 | *
4 | * Contribute: https://github.com/claviska/jquery-dropdown
5 | *
6 | * @license: MIT license: http://opensource.org/licenses/MIT
7 | *
8 | */
9 | jQuery&&function($){function t(t,e){var n=t?$(this):e,d=$(n.attr("data-jq-dropdown")),a=n.hasClass("jq-dropdown-open");if(t){if($(t.target).hasClass("jq-dropdown-ignore"))return;t.preventDefault(),t.stopPropagation()}else if(n!==e.target&&$(e.target).hasClass("jq-dropdown-ignore"))return;o(),a||n.hasClass("jq-dropdown-disabled")||(n.addClass("jq-dropdown-open"),d.data("jq-dropdown-trigger",n).show(),r(),d.trigger("show",{jqDropdown:d,trigger:n}))}function o(t){var o=t?$(t.target).parents().addBack():null;if(o&&o.is(".jq-dropdown")){if(!o.is(".jq-dropdown-menu"))return;if(!o.is("A"))return}$(document).find(".jq-dropdown:visible").each(function(){var t=$(this);t.hide().removeData("jq-dropdown-trigger").trigger("hide",{jqDropdown:t})}),$(document).find(".jq-dropdown-open").removeClass("jq-dropdown-open")}function r(){var t=$(".jq-dropdown:visible").eq(0),o=t.data("jq-dropdown-trigger"),r=o?parseInt(o.attr("data-horizontal-offset")||0,10):null,e=o?parseInt(o.attr("data-vertical-offset")||0,10):null;0!==t.length&&o&&t.css(t.hasClass("jq-dropdown-relative")?{left:t.hasClass("jq-dropdown-anchor-right")?o.position().left-(t.outerWidth(!0)-o.outerWidth(!0))-parseInt(o.css("margin-right"),10)+r:o.position().left+parseInt(o.css("margin-left"),10)+r,top:o.position().top+o.outerHeight(!0)-parseInt(o.css("margin-top"),10)+e}:{left:t.hasClass("jq-dropdown-anchor-right")?o.offset().left-(t.outerWidth()-o.outerWidth())+r:o.offset().left+r,top:o.offset().top+o.outerHeight()+e})}$.extend($.fn,{jqDropdown:function(r,e){switch(r){case"show":return t(null,$(this)),$(this);case"hide":return o(),$(this);case"attach":return $(this).attr("data-jq-dropdown",e);case"detach":return o(),$(this).removeAttr("data-jq-dropdown");case"disable":return $(this).addClass("jq-dropdown-disabled");case"enable":return o(),$(this).removeClass("jq-dropdown-disabled")}}}),$(document).on("click.jq-dropdown","[data-jq-dropdown]",t),$(document).on("click.jq-dropdown",o),$(window).on("resize",r)}(jQuery);
--------------------------------------------------------------------------------
/src/main/webapp/media/js/views.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 |
3 | $('#view-edit-advanced-options').click(function(){
4 | var thisButton = $(this)
5 | var showButtonText = "Show advanced options"
6 |
7 | $('.advanced-option').toggle();
8 |
9 | if(thisButton.text() === showButtonText) {
10 | thisButton.text("Hide advanced options");
11 | } else {
12 | thisButton.text(showButtonText);
13 | }
14 | })
15 |
16 | $('#add-view').click(function(){
17 | $(this).hide();
18 | var form = $('#add-view-form');
19 | form.css('display', 'inline-block');
20 | form.find('input:first-of-type').focus();
21 | })
22 | $('#add-view-cancel').click(function(){
23 | $('#add-view-form').hide();
24 | $('#add-view').show();
25 | })
26 |
27 | /* clicking on edit: change view to edit mode for selected view */
28 | $(".edit-button").on("click", function(){
29 | var viewId = $(this).attr("data-id")
30 |
31 | $(this).addClass("active");
32 |
33 | /* permissions and is public checkboxes get activated */
34 | $(".permission_value_cb").each(function(i){
35 | if($(this).attr("data-viewid") === viewId){
36 | $(this).removeAttr("disabled")
37 | }
38 | })
39 |
40 | $(".is_public_cb").each(function(i){
41 | if($(this).attr("data-viewid") === viewId){
42 | $(this).removeAttr("disabled")
43 | }
44 | })
45 |
46 | /* make Save / Cancel / Delete unclickable for not selected columns */
47 | var $actionButtons = $(".action");
48 | for(i=0; i<$actionButtons.length; i++){
49 | var $action = $($actionButtons[i]);
50 | if($action.attr("data-id") !== viewId)
51 | $action.attr("disabled", "disabled");
52 | }
53 |
54 | /* save and cancel button appear, edit button disappears */
55 | var parent = $(this).parent();
56 | parent.find('.action').show();
57 | parent.find('.edit-button.action').hide();
58 |
59 | var descriptionText = "";
60 | /* viewId become editable */
61 | $(".desc").each(function(i){
62 | var $desc = $(this);
63 | if($desc.attr("data-viewid") === viewId){
64 | descriptionText = $desc.text();
65 | $desc.hide();
66 | }
67 | })
68 |
69 | $(".desc_input").each(function(i){
70 | var $descInput = $(this);
71 | if($descInput.attr("data-viewid") === viewId){
72 | $descInput.val(descriptionText)
73 | $descInput.show();
74 | }
75 | })
76 |
77 | var metadataViewText = "";
78 | /* metadataVie become editable */
79 | $(".metadataView").each(function(i){
80 | var $metadataView = $(this);
81 | if($metadataView.attr("data-viewid") === viewId){
82 | metadataViewText = $metadataView.text();
83 | $metadataView.hide();
84 | }
85 | })
86 |
87 | $(".metadata_view_input").each(function(i){
88 | var $metadataViewInput = $(this);
89 | if($metadataViewInput.attr("data-viewid") === viewId){
90 | $metadataViewInput.val(metadataViewText)
91 | $metadataViewInput.show();
92 | }
93 | })
94 |
95 | /* alias field will become a select box */
96 | $(".alias").each(function(i){
97 | var $alias = $(this)
98 | if($alias.attr("data-viewid") === viewId){
99 | var content = $alias.html()
100 | $alias.html(
101 | ""+getOptions()+" "
102 | )
103 | }
104 | $("#selectAlias option[value='"+content+"']").attr('selected',true)
105 | })
106 |
107 | function getOptions () {
108 | var aliasOptions = new Array("public", "private", "none (display real names only)")
109 | var option = ""
110 | for(a in aliasOptions){
111 | var alias = aliasOptions[a]
112 | option += ""+alias+" "
113 | }
114 | return option
115 | };
116 |
117 | });
118 | /* clicking on cancel: reload the page */
119 | $(".cancel-button").on("click", function(){
120 | window.location.reload();
121 | });
122 |
123 | $(".save-button").on("click", function(){
124 | var $this = $(this);
125 | var viewId = $this.attr("data-id")
126 |
127 | //removes the alias dropdown and replaces it with the text of the previously selected option
128 | $(".alias").each(function(i){
129 | var $alias = $(this);
130 | if($alias.attr("data-viewid") === viewId){
131 | var aliasName = $alias.find(":selected").val();
132 | $alias.html(aliasName)
133 | }
134 | });
135 |
136 | //show the description again and hide the description input
137 | $(".description").each(function(i){
138 | var $description = $(this);
139 | if($description.attr("data-viewid") === viewId){
140 | var $descInput = $description.find(".desc_input")
141 | var newDescription = $descInput.val();
142 | $descInput.hide();
143 |
144 | var $desc = $description.find(".desc");
145 | $desc.text(newDescription);
146 | $desc.show();
147 | }
148 | });
149 |
150 | //show the metadataView again and hide the metadataView input
151 | $(".metadata_view").each(function(i){
152 | var $metadataView = $(this);
153 | if($metadataView.attr("data-viewid") === viewId){
154 | var $metadataViewInput = $metadataView.find(".metadata_view_input")
155 | var newMetadaView = $metadataViewInput.val();
156 | $metadataViewInput.hide();
157 |
158 | var $metadataView = $metadataView.find(".metadataView");
159 | $metadataView.text(newMetadaView);
160 | $metadataView.show();
161 | }
162 | });
163 |
164 | /* permissions and is public checkboxes get deactivated */
165 | $(".permission_value_cb").each(function(i){
166 | if($(this).attr("data-viewid") === viewId){
167 | $(this).attr("disabled", "disabled")
168 | }
169 | })
170 |
171 | $(".is_public_cb").each(function(i){
172 | if($(this).attr("data-viewid") === viewId){
173 | $(this).attr("disabled", "disabled")
174 | }
175 | })
176 |
177 | /* make Save / Cancel / Delete clickable for not selected columns */
178 | var $actionButtons = $(".action");
179 | for(i=0; i<$actionButtons.length; i++){
180 | var $action = $($actionButtons[i]);
181 | $action.removeAttr("disabled");
182 | }
183 |
184 | /* save and cancel button disappear, edit button appears */
185 | var parent = $(this).parent();
186 | parent.find('.action').hide();
187 | parent.find('.edit-button.action').show();
188 | });
189 | })
190 |
191 | var collectData = function(viewId){
192 | var saveJson = new Object();
193 | saveJson.viewId = viewId;
194 | var viewData = new Object();
195 | viewData.description = $(".desc_input[data-viewId=" + viewId + "]").val();
196 | viewData.metadata_view = $(".metadata_view_input[data-viewId=" + viewId + "]").val();
197 | viewData.which_alias_to_use = $(".alias[data-viewId=" + viewId + "] select").val();
198 | viewData.is_public = $(".is_public_cb[data-viewId=" + viewId + "]").is(':checked');
199 | viewData.hide_metadata_if_alias_used = $(".hide_metadata_if_alias_used[data-viewId=" + viewId + "]").is(':checked');
200 |
201 | var $permissions = $("input.permission_value_cb");
202 | var allowedActions = new Array();
203 | for(i=0; i < $permissions.length; i++){
204 | var $permission = $($permissions[i])
205 | if($permission.attr("data-viewid") == viewId && $permission.is(':checked')){
206 | allowedActions.push($permission.attr("name"));
207 | }
208 | }
209 | viewData.allowed_actions = allowedActions;
210 | saveJson.updateJson = viewData;
211 | return JSON.stringify(saveJson);
212 | };
213 |
214 |
--------------------------------------------------------------------------------
/src/main/webapp/media/js/website.js:
--------------------------------------------------------------------------------
1 | // $(function() {
2 | // $("body").polarize({
3 | // "position": "right",
4 | // "images_dir": "/media/images/",
5 | // "default_topic_url": "https://polarize.it/polarize/socialfinanceapp_55868373368"
6 | // });
7 | // });
--------------------------------------------------------------------------------
/src/main/webapp/my-accounts.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
My Accounts
41 |
42 |
43 |
44 |
45 |
46 | Account Name
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Public Accounts
68 |
69 |
70 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/main/webapp/oauthcallback.html:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 | Sorry! There was an error logging in.
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/_comment.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/_dashboard_account.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Account Title Left Side
6 |
7 |
Public account
8 |
9 |
10 |
11 |
12 | 27.10.2015
13 |
14 |
15 | BALANCE
16 | €53292
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Account holder
26 |
27 |
28 |
29 |
30 |
31 |
32 | €400
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/_nav_account_settings.html:
--------------------------------------------------------------------------------
1 |
40 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/_tag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The tag goes here
4 | x
5 |
6 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/_transactionImage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/src/main/webapp/templates-hidden/default.html:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |
35 |
36 | Open Bank Project: %*%
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
92 |
93 |
94 |
98 |
99 |
100 |
101 |
106 |
107 |
108 |
109 |
110 |
111 | The main content gets bound here
112 |
113 |
114 |
115 |
116 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
ACCOUNT
44 | 45 |Tags
71 |Images
83 |Comments
105 |106 | 107 | 108 |
109 | 110 |